1,同步代码块
-格式:
synchronized(对象){
需要同步的代码;
}
-注意:这个对象,同步代码块可以解决线程安全问题的根本就在于这个对象。
这个对象就好比是锁的功能。
-这个对象可以是任意对象,但是多个线程必须是同一个对象。
2,同步的好处:
-解决了多线程中的线程安全问题
3,同步的弊端
-当线程很多的时候,因为每个线程来了之后都要判断同步上的锁,这个很
耗费资源和时间,降低了程序的运行效率。
package com.momo.thread;
import com.momo.domain.Stu;
public class MyRunnable implements Runnable{
private int tickets = 100;
// Object o = new Object();
Stu s = new Stu();
@Override
public void run() {
while (true){
// synchronized (new Object()) {//注意不能这样
synchronized (s) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了第:" + (tickets--) + "张票");
}
}
}
}
}
package com.momo.demo;
import com.momo.thread.MyRunnable;
public class Demo1 {
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr,"都美竹");
Thread t2 = new Thread(mr,"小龙女");
Thread t3 = new Thread(mr,"都美艳");
t1.start(); t2.start(); t3.start(); }
}
package com.momo.thread;
import com.momo.domain.Stu;
public class MyRunnable implements Runnable{
// private int tickets = 100;
private static int tickets = 100;
// Object o = new Object();
private Stu s = new Stu();
private int i = 0;
@Override public void run() { while (true){ if(i%2==0){ // synchronized (s) { // synchronized (this) { synchronized (MyRunnable.class) { if (tickets > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "卖出了第:" + (tickets--) + "张票"); } } }else{ /* synchronized (s) { if (tickets > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "卖出了第:" + (tickets--) + "张票"); } }*/ // maiPiao(); // maiPiao2(); maiPiao3(); } i++; } } /* * 静态同步方法的锁对象是当前类的字节码文件对象 MyRunnable.class * */ private static synchronized void maiPiao3(){ if (tickets > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "卖出了第:" + (tickets--) + "张票"); } } /* * 同步方法:就是把同步关键字加到方法上 * 锁对象是this * */
/* private synchronized void maiPiao2(){
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖出了第:" + (tickets--) + "张票");
}
}//
* 如果一个方法从刚开始就发现它里面的代码被同步了,那么
* 就可以把这个同步加到方法上, 就变成了同步方法。
* //
synchronized (s) {
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖出了第:" + (tickets--) + "张票");
}
}
}//
public void run() {
while (true){
// synchronized (new Object()) {//注意不能这样
synchronized (s) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了第:" + (tickets--) + "张票");
}
}
}
}*/
}
package com.momo.demo;
import com.momo.thread.MyRunnable;
public class Demo1 {
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr,"都美竹");
Thread t2 = new Thread(mr,"小龙女");
Thread t3 = new Thread(mr,"都美艳");
t1.start(); t2.start(); t3.start(); }
}
一,解决线程安全问题方式二 1,同步方法 -把同步关键字加到方法上 2,同步方法的锁对象是 this 3,静态同步方法的锁对象是 当前类字节码文件对象 4,到底用哪个? -如果锁对象是this,就可以考虑用同步方法。 一般来说能用同步代码块就用同步代码块
二,后来jdk提供的新锁 Lock 锁
1,虽然我们可以使用同步机制解决线程安全问题,
但是我们看不到它到底是在哪里上锁的,又是在哪里开锁的。
为了能够更加清楚的表示上锁和解锁,后来就提供了lock锁。
2,Lock 接口
void lock()
获得锁。
void unlock()
释放锁。
3,实现类
ReentrantLock
package com.momo.thread;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyRunnable2 implements Runnable{
private int tickets = 100;
private Lock l = new ReentrantLock();
@Override
public void run() {
while (true){
try {
l.lock();
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖出了第:" + (tickets--) + "张票");
}
}finally {
l.unlock();
}
}
}
}
package com.momo.demo;
import com.momo.thread.MyRunnable;
import com.momo.thread.MyRunnable2;
public class Demo2 {
public static void main(String[] args) {
MyRunnable2 mr = new MyRunnable2();
Thread t1 = new Thread(mr,"都美竹");
Thread t2 = new Thread(mr,"小龙女");
Thread t3 = new Thread(mr,"都美艳");
t1.start(); t2.start(); t3.start(); }
}
三,同步注意事项:
1,注意死锁
-如果出现了同步嵌套,就很容易产生死锁
-指的是2个或2个以上的线程在执行过程中,以为争夺资源产生的一种相互等待的现象
package com.momo.thread;
import com.momo.domain.MyLock;
public class DieLock extends Thread{
private boolean boo;
public DieLock(boolean boo){
this.boo = boo;
}
@Override public void run() { if(boo){ synchronized (MyLock.O1){ System.out.println("if 的 o1"); synchronized (MyLock.O2){ System.out.println("if 的 o2"); } } }else{ synchronized (MyLock.O2){ System.out.println("else 的 o2"); synchronized (MyLock.O1){ System.out.println("else 的 o1"); } } } }
}
package com.momo.demo;
import com.momo.thread.DieLock;
public class Demo3 {
public static void main(String[] args) {
DieLock d1 = new DieLock(true);
DieLock d2 = new DieLock(false);
d1.start(); d2.start(); }
}
package com.momo.domain;
public class MyLock { public static final Object O1 = new Object(); public static final Object O2 = new Object(); }
四,线程之间的通信
1,有买票的,也会有退票的。
package com.momo.demo;
import com.momo.domain.BaoZi;
import com.momo.thread.GetBaoZi;
import com.momo.thread.SetBaoZi;
//测试类
/*
- 问题一:为了让效果更加明显,加入了循环和判断,
- 同一个数据出现多次
- 名字和价格不匹配
- 如果是消费者线程先抢到cpu的执行权,输出是null和0
- 原因:
- 同一个数据出现多次
- cpu的一点点时间足够我们的代码执行很多次
- 名字和价格不匹配
- 线程的执行是随机的
- 也就是说我们的代码存在线程安全问题:
- 是否是多线程 是
- 是否有共享数据 有
- 是否有多条语句操作共享数据 有
- 解决方式:加锁
- 注意:不同种类的线程都要加锁,还必须是同一个锁
- */
public class Demo4 {
public static void main(String[] args) {
//创建子类源
BaoZi b = new BaoZi();
//创建生产者和消费者线程
SetBaoZi sb = new SetBaoZi(b);
GetBaoZi gb = new GetBaoZi(b);
gb.start(); sb.start();
}
}
package com.momo.domain;
//资源类
public class BaoZi {
private String name;
private int price;
public String getName() {
return name;
}
public void setName(String name) {
= name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
package com.momo.thread;
import com.momo.domain.BaoZi;
//消费者线程
public class GetBaoZi extends Thread{
private BaoZi bz;//BaoZi bz = new Baozi();
public GetBaoZi(BaoZi bz){
this.bz = bz;
}
@Override
public void run() {
while (true) {
synchronized (bz) {
System.out.println("我买到了一个:" + bz.getName() + ",价格是:" + bz.getPrice());
}
}
}
}
package com.momo.thread;
import com.momo.domain.BaoZi;
//生产者线程
public class SetBaoZi extends Thread{
private BaoZi bz;//BaoZi bz = new Baozi();
private int i = 0;
public SetBaoZi(BaoZi bz){
this.bz = bz;
}
@Override public void run() { while (true) { synchronized (bz) { if (i % 2 == 0) { bz.setName("菜包子"); bz.setPrice(1); //BaoZi bz = new Baozi("菜包子",1); } else { bz.setName("肉包子"); // BaoZi bz = new Baozi("肉包子",1); bz.setPrice(2); } System.out.println("我生产了一个:" + bz.getName() + ",价格是:" + bz.getPrice()); i++; } } }
}
package com.momo.demo;
import com.momo.domain.Dumplings;
import com.momo.thread.GetDumplings;
import com.momo.thread.SetDumplings;
public class Demo5 {
public static void main(String[] args) {
Dumplings d = new Dumplings();
SetDumplings sd = new SetDumplings(d); GetDumplings gd = new GetDumplings(d); Thread t1 = new Thread(sd); Thread t2 = new Thread(gd); t2.start(); t1.start(); }
}
package com.momo.domain;
//资源
public class Dumplings {
private String name;
private int price;
public String getName() { return name; } public void setName(String name) { = name; } public int getPrice() { return price; } public void setPrice(int price) { this.price = price; }
}
package com.momo.thread;
import com.momo.domain.Dumplings;
//生产者线程
public class SetDumplings implements Runnable{
private Dumplings d;
private int i = 0;
public SetDumplings(Dumplings d){
this.d = d;
}
@Override
public void run() {
while (true) {
synchronized (d) {
if (i % 2 == 0) {
d.setName("素饺子");
d.setPrice(1);
} else {
d.setName("肉饺子");
d.setPrice(2);
}
System.out.println("生产者:" + d.getName() + "---" + d.getPrice());
i++;
}
}
}
}
package com.momo.thread;
import com.momo.domain.Dumplings;
//消费者线程 public class GetDumplings implements Runnable{ private Dumplings d; public GetDumplings(Dumplings d){ this.d = d; } @Override public void run() { while (true) { synchronized (d) { System.out.println("消费者:" + d.getName() + "---" + d.getPrice()); } } } }
-看图:等待唤醒机制图
2,上面代码加入同步之后,虽然把线程安全问题解决了。
但是依然存在一些问题。
-如果一开始就是消费者抢到cpu的执行权,就会消费,出现null和0
-如果一开始就是生产者获取执行权,一直生产也不合适。
-如果生产者生产了一个,结果消费者消费了多次也不合适。
-我们想实现的是生产一个消费一个,。。。。
-使用java提供的等待唤醒机制,用到的就是Object类中的方法:
void notify()
唤醒正在等待对象监视器的单个线程。
void notifyAll()
唤醒正在等待对象监视器的所有线程。
void wait()
导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法。
void wait(long timeout)
导致当前线程等待,直到另一个线程调用 notify()方法或该对象的 notifyAll()方法,或者指定的时间已过。
-问:为什么这几个方法定义在Object类中? 不在Thread类中?
这些方法必须使用锁对象来调用,而我们的锁对象可以是任意对象。
Object可以表示任意对象,所以这些方法在Object中。
package com.momo.demo;
import com.momo.domain.Dumplings;
import com.momo.thread.GetDumplings;
import com.momo.thread.SetDumplings;
public class Demo5 {
public static void main(String[] args) {
Dumplings d = new Dumplings();
SetDumplings sd = new SetDumplings(d); GetDumplings gd = new GetDumplings(d); Thread t1 = new Thread(sd); Thread t2 = new Thread(gd); t2.start(); t1.start(); }
}
package com.momo.domain;
//资源
public class Dumplings {
private String name;
private int price;
public boolean boo;//默认值false,表示没有数据。true表示有数据。
public String getName() { return name; } public void setName(String name) { = name; } public int getPrice() { return price; } public void setPrice(int price) { this.price = price; }
}
package com.momo.thread;
import com.momo.domain.Dumplings;
//生产者线程
public class SetDumplings implements Runnable{
private Dumplings d;
private int i = 0;
public SetDumplings(Dumplings d){
this.d = d;
}
@Override
public void run() {
while (true) {
synchronized (d) {
if(d.boo){//true 表示有数据
try {
d.wait();//生产者等待。
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (i % 2 == 0) {
d.setName("素饺子");
d.setPrice(1);
} else {
d.setName("肉饺子");
d.setPrice(2);
}
System.out.println("生产者:" + d.getName() + "---" + d.getPrice());
i++;
d.boo = true;//修改标记 d.notify();//唤醒 消费者 并不是说它可以直接执行,还是需要抢cpu的执行权。 } } }
}
package com.momo.thread;
import com.momo.domain.Dumplings;
//消费者线程
public class GetDumplings implements Runnable{
private Dumplings d;
public GetDumplings(Dumplings d){
this.d = d;
}
@Override
public void run() {
while (true) {
synchronized (d) {
if(!d.boo){//false没有数据 !false = true
try {
d.wait();//消费者等待, 释放锁。
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("消费者:" + d.getName() + "---" + d.getPrice());
d.boo = false;//修改标记
d.notify();//唤醒 生产者
/* if(d.boo){ System.out.println("消费者:" + d.getName() + "---" + d.getPrice()); }else{ try { d.wait(); } catch (InterruptedException e) { e.printStackTrace(); } }*/ } } }
}
3,多线程的生命周期状态图
-看图
五,线程组
1,线程组:把多个线程分到一起,方便我们对多个线程进行统一的管理。
2,java中使用ThreadGroup 来表示线程组,把多个线程可以放到一组。
使用线程组就可以一次对多个线程进行控制和管理。
-默认情况下,所有的线程都属于主线程组。 main组
ThreadGroup getThreadGroup()
返回此线程所属的线程组。
String getName()
返回此线程组的名称。
-我们也可以设置线程组
Thread(ThreadGroup group, Runnable target)
分配一个新的 Thread对象。
Thread(ThreadGroup group, Runnable target, String name)
分配一个新的 Thread对象,使其具有 target作为其运行对象,具有指定的 name作为其名称,属于 group引用的线程组。
Thread(ThreadGroup group, String name)
分配一个新的 Thread对象。
package com.momo.demo;
import com.momo.thread.MyRunna;
/*
- 线程组
- /
public class Demo6 {public static void main(String[] args) {// System.out.println(Thread.currentThread().getThreadGroup().getName());/
Thread t1 = new Thread(m,"美女一号");
Thread t2 = new Thread(m,"美女二号");*/
/* ThreadGroup tg1 = t1.getThreadGroup(); ThreadGroup tg2 = t2.getThreadGroup(); String n1 = tg1.getName(); String n2 = tg2.getName(); System.out.println(n1); System.out.println(n2);*/ ThreadGroup tg = new ThreadGroup("三国组"); MyRunna m = new MyRunna(); Thread t1 = new Thread(tg,m,"貂蝉"); Thread t2 = new Thread(tg,m,"孙尚香"); Thread t3 = new Thread(tg,m,"黄月英"); System.out.println(t1.getThreadGroup().getName()); System.out.println(t2.getThreadGroup().getName()); System.out.println(t3.getThreadGroup().getName()); // tg.setDaemon(true);
}
}
package com.momo.thread;
public class MyRunna implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } }
package com.momo.demo;
import com.momo.domain.Girl;
import com.momo.thread.GetThread;
import com.momo.thread.SetThread;
/*
- 改进版的生产者和消费者
- 把资源类的成员私有
- 把生产和消费用方法封装,将来在线程中调用方法即可。
- */
public class Demo7 {
public static void main(String[] args) {
Girl g = new Girl();
GetThread gt = new GetThread(g);
SetThread st = new SetThread(g);
Thread t1 = new Thread(gt); Thread t2 = new Thread(st); t1.start(); t2.start();
}
}
package com.momo.domain;
//资源类
public class Girl {
private String name;
private int age;
private boolean boo;
public synchronized void setGirl(String name,int age){
if(this.boo){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
= name;
this.age = age;
System.out.println("我new了一个:"+name+"---"+age);
this.boo = true;
this.notify();
}
public synchronized void getGirl(){
if(!this.boo){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(+"--"+this.age);
this.boo = false;
this.notify();
}
}
package com.momo.thread;
import com.momo.domain.Girl;
public class SetThread implements Runnable{
private Girl g;
private int i = 0;
public SetThread(Girl g){
this.g = g;
}
@Override
public void run() {
while (true) {
if (i % 2 == 0) {
g.setGirl("哈尼克子", 18);
} else {
g.setGirl("苍老师", 30);
}
i++;
}
}
}
package com.momo.thread;
import com.momo.domain.Girl;
public class GetThread implements Runnable{ private Girl g; public GetThread(Girl g){ this.g = g; } @Override public void run() { while (true){ g.getGirl(); } } }