回首翻了翻之前的文章,当时的思路和场景其实还是历历在目(os其实个人觉得自己记忆力还是不错的。。),废话不多说了,没有后续 也没有争取,有的就是坚持和执行力,过去了快6年 我发现自己还是能折腾的。
前提是多 synchronized 同步锁有一定的了解。
在多线程中,如若未借助同步锁的话。我们可以通过多个线程对同一个对象的状态进行处理,
/** * Service 为监控示例对象 */ public class Service { private List<Integer> list = new ArrayList<>(); public void add(){ list.add(1); } public int getSize(){ return list.size(); } }
package com.kirago.cp03.demo01; public class ThreadA extends Thread{ private Service service; public ThreadA(Service service){ super(); this.service = service; } @Override public void run(){ try { for(int i= 0;i<10;i++){ service.add(); System.out.println("list 添加了 " + (i+1) + " 个 元素"); Thread.sleep(1000); } }catch (InterruptedException e){ e.printStackTrace(); } } }
package com.kirago.cp03.demo01; public class ThreadB extends Thread{ private Service service; public ThreadB(Service service){ super(); this.service = service; } @Override public void run(){ try { while (true){ if(service.getSize() >= 5){ System.out.println(">= 5 了,线程 " + Thread.currentThread().getName() + " 要退出了" ); throw new InterruptedException(); } } }catch (InterruptedException e){ e.printStackTrace(); } } }
测试代码如下:
public class ServiceTest { @Test public void run(){ try { Service service = new Service(); ThreadA threadA = new ThreadA(service); threadA.setName("A"); threadA.start(); ThreadB threadB = new ThreadB(service); threadB.setName("B"); threadB.start(); Thread.sleep(10000); }catch (InterruptedException e) { e.printStackTrace(); } } }
线程B通过while 循环一直轮训“监控”对象的属性状态。
通过如此的处理有个很严重的问题:
我们会发现线程B的while循环一直去取 service 实例的属性来做业务逻辑的转移判断,如果 jvm 是 client 模式,那么会频繁的会从公共堆栈区域进行读取,其实这是严重的性能消耗。其实在 java 的体系中优秀的大佬们通过 wait/notify 机制来完美解决此问题。
需要记住的一点事,wait/notify 的调用一定是在同步代码块执行。
用一句话来总结一下wait和notify:wait使线程停止运行,而notify使停止的线程继续运行。
package com.kirago.cp03.demo04; import java.util.ArrayList; import java.util.List; public class MyList { private List list = new ArrayList(); public void add(){ list.add("item"); } public int size(){ return list.size(); } }
package com.kirago.cp03.demo04; public class ThreadA extends Thread{ private MyList myList; public ThreadA(MyList myList){ super(); this.myList = myList; } @Override public void run(){ try { synchronized (myList){ if(myList.size() != 5){ System.out.println(" wait begin " + System.currentTimeMillis()); myList.wait(); System.out.println(" wait end " + System.currentTimeMillis()); } } }catch (InterruptedException e){ e.printStackTrace(); } } }
package com.kirago.cp03.demo04; public class ThreadB extends Thread{ private MyList myList; public ThreadB(MyList myList){ super(); this.myList = myList; } @Override public void run(){ try { synchronized (myList){ for(int i=0;i<10;i++){ myList.add(); if(myList.size() == 5){ myList.notify(); System.out.println(" 已经发出通知!"); } System.out.println("添加了 " + myList.size() + " 个元素"); Thread.sleep(1000); } } }catch (InterruptedException e){ e.printStackTrace(); } } }
测试代码:
package com.kirago.cp03.demo04; import org.junit.Test; public class MyListTest { @Test public void run(){ try { MyList myList = new MyList(); ThreadA threadA = new ThreadA(myList); threadA.start(); ThreadB threadB = new ThreadB(myList); threadB.start(); Thread.sleep(14000); }catch (InterruptedException e){ e.printStackTrace(); } } }
关键字synchronized可以将任何一个Object对象作为同步对象来看待,而Java为每个Object都实现了wait()和notify()方法,它们必须用在被synchronized同步的Object的临界区内。通过调用wait()方法可以使处于临界区内的线程进入等待状态,同时释放被同步对象的锁。而notify操作可以唤醒一个因调用了wait操作而处于阻塞状态中的线程,使其进入就绪状态。被重新换醒的线程会试图重新获得临界区的控制权,也就是锁,并继续执行临界区内wait之后的代码。如果发出notify操作时没有处于阻塞状态中的线程,那么该命令会被忽略。wait()方法可以使调用该方法的线程释放共享资源的锁,然后从运行状态退出,进入等待队列,直到被再次唤醒。
原文:https://www.cnblogs.com/kirago/p/13438748.html