首先,请大家看如下代码:
public class ConcurrentTest { // 创建map,可用其他任何map,ConcurrentHashMap加锁分段map,更具说服力 static ConcurrentHashMap map = new ConcurrentHashMap(); public static void main(String[] args) { for (int i = 0; i < 10; i++) { map.put("abc", 123); Thread thread = new Thread(new Workers(), "线程1"); thread.start(); System.out.println(Thread.currentThread().getName() + "----" + (map.get("abc") == 123 ? "获取线程失败--值:" + map.get("abc") : "获取线程成功--值:" + map.get("abc"))); map.clear(); } } static class Workers implements Runnable { public void run () { map.put("abc", 12); } } }
打印的结果会是什么呢?(打印结果可能有所不同,与JVM和CPU的执行有关)
大家应该都知道结果是什么,有时候会获取更新为12,但大多数都会获取更新失败为123,原因是主线程在线程处理当中可能会先于子线程执行,可以怎么能总让主线程更新到子线程的值呢?
锁,是我们最能先想到的解决方案。
public class ConcurrentTest { static ConcurrentHashMap map = new ConcurrentHashMap(); public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 10; i++) { // 线程state状态空闲,主线程获取到锁 synchronized (ConcurrentTest.class) { map.put("abc", 123); Thread thread = new Thread(new Workers(), "线程1"); thread.start(); // 启动线程,锁等待,并释放锁,等待子线程锁的通知 ConcurrentTest.class.wait(); System.out.println(Thread.currentThread().getName() + "----" + (map.get("abc") == 123 ? "获取线程失败--值:" + map.get("abc") : "获取线程成功--值:" + map.get("abc"))); map.clear(); } } } static class Workers implements Runnable { public void run () { // 通过主线程释放锁,锁空闲,子线程得到锁 synchronized (ConcurrentTest.class) { map.put("abc", 12); // 执行完put方法加入map,通知所有正在等待的锁执行,.notify()通知这个锁执行同样,notifyAll()可以提高效率 ConcurrentTest.class.notifyAll(); } } } }
执行.结果
至此,主线程能确保打印出来的是子线程更新够的值、
浅析:
synchronized 锁是一个重量级锁,他可以锁住一个对象,他可以具体表现为三种形式(取自《Java并发编程的艺术》P12)
1)普通同步方法,锁是当前的实例对象
2)静态同步方法,锁是当前类的class对象
3)同步方法块,锁是synchronized括号里面配置的对象
我们这里用到的锁住当前类的class对象,给对象加锁,当前Thread中的state为空闲状态(state = 0),所以主线程获取到了当前锁,当启动子线程的时候,调用对象Object类的wait(),等待方法,让当前锁等待,锁等待的时候会释放锁,其他阻塞线程会获取到当前的锁,所以子线程中的synchronized中获取到了锁,执行,当执行的notity()方法的时候,会通知因wait而阻塞的线程,而此时map的数据已经更新完毕,在wait执行之后释放锁,所以,主线程一点会更新到最新的值。
这是基础的实现方式,有好的或者更优化的方式还请交流(我还是个小白。。。)