关于volitale,notify,wait面试问题:
1、问题:两个线程操作一个对象时,使得其中的一个线程不要一直死循环等待另外一个线程
解决思路:上述情况如果不添加volatile关键字就会是一个线程一直循环等待
(1)代码示例:
package net.oschina.tkj.mulitcoding.notifykey; import java.util.ArrayList; import java.util.List; /** * notify,wait wait:使得线程处于等待状态,并且释放线程的锁 notify:唤醒一个线程,但是不释放锁,因此可能会产生线程信息不同步的问题 * notify与wait都是改变线程状态的方法,因此要放到synchronizd中 * * 1.如下问题:两个线程操作一个对象时,使得其中的一个线程不要一直死循环等待另外一个线程 * * 上述情况如果不添加volatile关键字就会是一个线程一直循环等待 * * * @author Freedom * */ public class NotifyWaitV1 { private static volatile List<String> list = new ArrayList<>(); public void add() { list.add("freedom"); } public int size() { return list.size(); } // 开启两个线程 public static void main(String[] args) { final NotifyWaitV1 v1 = new NotifyWaitV1(); Thread t1 = new Thread(new Runnable() { @Override public void run() { try { for (int i = 0; i < 10; i++) { v1.add(); System.out.println("当前线程:" + Thread.currentThread().getName() + "添加了一个元素!"); Thread.sleep(100); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }, "t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { while (true) { if (v1.size() == 5) { System.out.println("当前线程:" + Thread.currentThread().getName() + " list.size==5退出循环,任务执行完成!"); throw new RuntimeException(); } } } }, "t2"); t2.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } t1.start(); } }
(2)结果展示:
①不添加“volatile”关键字
②添加volatile关键字
2、用volatile解决了,一个线程空等待的问题 但是,while循环浪费性能,有没有一种机制来解决该问题?
解决思路: 此时,可以使用notify,wait机制 通过执行结果可以得出,notify唤醒一个线程,但是不释放锁,示例中只有当t1线程执行结束之后,才会执行t2线程内容。
(1)代码示例
package net.oschina.tkj.mulitcoding.notifykey; import java.util.ArrayList; import java.util.List; /** * notify,wait wait:使得线程处于等待状态,并且释放线程的锁 notify:唤醒一个线程,但是不释放锁,因此可能会产生线程信息不同步的问题 * notify与wait都是改变线程状态的方法,因此要放到synchronizd中 * * 1.如下问题:两个线程操作一个对象时,使得其中的一个线程不要一直死循环等待另外一个线程 * * 上述情况如果不添加volatile关键字就会是一个线程一直循环等待 * * 问题2,用volatile解决了,一个线程空等待的问题 但是,while循环浪费性能,有没有一种机制来解决该问题? * * 此时,可以使用notify,wait机制 通过执行结果可以得出,notify唤醒一个线程,但是不释放锁, * 示例中只有当t1线程执行结束之后,才会执行t2线程内容 * * @author Freedom * */ public class NotifyWaitV2 { private static volatile List<String> list = new ArrayList<>(); private static final Object lock = new Object(); public void add() { list.add("freedom"); } public int size() { return list.size(); } // 开启两个线程 public static void main(String[] args) { final NotifyWaitV2 v1 = new NotifyWaitV2(); Thread t1 = new Thread(new Runnable() { @Override public void run() { try { synchronized (lock) { for (int i = 0; i < 10; i++) { v1.add(); System.out.println("当前线程:" + Thread.currentThread().getName() + "添加了一个元素!"); Thread.sleep(100); if (v1.size() == 5) { System.out.println("当前线程:" + Thread.currentThread().getName() + " 发出了notify通知!"); lock.notify(); } } } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }, "t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { synchronized (lock) { if (v1.size() != 5) { try { lock.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println("当前线程:" + Thread.currentThread().getName() + " list.size==5退出循环,任务执行完成!"); throw new RuntimeException(); } } }, "t2"); t2.start(); try { Thread.sleep(3000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } t1.start(); } }
(2)运行结果:
3、问题2通过notify,wait机制解决了循环空等对资源浪费的情况,但是如果对于高并发情况下大量数据,当执行到某个业务逻辑节点时,需要唤醒另外一个线程对当前节点数据处理,使用notify通知,并不解决线程间的实时通信问题(notify不释放线程的锁),所以需要考虑另外一种思路。
解决方案使用:CountDownLatch类对象
(1)代码示例
package net.oschina.tkj.mulitcoding.notifykey; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; /** * notify,wait wait:使得线程处于等待状态,并且释放线程的锁 notify:唤醒一个线程,但是不释放锁,因此可能会产生线程信息不同步的问题 * notify与wait都是改变线程状态的方法,因此要放到synchronizd中 * * ###问题1### 如下问题:两个线程操作一个对象时,使得其中的一个线程不要一直死循环等待另外一个线程 * * 上述情况如果不添加volatile关键字就会是一个线程一直循环等待 * * ###问题2### 用volatile解决了,一个线程空等待的问题 但是,while循环浪费性能,有没有一种机制来解决该问题? * 此时,可以使用notify,wait机制 通过执行结果可以得出,notify唤醒一个线程,但是不释放锁, * 示例中只有当t1线程执行结束之后,才会执行t2线程内容 * * ###问题3### notify与wait方法可以解决循环空转浪费资源的问题,但是没法解决,线程间消息的实时更新问题 因此,需要使用 * * @author Freedom * */ public class NotifyWaitV3 { private static volatile List<String> list = new ArrayList<>(); // private static final Object lock = new Object(); private static final CountDownLatch countDownLatch = new CountDownLatch(1); public void add() { list.add("freedom"); } public int size() { return list.size(); } // 开启两个线程 public static void main(String[] args) { final NotifyWaitV3 v1 = new NotifyWaitV3(); Thread t1 = new Thread(new Runnable() { @Override public void run() { try { // synchronized (lock) { for (int i = 0; i < 10; i++) { v1.add(); System.out.println("当前线程:" + Thread.currentThread().getName() + "添加了一个元素!"); Thread.sleep(100); if (v1.size() == 5) { System.out.println("当前线程:" + Thread.currentThread().getName() + " 发出了countDown通知!"); // lock.notify(); countDownLatch.countDown(); } } // } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }, "t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { // synchronized (lock) { if (v1.size() != 5) { try { // lock.wait(); countDownLatch.await();// t2线程等待 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println("当前线程:" + Thread.currentThread().getName() + " list.size==5退出循环,任务执行完成!"); throw new RuntimeException(); } // } }, "t2"); t2.start(); try { Thread.sleep(300); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } t1.start(); } }
(2)运行结果:
4、总结
(1)volatile,可以解决多个线程共享数据时的可见性,但是没法保证原子性操作;
(2)wait使得线程处于等待状态,会释放线程的锁;
(3)notify唤醒一个线程,但是会持有线程的锁;
(4)sleep使得线程休眠一段时间,会持有线程的锁;