在并发场景下限制共享资源的访问,使其只有一个线程可以执行某个方法或代码块,实现线程安全。
Thread1:lock A,waiting for B。
Thread2:lock B,waiting for A。
public class DeadLock { /** * A锁 */ private static String A = "A"; /** * B锁 */ private static String B = "B"; public static void main(String[] args) { new DeadLock().deadLock(); } public void deadLock() { // 先获取A锁再获取B锁 Thread t1 = new Thread(() -> { synchronized (A) { try { // 获取A锁后休眠2s Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (B) { // 获取B锁 System.out.println("thread1..."); } } }); // 先获取B锁再获取A锁 Thread t2 = new Thread(() -> { synchronized (B) { try { // 获取B锁后休眠2s Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (A) { System.out.println("thread2..."); } } }); t1.start(); t2.start(); } }
1、原子性:线程互斥的访问同步代码。
2、可见性:保证共享变量的修改能及时可见。
3、有序性:有效解决重排序问题,即一个unlock操作先行发生与lock操作。
synchronize可以把任何非null的对象作为锁,在Host JVM中锁叫做对象监视器。
当一个线程访问同步代码块的时候,首先需要获取锁才能执行,当退出或抛出异常时需要释放锁。
那么它是如何来实现这一机制的呢,来看看如下代码:
public class SynchronizedDemo { public void method() { synchronized (this) { System.out.println("Method 1 start"); } } }
monitorenter:监视器锁(monitor),当monitor被占用时便是锁定状态,执行monitorenter会尝试获取monitor的所有权。过程如下:
monitorexit:执行monitorexit的必须是monitor的拥有者。
———————————————————————————————————————————————————————
通过上述的示例便可以很清楚的了解到synchronize是如何实现的了,通过monitor对象来实现的。
其实wait、notify方法也依赖于monitor对象,这也就是为什么wait、notify方法必须要在synchronize中才能调用的原因了。
原文:https://www.cnblogs.com/bzfsdr/p/12499742.html