1、wait和notify
注意下面的几点:
wait和notify是Object类的常用的方法
wait要释放锁对象
notify不释放锁对象
下面实现这样一个功能:
开启两个线程一个线程向一个list集合中添加元素
另外一个线程如果监听到第一个线程添加元素的编号是5,就退出该线程
如何实现了,我们来看下面的代码
package com.bjsxt.base.conn008; import java.util.ArrayList; import java.util.List; public class ListAdd1 { private volatile static List list = new ArrayList(); public void add(){ list.add("bjsxt"); } public int size(){ return list.size(); } public static void main(String[] args) { final ListAdd1 list1 = new ListAdd1(); Thread t1 = new Thread(new Runnable() { @Override public void run() { try { for(int i = 0; i <10; i++){ list1.add(); System.out.println("当前线程:" + Thread.currentThread().getName() + "添加了一个元素.."); Thread.sleep(500); } } catch (InterruptedException e) { e.printStackTrace(); } } }, "t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { while(true){ if(list1.size() == 5){ System.out.println("当前线程收到通知:" + Thread.currentThread().getName() + " list size = 5 线程停止.."); throw new RuntimeException(); } } } }, "t2"); t1.start(); t2.start(); } }
程序的运行效果是:
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程收到通知:t2 list size = 5 线程停止..
Exception in thread "t2" java.lang.RuntimeException
at com.bjsxt.base.conn008.ListAdd1$2.run(ListAdd1.java:42)
at java.lang.Thread.run(Thread.java:744)
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
有两个点需要注意的地方:
1、第一两个线程都操作了list这个集合,为了保证集合在线程的可见性,所以用volatile申明
2、第二个线程一直开启一个while循环一直在执行判断,当等于5的时候抛出一个运行时的异常退出当前线程
这个办法效率不高一直开启一个线程一直在while循环
应该使用线程之间的通信使用wait和notify
我们来看下面的代码:
package com.bjsxt.base.conn008; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; /** * @author alienware * */ public class ListAdd2 { private volatile static List list = new ArrayList(); public void add(){ list.add("bjsxt"); } public int size(){ return list.size(); } public static void main(String[] args) { final ListAdd2 list2 = new ListAdd2(); final Object lock = new Object(); Thread t1 = new Thread(new Runnable() { @Override public void run() { try { synchronized (lock) { System.out.println("t1启动.."); for(int i = 0; i <10; i++){ list2.add(); System.out.println("当前线程:" + Thread.currentThread().getName() + "添加了一个元素.."); Thread.sleep(500); if(list2.size() == 5){ System.out.println("已经发出通知.."); lock.notify(); } } } } catch (InterruptedException e) { e.printStackTrace(); } } }, "t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { synchronized (lock) { System.out.println("t2启动.."); if(list2.size() != 5){ try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("当前线程:" + Thread.currentThread().getName() + "收到通知线程停止.."); throw new RuntimeException(); } } }, "t2"); t2.start(); t1.start(); } }
程序的运行结果是:
t2启动..
t1启动..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
已经发出通知..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t2收到通知线程停止..
Exception in thread "t2" java.lang.RuntimeException
at com.bjsxt.base.conn008.ListAdd2$2.run(ListAdd2.java:60)
at java.lang.Thread.run(Thread.java:744)
没有达到我们的效果我们来分析下
t2线程先启动,首先获得锁,然后判断当前的编号不是5,执行wait操作,wait操作会将当前的线程阻塞,但是会释放锁,此时t2线程就被阻塞了,但是锁被释放了
t1线程此时就可以获得锁对象,执行代码一直执行,执行到编号是5的时候调用notify函数,notify会发出通知,不会阻塞当前的线程,但是不会释放当前的锁,这样t1线程就无法获得锁对象执行wait阻塞线程后面的代码
执行t1线程添加完成所有的代码之后,线程结束才释放锁,t2线程才能继续执行阻塞后面的代码
如何修改了,当编号是5的时候 t1线程执行notify之后应该释放锁 阻塞t1线程,然后t2线程获得锁对象,执行完成之后释放锁,让t1线程继续执行后续的代码,修改如下
package com.bjsxt.base.conn008; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; /** * @author alienware * */ public class ListAdd2 { private volatile static List list = new ArrayList(); public void add(){ list.add("bjsxt"); } public int size(){ return list.size(); } public static void main(String[] args) { final ListAdd2 list2 = new ListAdd2(); final Object lock = new Object(); Thread t1 = new Thread(new Runnable() { @Override public void run() { try { synchronized (lock) { System.out.println("t1启动.."); for(int i = 0; i <10; i++){ list2.add(); System.out.println("当前线程:" + Thread.currentThread().getName() + "添加了一个元素.."); Thread.sleep(500); if(list2.size() == 5){ System.out.println("已经发出通知.."); lock.notify(); lock.wait(); } } } } catch (InterruptedException e) { e.printStackTrace(); } } }, "t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { synchronized (lock) { System.out.println("t2启动.."); if(list2.size() != 5){ try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("当前线程:" + Thread.currentThread().getName() + "收到通知线程停止.."); lock.notify(); throw new RuntimeException(); } } }, "t2"); t2.start(); t1.start(); } }
我们来看看程序运行的效果:
t2启动..
t1启动..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
已经发出通知..
Exception in thread "t2" java.lang.RuntimeException
at com.bjsxt.base.conn008.ListAdd2$2.run(ListAdd2.java:64)
at java.lang.Thread.run(Thread.java:744)
当前线程:t2收到通知线程停止..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
就达到了我们的效果
上面的这种方式比较复杂,我们可以使用countDownlatch来解决该问题
package com.bjsxt.base.conn008; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; /** * @author alienware * */ public class ListAdd2 { private volatile static List list = new ArrayList(); public void add(){ list.add("bjsxt"); } public int size(){ return list.size(); } public static void main(String[] args) { final ListAdd2 list2 = new ListAdd2(); /** * CountDownLatch不需要和锁对象一起使用 * CountDownLatch(1)的初始化值1,当值变成0的时候就会释放锁对象 * */ final CountDownLatch cLatch = new CountDownLatch(1); Thread t1 = new Thread(new Runnable() { @Override public void run() { try { System.out.println("t1启动.."); for(int i = 0; i <10; i++){ list2.add(); System.out.println("当前线程:" + Thread.currentThread().getName() + "添加了一个元素.."); Thread.sleep(500); if(list2.size() == 5){ System.out.println("已经发出通知.."); cLatch.countDown(); } } } catch (InterruptedException e) { e.printStackTrace(); } } }, "t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { System.out.println("t2启动.."); if(list2.size() != 5){ try { cLatch.await(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println("当前线程:" + Thread.currentThread().getName() + "收到通知线程停止.."); throw new RuntimeException(); } }, "t2"); t2.start(); t1.start(); } }
t2启动..
t1启动..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
已经发出通知..Exception in thread "t2" java.lang.RuntimeException
at com.bjsxt.base.conn008.ListAdd2$2.run(ListAdd2.java:64)
at java.lang.Thread.run(Thread.java:744)
当前线程:t1添加了一个元素..
当前线程:t2收到通知线程停止..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
当前线程:t1添加了一个元素..
就达到了我们的效果