线程间通信
wait / notify
package SynchronizedCass; /* * synchronized代码块也能将线程工作内存中的私有变量与公共内存的变量同步 */ public class t6 { public static void main(String[] args) { try { Object lock = new Object(); MyThread1 t1 = new MyThread1(lock); t1.start(); Thread.sleep(3000); MyThread2 t2 = new MyThread2(lock); t2.start(); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } class MyThread1 extends Thread { private Object lock; public MyThread1(Object lock) { super(); this.lock = lock; } @Override public void run() { try { synchronized(lock) { System.out.println("开始 wait time = " + System.currentTimeMillis()); lock.wait(); System.out.println("结束 wait time = " + System.currentTimeMillis()); } } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } class MyThread2 extends Thread { private Object lock; public MyThread2(Object lock) { super(); this.lock = lock; } @Override public void run() { synchronized(lock) { System.out.println("开始 notify time = " + System.currentTimeMillis()); lock.notify(); System.out.println("结束 notify time = " + System.currentTimeMillis()); } } }
开始 wait time = 1603525965724 开始 notify time = 1603525968732 结束 notify time = 1603525968732 结束 wait time = 1603525968732
wait使线程停止运行,而notify使停止的线程继续运行。
wait()方法的作用是使当前执行代码的线程进行等待,将其置入“预执行队列”中。wait所在代码行处停止执行,直到接到通知或被中断为止,线程与其他线程竞争锁。
注意:在调用wait之前,线程必须获得该对象的对象级别锁(也就是说,只能在同步方法或同步快中调用wait()方法);如果调用wait()时没有持有适当的锁,则会抛出异常。
notify()方法也要在同步方法或者同步块中调用,即在调用前,线程必须获得该对象的对象级别锁。如果有多个线程等待,则由线程规划器随机挑选出其中的一个呈wait状态的线程,对其发出notify,并使它等待获取该对象的对象锁。特别说明:执行notify()方法后,当前线程不会马上释放该对象锁,呈wait状态的线程也并不能马上获取该对象锁,要等到执行notify方法的线程将程序执行完,也就是退出synchronized代码块之后,当前线程才会释放锁。
分析上述例子可以发现:第二个线程将第一个线程唤醒,不过要等到第二个线程中的synchronized同步代码块中的代码执行完,才能返回到第一个线程的wait()处。
package SynchronizedCass; import java.util.ArrayList; import java.util.List; /* * synchronized代码块也能将线程工作内存中的私有变量与公共内存的变量同步 */ public class t6 { public static void main(String[] args) { try { Object lock = new Object(); MyThread1 t1 = new MyThread1(lock); t1.start(); Thread.sleep(3000); MyThread2 t2 = new MyThread2(lock); t2.start(); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } class Mylist { private static List<String> list = new ArrayList<>(); public static void add() { list.add("anyString"); } public static int size() { return list.size(); } } class MyThread1 extends Thread { private Object lock; public MyThread1(Object lock) { super(); this.lock = lock; } @Override public void run() { try { synchronized(lock) { if(Mylist.size() != 5) { System.out.println("开始 wait time = " + System.currentTimeMillis()); lock.wait(); System.out.println("结束 wait time = " + System.currentTimeMillis()); } } } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } class MyThread2 extends Thread { private Object lock; public MyThread2(Object lock) { super(); this.lock = lock; } @Override public void run() { synchronized(lock) { for(int i = 0; i < 10; i++) { Mylist.add(); if(Mylist.size() == 5) { lock.notify(); System.out.println("已经发出通知!"); } System.out.println("添加了" + (i + 1) + "个元素!"); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } } } package SynchronizedCass; import java.util.ArrayList; import java.util.List; /* * synchronized代码块也能将线程工作内存中的私有变量与公共内存的变量同步 */ public class t6 { public static void main(String[] args) { try { Object lock = new Object(); MyThread1 t1 = new MyThread1(lock); t1.start(); Thread.sleep(3000); MyThread2 t2 = new MyThread2(lock); t2.start(); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } class Mylist { private static List<String> list = new ArrayList<>(); public static void add() { list.add("anyString"); } public static int size() { return list.size(); } } class MyThread1 extends Thread { private Object lock; public MyThread1(Object lock) { super(); this.lock = lock; } @Override public void run() { try { synchronized(lock) { if(Mylist.size() != 5) { System.out.println("开始 wait time = " + System.currentTimeMillis()); lock.wait(); System.out.println("结束 wait time = " + System.currentTimeMillis()); } } } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } class MyThread2 extends Thread { private Object lock; public MyThread2(Object lock) { super(); this.lock = lock; } @Override public void run() { synchronized(lock) { for(int i = 0; i < 10; i++) { Mylist.add(); if(Mylist.size() == 5) { lock.notify(); System.out.println("已经发出通知!"); } System.out.println("添加了" + (i + 1) + "个元素!"); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } } }
package SynchronizedCass; import java.util.ArrayList; import java.util.List; /* * synchronized代码块也能将线程工作内存中的私有变量与公共内存的变量同步 */ public class t6 { public static void main(String[] args) { try { Object lock = new Object(); MyThread1 t1 = new MyThread1(lock); t1.start(); Thread.sleep(3000); MyThread2 t2 = new MyThread2(lock); t2.start(); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } class Mylist { private static List<String> list = new ArrayList<>(); public static void add() { list.add("anyString"); } public static int size() { return list.size(); } } class MyThread1 extends Thread { private Object lock; public MyThread1(Object lock) { super(); this.lock = lock; } @Override public void run() { try { synchronized(lock) { if(Mylist.size() != 5) { System.out.println("开始 wait time = " + System.currentTimeMillis()); lock.wait(); System.out.println("结束 wait time = " + System.currentTimeMillis()); } } } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } class MyThread2 extends Thread { private Object lock; public MyThread2(Object lock) { super(); this.lock = lock; } @Override public void run() { synchronized(lock) { for(int i = 0; i < 10; i++) { Mylist.add(); if(Mylist.size() == 5) { lock.notify(); System.out.println("已经发出通知!"); } System.out.println("添加了" + (i + 1) + "个元素!"); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } } } package SynchronizedCass; import java.util.ArrayList; import java.util.List; /* * synchronized代码块也能将线程工作内存中的私有变量与公共内存的变量同步 */ public class t6 { public static void main(String[] args) { try { Object lock = new Object(); MyThread1 t1 = new MyThread1(lock); t1.start(); Thread.sleep(3000); MyThread2 t2 = new MyThread2(lock); t2.start(); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } class Mylist { private static List<String> list = new ArrayList<>(); public static void add() { list.add("anyString"); } public static int size() { return list.size(); } } class MyThread1 extends Thread { private Object lock; public MyThread1(Object lock) { super(); this.lock = lock; } @Override public void run() { try { synchronized(lock) { if(Mylist.size() != 5) { System.out.println("开始 wait time = " + System.currentTimeMillis()); lock.wait(); System.out.println("结束 wait time = " + System.currentTimeMillis()); } } } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } class MyThread2 extends Thread { private Object lock; public MyThread2(Object lock) { super(); this.lock = lock; } @Override public void run() { synchronized(lock) { for(int i = 0; i < 10; i++) { Mylist.add(); if(Mylist.size() == 5) { lock.notify(); System.out.println("已经发出通知!"); } System.out.println("添加了" + (i + 1) + "个元素!"); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } } }
开始 wait time = 1603528243105 添加了1个元素! 添加了2个元素! 添加了3个元素! 添加了4个元素! 已经发出通知! 添加了5个元素! 添加了6个元素! 添加了7个元素! 添加了8个元素! 添加了9个元素! 添加了10个元素! 结束 wait time = 1603528256153
wait end 在最后输出,这也说明notify()方法执行后并不立即释放锁。
如果发出notify操作时没有处于阻塞状态的线程,那么该命令会被忽略。
关键字synchronized可以将任何一个Object对象作同步对象看待,而java为每个Object都实现了wait和notify,他们必须用在被synchronized同步的Object的临界区内。
线程状态切换示意图
这张图常常考,背吧
方法wait()锁自动释放与方法notify()锁不释放
package SynchronizedCass; /* * wait方法自动释放,notify不自动释放锁 */ public class t4 { public static void main(String[] args) { Object lock = new Object(); ThreadC a = new ThreadC(lock); a.setName("进程1"); a.start(); ThreadD b = new ThreadD(lock); b.setName("进程2"); b.start(); synNotifyMethodThread c = new synNotifyMethodThread(lock); c.setName("进程3"); c.start(); } } class service1 { public void testMethod(Object lock) { try { synchronized(lock) { System.out.println("begin wait() ThreadName = " + Thread.currentThread().getName()); lock.wait(); System.out.println(" end wait() ThreadName = " + Thread.currentThread().getName()); } } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } public void synNotifyMethod(Object lock) { try { synchronized(lock) { System.out.println("begin notify() ThreadName = " + Thread.currentThread().getName() + " time = " + System.currentTimeMillis()); lock.notify(); Thread.sleep(5000); System.out.println(" end notify() ThreadName = " + Thread.currentThread().getName() + " time = " + System.currentTimeMillis()); } } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } class ThreadC extends Thread { private Object lock; public ThreadC(Object lock) { super(); this.lock = lock; } @Override public void run() { super.run(); service1 ser = new service1(); ser.testMethod(lock); } } class ThreadD extends Thread { private Object lock; public ThreadD(Object lock) { super(); this.lock = lock; } @Override public void run() { super.run(); service1 ser = new service1(); ser.synNotifyMethod(lock); } } class synNotifyMethodThread extends Thread { private Object lock; public synNotifyMethodThread(Object lock) { super(); this.lock = lock; } @Override public void run() { super.run(); service1 ser = new service1(); ser.synNotifyMethod(lock); } }
begin wait() ThreadName = 进程1 begin notify() ThreadName = 进程2 time = 1603532556469 end notify() ThreadName = 进程2 time = 1603532561472 end wait() ThreadName = 进程1 begin notify() ThreadName = 进程3 time = 1603532561472 end notify() ThreadName = 进程3 time = 1603532566473
这个实验说明,必须执行完notify()方法所在的同步synchronized代码块后才释放锁
还有在执行同步代码块的过程中,遇到异常而导致线程终止,锁也会被释放。
package SynchronizedCass; /* * 调用方法notify一次只随机通知一个线程进行唤醒 */ public class t4 { public static void main(String[] args) throws InterruptedException { Object lock = new Object(); ThreadC a = new ThreadC(lock); a.setName("进程1"); a.start(); ThreadD b = new ThreadD(lock); b.setName("进程2"); b.start(); ThreadE c = new ThreadE(lock); c.setName("进程3"); c.start(); NotifyThread notifyThread = new NotifyThread(lock); notifyThread.setName("进程4"); notifyThread.start(); } } class service1 { public void testMethod(Object lock) { try { synchronized(lock) { System.out.println("begin wait() ThreadName = " + Thread.currentThread().getName()); lock.wait(); System.out.println(" end wait() ThreadName = " + Thread.currentThread().getName()); } } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } // public void synNotifyMethod(Object lock) { // try { // synchronized(lock) { // System.out.println("begin notify() ThreadName = " + Thread.currentThread().getName() + // " time = " + System.currentTimeMillis()); // lock.notify(); // Thread.sleep(5000); // System.out.println(" end notify() ThreadName = " + Thread.currentThread().getName() + // " time = " + System.currentTimeMillis()); // } // } catch (InterruptedException e) { // // TODO 自动生成的 catch 块 // e.printStackTrace(); // } // } } class ThreadC extends Thread { private Object lock; public ThreadC(Object lock) { super(); this.lock = lock; } @Override public void run() { super.run(); service1 ser = new service1(); ser.testMethod(lock); } } class ThreadD extends Thread { private Object lock; public ThreadD(Object lock) { super(); this.lock = lock; } @Override public void run() { super.run(); service1 ser = new service1(); ser.testMethod(lock); } } class ThreadE extends Thread { private Object lock; public ThreadE(Object lock) { super(); this.lock = lock; } @Override public void run() { super.run(); service1 ser = new service1(); ser.testMethod(lock); } } class NotifyThread extends Thread { private Object lock; public NotifyThread(Object lock) { super(); this.lock = lock; } @Override public void run() { synchronized (lock) { lock.notify(); lock.notify(); lock.notify(); } } }
begin wait() ThreadName = 进程1 begin wait() ThreadName = 进程2 begin wait() ThreadName = 进程3 end wait() ThreadName = 进程1 end wait() ThreadName = 进程3 end wait() ThreadName = 进程2
调用方法notify()一次只能随机通知一个线程唤醒。当多次调用nitify方法时,会随机将等待wait状态的线程进行唤醒。为了唤醒全部线程,可以使用notifyAll()方法。
方法wait(long)的使用
带一个参数的wait(long)方法的功能是:等待某一时间内是否有线程对锁进行唤醒,如果超过这个时间则自动唤醒。注意一个问题,如果notify通知过早,则会打乱程序正常的运行逻辑。
下面讲解生产者-消费者问题
package SynchronizedCass; public class P { public static void main(String[] args) { String lock = new String(""); Product p = new Product(lock); Consume c = new Consume(lock); ThreadP threadp = new ThreadP(p); ThreadCo threadc = new ThreadCo(c); threadp.start(); threadc.start(); } } //生产者 class Product { private String lock; public Product(String lock) { super(); this.lock = lock; } public void setValue() { try { synchronized (lock) { if(!ValueObject.value.equals("")) { lock.wait(); //如果ValueObject非空,说明有对象,就暂停生产 } String value = System.currentTimeMillis() + "_" + System.nanoTime(); System.out.println("set的值是" + value); ValueObject.value = value; lock.notify(); } } catch (InterruptedException e) { e.printStackTrace(); } } } //消费者 class Consume { private String lock; public Consume(String lock) { super(); this.lock = lock; } public void getValue() { try { synchronized(lock) { if(ValueObject.value.equals("")) { lock.wait(); } System.out.println("get 的值是 " + ValueObject.value); ValueObject.value = ""; lock.notify(); //消费者取走后,要唤醒生产者继续生产 } } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } } //创建存储值的对象ValueObejct.java class ValueObject { public static String value = " "; } //生产者进程 class ThreadP extends Thread { private Product p; public ThreadP(Product p) { // TODO 自动生成的构造函数存根 super(); this.p = p; } @Override public void run() { while(true) { p.setValue(); } } } //消费者进程 class ThreadCo extends Thread { private Consume c; public ThreadCo(Consume c) { super(); this.c = c; } @Override public void run() { while(true) { c.getValue(); } } }
set的值是1603617803974_718699827459900 get 的值是 1603617803974_718699827459900 set的值是1603617803974_718699827472100 get 的值是 1603617803974_718699827472100 set的值是1603617803974_718699827484400 get 的值是 1603617803974_718699827484400 set的值是1603617803974_718699827496400 get 的值是 1603617803974_718699827496400 set的值是1603617803974_718699827509400
...
从结果可以看出,本例子是1个生产者和1个消费者进行数据交互,控制台打印的日志get和set是小题运行的。
多生产与多消费:操作值-假死
”假死“的现象其实就是线程进入WAITING等待状态。如果全部线程都进入WAITING状态,则程序就不再执行任何业务功能了,整个项目呈停止状态。
简单分析一下:在代码中已经使用了wait/notify,但不保证notify唤醒的是异类,也许是同类,比如“生产者”唤醒“生产者”,或“消费者”唤醒“消费者这样的情况”。这样积少成多,就会导致所有的线程都不能执行下去,大家都处于等待的状态。解决的办法是将notify()改成notifyAll方法即可,他就不光通知同类线程,也包括异类。
一生产与一消费——栈操作
package SynchronizedCass; import java.util.ArrayList; import java.util.List; public class P { public static void main(String[] args) { MyStack myStack = new MyStack(); Product p = new Product(myStack); Consume c = new Consume(myStack); ThreadP threadp = new ThreadP(p); ThreadCo threadc = new ThreadCo(c); threadp.start(); threadc.start(); } } /*list的最大容量是1*/ class MyStack { private List<String> list = new ArrayList<>(); synchronized public void push() { try { if(list.size() == 1) { this.wait(); } list.add("anyString = " + Math.random()); this.notify(); System.out.println("push = " + list.size()); }catch (InterruptedException e) { e.printStackTrace(); } } synchronized public String pop() { String returnValue = ""; try { if(list.size() == 0) { System.out.println("pop操作中的:" + Thread.currentThread().getName() + "线程呈wait状态"); this.wait(); } returnValue = "" + list.get(0); list.remove(0); this.notify(); System.out.println("pop = " + list.size()); }catch (InterruptedException e) { e.printStackTrace(); } return returnValue; } } //生产者 class Product { private MyStack myStack; public Product(MyStack myStack) { super(); this.myStack = myStack; } public void pushService() { myStack.push(); } } //消费者 class Consume { private MyStack myStack; public Consume(MyStack myStack) { super(); this.myStack = myStack; } public void popService() { System.out.println("pop = " + myStack.pop()); } } //生产者进程 class ThreadP extends Thread { private Product p; public ThreadP(Product p) { // TODO 自动生成的构造函数存根 super(); this.p = p; } @Override public void run() { while(true) { p.pushService(); } } } //消费者进程 class ThreadCo extends Thread { private Consume c; public ThreadCo(Consume c) { super(); this.c = c; } @Override public void run() { while(true) { c.popService(); } } }
pop = anyString = 0.08250087230110892 push = 1 pop = 0 pop = anyString = 0.5222318002615051 push = 1 pop = 0 pop = anyString = 0.646779057439545 push = 1 pop = 0 pop = anyString = 0.6995607244467061 push = 1 pop = 0
通过使用生产者/消费者模式,本例的结果,值在0和1之间进行交替,也就是生产和消费两个过程在交替执行。
package SynchronizedCass; import java.util.ArrayList; import java.util.List; public class P { public static void main(String[] args) { MyStack myStack = new MyStack(); Product p = new Product(myStack); Consume c = new Consume(myStack); Consume c1 = new Consume(myStack); Consume c2 = new Consume(myStack); Consume c3 = new Consume(myStack); ThreadP threadp = new ThreadP(p); ThreadCo threadc = new ThreadCo(c); ThreadCo threadc1 = new ThreadCo(c1); ThreadCo threadc2 = new ThreadCo(c2); ThreadCo threadc3 = new ThreadCo(c3); threadp.start(); threadc.start(); threadc1.start(); threadc2.start(); threadc3.start(); } } /*list的最大容量是1*/ class MyStack { private List<String> list = new ArrayList<>(); synchronized public void push() { try { while(list.size() == 1) { this.wait(); } list.add("anyString = " + Math.random()); this.notifyAll(); System.out.println("push = " + list.size()); }catch (InterruptedException e) { e.printStackTrace(); } } synchronized public String pop() { String returnValue = ""; try { while(list.size() == 0) { System.out.println("pop操作中的:" + Thread.currentThread().getName() + "线程呈wait状态"); this.wait(); } returnValue = "" + list.get(0); list.remove(0); this.notifyAll(); System.out.println("pop = " + list.size()); }catch (InterruptedException e) { e.printStackTrace(); } return returnValue; } } //生产者 class Product { private MyStack myStack; public Product(MyStack myStack) { super(); this.myStack = myStack; } public void pushService() { myStack.push(); } } //消费者 class Consume { private MyStack myStack; public Consume(MyStack myStack) { super(); this.myStack = myStack; } public void popService() { System.out.println("pop = " + myStack.pop()); } } //生产者进程 class ThreadP extends Thread { private Product p; public ThreadP(Product p) { // TODO 自动生成的构造函数存根 super(); this.p = p; } @Override public void run() { while(true) { p.pushService(); } } } //消费者进程 class ThreadCo extends Thread { private Consume c; public ThreadCo(Consume c) { super(); this.c = c; } @Override public void run() { while(true) { c.popService(); } } }
pop操作中的:Thread-1线程呈wait状态 push = 1 pop = 0 pop = anyString = 0.9837796821362911 pop操作中的:Thread-2线程呈wait状态 pop操作中的:Thread-3线程呈wait状态 pop操作中的:Thread-4线程呈wait状态 pop操作中的:Thread-1线程呈wait状态 push = 1 pop = 0 pop = anyString = 0.2304706772528935 pop操作中的:Thread-4线程呈wait状态 pop操作中的:Thread-3线程呈wait状态 pop操作中的:Thread-2线程呈wait状态 pop操作中的:Thread-1线程呈wait状态 push = 1 pop = 0 pop = anyString = 0.10921236392571365
。。。
注意:
①、if(list.size() == 0)这个wait()条件,可能会导致程序发生异常。因为条件发生改变时并没有得到及时的响应,所以多个呈现wait状态的线程先后被唤醒,继而执行list.remove(0)代码而出现异常。举一个例子:现在c,c1,c2,c3四个消费这进程处于wait状态,生产者进程p给list加了一个元素后,list中有且只有一个元素。而p唤醒了c,c进程remove了一个元素,然后唤醒了c1,c1退出条件,也remove一个元素。但是list中的元素不超过1,所以会出现索引异常。正确的做法是把if(list.size() == 0)==========>换成while(list.size() == 0) ,退出wait还要再判断能否取list中的元素。
还需要注意的可能会发生“假死”的现象,导致所有进程都处于WAITING,所以最好将notify改为notifyAll。
多生产与多消费:栈操作
多生产多消费的核心代码同一生产多消费,只需要在添加几个生产者进程即可。
package SynchronizedCass; import java.util.ArrayList; import java.util.List; public class P { public static void main(String[] args) { MyStack myStack = new MyStack(); Product p = new Product(myStack); Product p1 = new Product(myStack); Product p2 = new Product(myStack); Product p3 = new Product(myStack); Consume c = new Consume(myStack); Consume c1 = new Consume(myStack); Consume c2 = new Consume(myStack); Consume c3 = new Consume(myStack); ThreadP threadp = new ThreadP(p); ThreadP threadp1 = new ThreadP(p1); ThreadP threadp2 = new ThreadP(p2); ThreadP threadp3 = new ThreadP(p3); ThreadCo threadc = new ThreadCo(c); ThreadCo threadc1 = new ThreadCo(c1); ThreadCo threadc2 = new ThreadCo(c2); ThreadCo threadc3 = new ThreadCo(c3); threadp.start(); threadp1.start(); threadp2.start(); threadp3.start(); threadc.start(); threadc1.start(); threadc2.start(); threadc3.start(); } } /*list的最大容量是1*/ class MyStack { private List<String> list = new ArrayList<>(); synchronized public void push() { try { while(list.size() == 1) { this.wait(); } list.add("anyString = " + Math.random()); this.notifyAll(); System.out.println("push = " + list.size()); }catch (InterruptedException e) { e.printStackTrace(); } } synchronized public String pop() { String returnValue = ""; try { while(list.size() == 0) { System.out.println("pop操作中的:" + Thread.currentThread().getName() + "线程呈wait状态"); this.wait(); } returnValue = "" + list.get(0); list.remove(0); this.notifyAll(); System.out.println("pop = " + list.size()); }catch (InterruptedException e) { e.printStackTrace(); } return returnValue; } } //生产者 class Product { private MyStack myStack; public Product(MyStack myStack) { super(); this.myStack = myStack; } public void pushService() { myStack.push(); } } //消费者 class Consume { private MyStack myStack; public Consume(MyStack myStack) { super(); this.myStack = myStack; } public void popService() { System.out.println("pop = " + myStack.pop()); } } //生产者进程 class ThreadP extends Thread { private Product p; public ThreadP(Product p) { // TODO 自动生成的构造函数存根 super(); this.p = p; } @Override public void run() { while(true) { p.pushService(); } } } //消费者进程 class ThreadCo extends Thread { private Consume c; public ThreadCo(Consume c) { super(); this.c = c; } @Override public void run() { while(true) { c.popService(); } } }
pop操作中的:Thread-4线程呈wait状态 push = 1 pop = 0 pop = anyString = 0.9242501412687139 pop操作中的:Thread-6线程呈wait状态 pop操作中的:Thread-5线程呈wait状态 pop操作中的:Thread-7线程呈wait状态 pop操作中的:Thread-4线程呈wait状态 push = 1 pop = 0 pop = anyString = 0.0698453157466532 pop操作中的:Thread-7线程呈wait状态 pop操作中的:Thread-5线程呈wait状态 pop操作中的:Thread-6线程呈wait状态 pop操作中的:Thread-4线程呈wait状态 push = 1 pop = 0
实战:等待/通知
创建20个线程,其中10个线程是将数据备份到A数据库,另外10个线程是将数据备份到B数据库,然后备份A数据库和备份B数据库交叉进行。
package SynchronizedCass; public class wait_notify_insert_test { public static void main(String[] args) { DBTools dbtools = new DBTools(); BackupA[] threadA = new BackupA[10]; BackupB[] threadB = new BackupB[10]; for(int i = 0; i < 10; i++) { threadA[i] = new BackupA(dbtools); threadA[i].start(); threadB[i] = new BackupB(dbtools); threadB[i].start(); } } } class DBTools { //prevIsA = true 表示前一个星串是数据库A首先执行的,那么下次就换成数据库B执行 volatile private boolean prevIsA = false; synchronized public void backupA() { try { while(prevIsA == true) { wait(); } for(int i = 0; i < 5; i++) { System.out.println("★★★★★★★★★★"); } prevIsA = true; notifyAll(); }catch (InterruptedException e) { e.printStackTrace(); } } synchronized public void backupB() { try { while(prevIsA == false) { wait(); } for(int i = 0; i < 5; i++) { System.out.println("☆☆☆☆☆☆☆☆☆☆"); } prevIsA = false; notifyAll(); }catch (InterruptedException e) { e.printStackTrace(); } } } class BackupA extends Thread { private DBTools dbtools; public BackupA(DBTools dbtools) { this.dbtools = dbtools; } @Override public void run() { super.run(); dbtools.backupA(); } } class BackupB extends Thread { private DBTools dbtools; public BackupB(DBTools dbtools) { this.dbtools = dbtools; } @Override public void run() { super.run(); dbtools.backupB(); } }
★★★★★★★★★★ ★★★★★★★★★★ ★★★★★★★★★★ ★★★★★★★★★★ ★★★★★★★★★★ ☆☆☆☆☆☆☆☆☆☆ ☆☆☆☆☆☆☆☆☆☆ ☆☆☆☆☆☆☆☆☆☆ ☆☆☆☆☆☆☆☆☆☆ ☆☆☆☆☆☆☆☆☆☆ ★★★★★★★★★★ ★★★★★★★★★★ ★★★★★★★★★★ ★★★★★★★★★★ ★★★★★★★★★★ ☆☆☆☆☆☆☆☆☆☆ ☆☆☆☆☆☆☆☆☆☆ ☆☆☆☆☆☆☆☆☆☆ ☆☆☆☆☆☆☆☆☆☆ ☆☆☆☆☆☆☆☆☆☆ ★★★★★★★★★★ ★★★★★★★★★★ ★★★★★★★★★★ ★★★★★★★★★★ ★★★★★★★★★★
打印输出是交替的,这里使用了valatile关键字使得prevIsA对20个线程之间是可见的。
join方法的使用
使用背景:在很多情况下,主线程创建并启动子线程,如果子线程中要进行大量的耗时运算,主线程往往将遭遇子线程结束之前结束。这时,如果主线程想等待子线程执行完成之后再结束(比如子线程处理一个数据,主线程要取得这个数据中的值,就要用到join方法)。
下面看一个例子:
package SynchronizedCass; public class t7 { public static void main(String[] args) { try { MyThread threadTest = new MyThread(); threadTest.start(); threadTest.join(); System.out.println("我想当ThreadTest对象执行完毕后,我再执行,我做到了!"); }catch (InterruptedException e) { e.printStackTrace(); } } } class MyThread extends Thread { @Override public void run() { try { int secondValue = (int) (Math.random() * 10000); System.out.println(secondValue); Thread.sleep(secondValue); System.out.println("这次我ThreadTest对象时真的执行完了!"); }catch (InterruptedException e) { e.printStackTrace(); } } }
ThreadTest对象要等待5858毫秒。 这次我ThreadTest对象时真的执行完了! 我想当ThreadTest对象执行完毕后,我再执行,我做到了!
从实验结果上来看,threadTest.join()的确使得主线程在子线程执行完毕以后在执行后面的代码。我们总结一下join方法:
方法join方法使 所属的线程x正常执行run()方法中的任务,而使当前线程y无限期的阻塞,等待线程x销毁以后再继续执行线程y后面的代码。
可以看出方法join()具有使线程排队运行的作用,有点类似于同步运行。但是与synchronized还有些区别:
- join在内部使用了wait()方法进行等待,
- synchronized关键字使用的是“对象监视器”原理,来同步的。
3.2.5 方法join(long)与sleep(long)的区别
先看两个例子:
package SynchronizedCass; public class t9 { public static void main(String[] args) { try { ThreadBB b = new ThreadBB(); ThreadAA a = new ThreadAA(b); a.start(); Thread.sleep(1000); ThreadCC c = new ThreadCC(b); c.start(); }catch (Exception e) { // TODO: handle exception } } } class ThreadAA extends Thread { private ThreadBB b; public ThreadAA(ThreadBB b) { this.b = b; } @Override public void run() { try { synchronized (b) { b.start(); Thread.sleep(6000);//不释放锁 //b.join(6000); } }catch (InterruptedException e) { e.printStackTrace(); } } } class ThreadCC extends Thread { private ThreadBB threadB; public ThreadCC(ThreadBB threadb) { this.threadB = threadb; } @Override public void run() { threadB.bService(); } } class ThreadBB extends Thread { @Override public void run() { try { System.out.println(" b run begin timer = " + System.currentTimeMillis()); Thread.sleep(5000); System.out.println(" b run end timer = " + System.currentTimeMillis()); }catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } synchronized public void bService() { System.out.println("打印了 BService timer = " + System.currentTimeMillis()); } }
b run begin timer = 1603877992713 b run end timer = 1603877997713 打印了 BService timer = 1603877998723
package SynchronizedCass; public class t9 { public static void main(String[] args) { try { ThreadBB b = new ThreadBB(); ThreadAA a = new ThreadAA(b); a.start(); Thread.sleep(1000); ThreadCC c = new ThreadCC(b); c.start(); }catch (Exception e) { // TODO: handle exception } } } class ThreadAA extends Thread { private ThreadBB b; public ThreadAA(ThreadBB b) { this.b = b; } @Override public void run() { try { synchronized (b) { b.start(); //Thread.sleep(6000);//不释放锁 b.join(6000); } }catch (InterruptedException e) { e.printStackTrace(); } } } class ThreadCC extends Thread { private ThreadBB threadB; public ThreadCC(ThreadBB threadb) { this.threadB = threadb; } @Override public void run() { threadB.bService(); } } class ThreadBB extends Thread { @Override public void run() { try { System.out.println(" b run begin timer = " + System.currentTimeMillis()); Thread.sleep(5000); System.out.println(" b run end timer = " + System.currentTimeMillis()); }catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } synchronized public void bService() { System.out.println("打印了 BService timer = " + System.currentTimeMillis()); } }
b run begin timer = 1603878064069 打印了 BService timer = 1603878065081 b run end timer = 1603878069077
对比两个结果可以发现:线程a使用了join(long)时,他会释放b的锁,所以线程c可以调用b中的同步方法synchronized public void bService()。
故当执行join(long),会在内部使用wait(long)方法来实现,所以join(long)会有释放锁的特点,那么其他线程就可以调用此线程中的同步方法了。但是Thread.sleep(long)方法却不会释放锁。
类ThreadLocal的使用
类ThreadLocal解决的是变量在不同线程间的隔离性,也就是不同线程拥有自己的值,不同线程中的值是可以放入ThreadLocal类中进行保存的。线程局部变量(ThreadLocal)为每一个使用该变量的线程都提供一个变量值的副本。
package SynchronizedCass; import java.util.Date; public class t9 { public static void main(String[] args) { try { ThreadAA a = new ThreadAA(); a.setName("a"); a.start(); Thread.sleep(1000); ThreadBB b = new ThreadBB(); b.setName("b"); b.start(); Thread.sleep(1000); }catch (InterruptedException e) { // TODO: handle exception e.printStackTrace(); } } } class ThreadAA extends Thread { @Override public void run() { try { for(int i = 0; i < 10; i++) { if(Tools.tl.get() == null) { Tools.tl.set(new Date()); } System.out.println(Thread.currentThread().getName() + " " + Tools.tl.get().getTime()); Thread.sleep(100); } }catch (InterruptedException e) { // TODO: handle exception e.printStackTrace(); } } } class ThreadBB extends Thread { @Override public void run() { try { for(int i = 0; i < 10; i++) { if(Tools.tl.get() == null) { Tools.tl.set(new Date()); } System.out.println(Thread.currentThread().getName() + " " +Tools.tl.get().getTime()); Thread.sleep(100); } }catch (InterruptedException e) { // TODO: handle exception e.printStackTrace(); } } } class Tools { public static ThreadLocal<Date> tl = new ThreadLocal<>(); }
a 1603889652770 a 1603889652770 a 1603889652770 a 1603889652770 a 1603889652770 a 1603889652770 a 1603889652770 a 1603889652770 a 1603889652770 a 1603889652770 b 1603889653776 b 1603889653776 b 1603889653776 b 1603889653776 b 1603889653776 b 1603889653776 b 1603889653776 b 1603889653776 b 1603889653776 b 1603889653776
作者:Ryanjie
出处:http://www.cnblogs.com/ryanjan/
本文版权归作者和博客园所有,欢迎转载。转载请在留言板处留言给我,且在文章标明原文链接,谢谢!
如果您觉得本篇博文对您有所收获,觉得我还算用心,请点击右下角的 [推荐],谢谢!