import java.util.HashMap; import java.util.concurrent.TimeUnit; public class Test { public static void main(String[] args){ // TickWindowRunnable.test(); // Mutex.test(); // TaskExample.test(); // ThisMonitor.test(); DeadLock.test(); } } /* 4.2 初识synchronized关键字 synchronized关键字可以实现一个简单的策略来防止线程干扰和内存一致性错误,如果一个对象 对多个线程是可见的,那么对该对想的所有读或者写都将通过同步的方式来进行,具体体现如下: 1.synchronized关键字提供了一种锁的机制,能够确保共享变量的互斥访问,从而防止 数据不一致的问题出现。 2.synchronized关键字包括monitor enter和monitor exit两个jvm指令,它能够保证 在任何时候任何线程执行到monitor enter成功之前都必须从主内存中获取数据,而不是 从缓存中,在monitor exit运行成功之后,共享内存被更新后的值必须刷入主内存 3.synchronized的指令严格遵守java happens-before规则,一个monitor exit指令之 前必定要有一个monitor enter */ /* 4.2.2 synchronized关键字的用法 1.同步方法: 2.同步代码块: private final Object MUTEX = new Object(); public void sync(){ synchronized(MUTEX){ //Do Something... } } */ class TickWindowRunnable implements Runnable{ private int index = 1; private final static int MAX = 500; private final static Object MUTEX = new Object(); @Override public void run() { synchronized (MUTEX){ while (index<=MAX){ System.out.println(Thread.currentThread()+"的号码是:"+(index++)); } } } public static void test() { final TickWindowRunnable task = new TickWindowRunnable(); Thread windowThread1 = new Thread(task,"fir"); Thread windowThread2 = new Thread(task,"sec"); Thread windowThread3 = new Thread(task,"thi"); Thread windowThread4 = new Thread(task,"for"); Thread windowThread5 = new Thread(task,"fif"); windowThread1.start(); windowThread2.start(); windowThread3.start(); windowThread4.start(); windowThread5.start(); } } /* 4.3.1 线程堆栈分析 线程获取了与mutex关联的monitor锁 ——额,一个monitor锁与一个mutex关联着,我这个线程现在进入了你的方法 所以我获取了关联着你这个mutex对象的monitor锁锁锁!!! */ class Mutex{ //注意下这个问题,我发现这个对象都是静态的 private final static Object MUTEX = new Object(); public void accessResource(){ synchronized (MUTEX){ try{ TimeUnit.MINUTES.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void test(){ final Mutex mutex = new Mutex(); for (int i = 0; i < 5; i++) { new Thread(mutex::accessResource).start(); } } } /* ============================================================================================================================== 对 jstack 打印的日志进行分析: ============================================================================================================================== "Thread-2" #13 prio=5 os_prio=0 tid=0x000000005696a000 nid=0xe54 waiting for monitor entry [0x000000005812e000] java.lang.Thread.State: BLOCKED (on object monitor) at Mutex.accessResource(Test.java:82) - waiting to lock <0x00000000ec195368> (a java.lang.Object) //这个地方说明这个线程在等待锁的释放 at Mutex$$Lambda$1/1096979270.run(Unknown Source) at java.lang.Thread.run(Thread.java:748) "Thread-1" #12 prio=5 os_prio=0 tid=0x0000000056969000 nid=0xc38 waiting on condition [0x000000005825e000] java.lang.Thread.State: TIMED_WAITING (sleeping) at java.lang.Thread.sleep(Native Method) at java.lang.Thread.sleep(Thread.java:340) at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386) at Mutex.accessResource(Test.java:82) - locked <0x00000000ec195368> (a java.lang.Object) //这个地方说明这个线程已经获得锁了 at Mutex$$Lambda$1/1096979270.run(Unknown Source) at java.lang.Thread.run(Thread.java:748) ============================================================================================================================== */ /* 4.3.2 JVM指令分析 ============================================================================================================================== JVM指令分析 ============================================================================================================================== public void accessResource(); Code: 0: getstatic #2 //获取 MUTEX对象 3: dup 4: astore_1 5: monitorenter //执行monitor enter JVM指令 6: getstatic #3 9: ldc2_w #4 12: invokevirtual #6 15: goto 23 //跳转到23行 18: astore_2 19: aload_2 20: invokevirtual #8 23: aload_1 // 24: monitorexit //执行monitor exit JVM指令 25: goto 33 28: astore_3 29: aload_1 30: monitorexit 31: aload_3 32: athrow 33: return ============================================================================================================================== */ /* Monitorenter JVM指令 每个对象都与一个monitor相关联,一个monitor的lock锁只能被一个线程在同一时间获得, 在一个线程尝试获得与对象关联monitor的所有权会发生如下几件事情: 1.如果monitor的计数器为0,则意味这该monitor的lock还没有被获得,某个线程获得 之后将立即对计数器加一,从此该线程就是这个monitor的所有者了。 2.如果一个已经拥有该monitor所有权的线程重入,会导致monitor计数器再次累加。 3.如果monitor已经被其他线程所拥有,则其他线程尝试获取该monitor的所有权时, 或被陷入阻塞状态,知道monitor计数器变为0,才能再次尝试获取对monitor 的所有权。 Monitorexit JVM指令 当monitor计数器变为0时,被该monitor阻塞的线程将再次尝试获得对该monitor的所有权。 */ /* 4.3.3 使用synchronized需要注意的问题 1.与monitor关联的对象不能为空 2.synchronized作用域太大了 3.不同的monitor企图锁相同的方法 */ class Task implements Runnable{ private final Object MUTEX = new Object(); @Override public void run() { synchronized (MUTEX){ } } } class TaskExample{ public static void test(){ /* 案例解析: 这个案例中构造了5个Runnable实例,Runnable作为线程逻辑执行单元传递给 Thread~~~线程之间进行monitor lock的争夺只能发生在与monitor关联的 同一个引用上!!!所以这个地方,需要new 出一个Task对象,或者将MUTEX 对象编程静态的,但是那没有意义咯,因为你保护的资源大家都有自己的一份, 哈哈哈哈哈哈哈 */ for (int i = 0; i < 5; i++) { new Thread(Task::new).start(); } } } /* 4.多个锁的交叉导致死锁 案例分析: 如果write方法和read方法,同时在两个线程中调用,而调用了read方法的线程获 得了MUTEX_READ对象上的monitor锁,调用write方法的线程获得了MUTEX_WRITE 对象上的monitor锁,就会发生死锁。 */ class Mutex2{ private final Object MUTEX_READ = new Object(); private final Object MUTEX_WRITE = new Object(); public void read(){ synchronized (MUTEX_READ){ synchronized (MUTEX_WRITE){ } } } public void write() { synchronized (MUTEX_WRITE){ synchronized (MUTEX_READ){ } } } } /* 4.4.1 this monitor */ class ThisMonitor{ public synchronized void Method1(){ System.out.println(Thread.currentThread().getName()+" enter to method1"); try{ TimeUnit.MINUTES.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } public synchronized void Method2(){ System.out.println(Thread.currentThread().getName()+" enter to method2"); try{ TimeUnit.MINUTES.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } public static void test(){ ThisMonitor thisMonitor = new ThisMonitor(); new Thread(thisMonitor::Method1,"T1").start(); new Thread(thisMonitor::Method2,"T2").start(); } } /* ============================================================================================================== jstack 打印的日志分析 ============================================================================================================== "T2" #12 prio=5 os_prio=0 tid=0x0000000057971800 nid=0xb50 waiting for monitor entry [0x000000005837f000] java.lang.Thread.State: BLOCKED (on object monitor) at ThisMonitor.Method2(Test.java:251) - waiting to lock <0x00000000eb3c02b8> (a ThisMonitor) at ThisMonitor$$Lambda$2/1831932724.run(Unknown Source) at java.lang.Thread.run(Thread.java:748) "T1" #11 prio=5 os_prio=0 tid=0x000000005796e800 nid=0x16d8 waiting on condition [0x000000005824e000] java.lang.Thread.State: TIMED_WAITING (sleeping) at java.lang.Thread.sleep(Native Method) at java.lang.Thread.sleep(Thread.java:340) at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386) at ThisMonitor.Method1(Test.java:245) - locked <0x00000000eb3c02b8> (a ThisMonitor) at ThisMonitor$$Lambda$1/1096979270.run(Unknown Source) at java.lang.Thread.run(Thread.java:748) ============================================================================================================== 自己的分析: 1.我看到了T1进行了加锁,T2在等待锁 2.额,这个类似与什么情况类似?这个类似与我准备了一个MUTEX对象,用它来保护两个语句块 按照我之前看到的,一点需要进入到这个对象保护的区域了,它都需要需询问一下monitor 能否给个锁。但是发现不能狗获得这个锁。所以就只有一个方法运行了。 */ /* 4.5.1 程序死锁 1.交叉锁可导致程序出现死锁 2.内存不足导致的死锁 3.一问一答的数据交换 4.数据库锁 5.文件锁 6.死循环引起的死锁 4.5.2 程序死锁举例 */ class DeadLock{ private final Object MUTEX_READ = new Object(); private final Object MUTEX_WRITE = new Object(); public void read(){ synchronized (MUTEX_READ){ synchronized (MUTEX_WRITE){ System.out.println("read..."); } } } public void write() { synchronized (MUTEX_WRITE){ synchronized (MUTEX_READ){ System.out.println("write..."); } } } public static void test() { final DeadLock deadLock = new DeadLock(); new Thread(()->{ while(true){ deadLock.read(); } },"READ-THEAD").start(); new Thread(()->{ while(true){ deadLock.write(); } },"Write-THEAD").start(); } } /* HashMap 不具备线程安全的能力,如果想要使用线程安全的map结构请使用 ConcurrentHashMap或者使用Collection.synchronizeMap来代替 ——这个还是了解一下吧。。。 案例分析: 书中是这么说的:如果多线程同时写操作的情况下,很容易出现死循环引起的死锁, 程序运行一段时间后,CPU等资源居高不下。 ——我想知道为什么会这样。。。 */ class HashMapDeadLock{ private final HashMap<String,String> map = new HashMap<>(); public void add(String key, String value) { this.map.put(key,value); } public static void test(){ final HashMapDeadLock hmdl = new HashMapDeadLock(); for (int x = 0; x < 2; x++) { new Thread(()->{ for (int i = 1; i < Integer.MAX_VALUE; i++) { hmdl.add(String.valueOf(i),String.valueOf(i)); } }).start(); } } } /* 4.5.3 死锁诊断 ——这部分没看着,想找本专业的书,看看这部分 */
——《Java高并发编程详解》笔记