synchronized在我们的程序中非常的常见,主要是为了解决多个线程抢占同一个资源。那么我们知道synchronized有多种用法,以下从实践出发,题目由简入深,看你能答对几道题目?
调用代码如下
public static void main(String[] args) throws Exception { ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1024)); SyncTest st = new SyncTest(); // 注意是不同方法 executor.submit(() -> st.sync1_1()); executor.submit(() -> st.sync1_2()); executor.shutdown(); }
锁lock全局对象,会输出什么?
public static final Object LOCK = new Object(); public void sync1_1() { // 锁住lock对象 synchronized (LOCK) { sleep("sync1_1"); } } public void sync1_2() { // 锁住lock对象 synchronized (LOCK) { sleep("sync1_2"); } } public static void sleep(String name) { try { log.info(name + " get lock"); TimeUnit.SECONDS.sleep(1); log.info(name + " release lock"); } catch (Exception e) {} }
[INFO 2023-04-14 15:12:46.639] [pool-2-thread-1] [] - [SyncTest.java.sleep:86] [sync1_1 get lock] [INFO 2023-04-14 15:12:47.652] [pool-2-thread-1] [] - [SyncTest.java.sleep:88] [sync1_1 release lock] [INFO 2023-04-14 15:12:47.652] [pool-2-thread-2] [] - [SyncTest.java.sleep:86] [sync1_2 get lock] [INFO 2023-04-14 15:12:48.652] [pool-2-thread-2] [] - [SyncTest.java.sleep:88] [sync1_2 release lock]
等待线程A执行完成后,线程B才能执行,否则阻塞。符合我们预期。锁的资源就是我们所谓的LOCK对象
锁this对象,会输出什么?this是代表什么?
public void sync2_1() { synchronized (this) { sleep("sync2_1"); } } public void sync2_2() { synchronized (this) { sleep("sync2_2"); } }
[INFO 2023-04-14 15:12:46.639] [pool-2-thread-1] [] - [SyncTest.java.sleep:86] [sync2_1 get lock] [INFO 2023-04-14 15:12:47.652] [pool-2-thread-1] [] - [SyncTest.java.sleep:88] [sync2_1 release lock] [INFO 2023-04-14 15:12:47.652] [pool-2-thread-2] [] - [SyncTest.java.sleep:86] [sync2_2 get lock] [INFO 2023-04-14 15:12:48.652] [pool-2-thread-2] [] - [SyncTest.java.sleep:88] [sync2_2 release lock]
等待线程A执行完成后,线程B才能执行,否则阻塞。符合我们预期。锁的是调用方,SyncTest st = new SyncTest(); 中的st对象。 st是SyncTest类的一个对象。也就是锁的这个this资源
锁方法,会输出什么? 锁的又是什么资源?
public synchronized void sync3_1() { sleep("sync3_1"); } public synchronized void sync3_2() { sleep("sync3_2"); }
锁static方法,会输出什么? 锁的又是什么资源?
public static synchronized void sync4_1() { sleep("sync4_1"); } public static synchronized void sync4_2() { sleep("sync4_2"); }
[INFO 2023-04-14 15:12:46.639] [pool-2-thread-1] [] - [SyncTest.java.sleep:86] [sync4_1 get lock] [INFO 2023-04-14 15:12:47.652] [pool-2-thread-1] [] - [SyncTest.java.sleep:88] [sync4_1 release lock] [INFO 2023-04-14 15:12:47.652] [pool-2-thread-2] [] - [SyncTest.java.sleep:86] [sync4_2 get lock] [INFO 2023-04-14 15:12:48.652] [pool-2-thread-2] [] - [SyncTest.java.sleep:88] [sync4_2 release lock]
等待线程A执行完成后,线程B才能执行,否则阻塞。符合我们预期。因为是在static上面加锁,而static方法即是类方法,因此他锁的是这个类,也就是对SyncTest这个this class加的锁
锁类的class,会输出什么? 锁的又是什么资源?
public void sync5_1() { synchronized (SyncTest.class) { sleep("sync5_1"); } } public void sync5_2() { synchronized (SyncTest.class) { sleep("sync5_2"); } }
其他全部代码不改变,仅修改调用方式。具体代码如下
public static void main(String[] args) throws Exception { ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1024)); // 这个是new了一个st1 SyncTest st1 = new SyncTest(); executor.submit(() -> st1.sync1_1()); // 这里new了一个st2 SyncTest st2 = new SyncTest(); executor.submit(() -> st2.sync1_2()); executor.shutdown(); }
其他全部不变,问题从2.1 - 2.5重新全部调用一遍。结果又是否相同。
注意一下:调用方为 new 了两个st1以及st2, 如果你完全理解上述问题,这个问题就非常简单了。我们以问题1以及问题2为例:
[INFO 2023-04-14 15:12:46.639] [pool-2-thread-1] [] - [SyncTest.java.sleep:86] [sync1_1 get lock] [INFO 2023-04-14 15:12:47.652] [pool-2-thread-1] [] - [SyncTest.java.sleep:88] [sync1_1 release lock] [INFO 2023-04-14 15:12:47.652] [pool-2-thread-2] [] - [SyncTest.java.sleep:86] [sync1_2 get lock] [INFO 2023-04-14 15:12:48.652] [pool-2-thread-2] [] - [SyncTest.java.sleep:88] [sync1_2 release lock]
这是由于无论new几个st,锁的永远是唯一资源lock,因此结论不变
[INFO 2023-04-14 15:26:31.676] [pool-2-thread-2] [] - [SyncTest.java.sleep:86] [sync2_1 get lock] [INFO 2023-04-14 15:26:31.676] [pool-2-thread-1] [] - [SyncTest.java.sleep:86] [sync2_1 get lock] [INFO 2023-04-14 15:26:32.688] [pool-2-thread-1] [] - [SyncTest.java.sleep:88] [sync2_1 release lock] [INFO 2023-04-14 15:26:32.688] [pool-2-thread-2] [] - [SyncTest.java.sleep:88] [sync2_1 release lock]
这个结果就非常有意思了。注意看,线程B并没有阻塞,而是直接获取到了这个资源。也就是synchronized失效了。我们来分析一下为什么synchronized失效了?
我们知道在问题2中,synchronized锁的是this对象,而这个this对象分别为st1, 以及st2。那么是不是就是两个资源了。如果是两个资源,就不存在互斥的作用了,也就是不会相互争夺资源。
请各位读者自己分析问题2.3 - 2.5的第二种代码调用方式的结果。
synchronized是我们工程中常用的一个方法。但对于其用法,如果深究,还是有非常多意外的惊喜。如果小伙伴有其他问题,随时欢迎交流讨论