Synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法。Synchronized的作用主要有三个: 从语法上讲,Synchronized总共有三种用法: synchronized基于操作系统的管程(monitor)实现,对线程的阻塞和唤醒使用的是 AQS是一种提供了原子式管理同步状态、阻塞和唤醒线程功能以及CLH队列模型的简单框架。Java中的大部分同步类(Lock、Semaphore、ReentrantLock等)都是基于AbstractQueuedSynchronizer(简称为AQS)实现的,AQS同时提供了互斥模式(exclusive)和共享模式(shared)两种不同的同步逻辑。基于AQS同步器实现参考: AQS是纯基于 JDK 实现,对线程的阻塞和唤醒使用的是Condition的 以AQS的经典实现 阻塞是指线程跳出当前执行任务,由于某种原因暂时无法继续执行任务逻辑。而自旋和挂起可以使线程处于阻塞状态,所以自旋锁与挂起是阻塞的子集关系。 挂起和唤醒一个线程将会带来上下文切换的开销,而如果在一个低并发的环境下,线程频繁的挂起和唤醒将消耗大量的系统资源,与CUP密集型程序发生大量的上下文切换,从而增加调度开销降低吞吐量。因此在低并发的环境下(线程等待时间较短)通过自旋等待将可以完全避免上下文切换带来的系统开销。 自旋等待会使阻塞线程与其他线程竞争CUP的时间片,占有cup资源,尽管这种开销是很小的,但是当大量的线程长时间的去竞争CUP的时间片将给操作系统带来毁灭性的灾难。有些事情如果我们不把它推向极端,我们是不会知道有多荒谬。同样的我们只有在一个极端条件下,才能发现多线程长时间自旋等待的危害。 自旋等待与挂起两种方式的效率高低取决于上下文切换的开销以及成功获取锁之前需要等待的时间,如果等待时间短则适合使用自旋锁,如果等待时间长则适合使用挂起操作。---《Java并发编程实践》 Doug Lea在设计AQS的线程阻塞策略使用了自旋等待和挂起两种方式,通过挂起线程前的低频自旋保证了AQS阻塞线程上下文切换开销及CUP时间片占用的最优化选择。保证在等待时间短通过自旋去占有锁而不需要挂起,而在等待时间长时将线程挂起。实现锁性能的最大化。synchronized
Object的wait()和notify()AbstractQueuedSynchronizer (AQS)
await()、signal()方法区别和联系
ReentrantLock为例,分析其和synchronized 的区别,相比于synchronized,ReentrantLock需要显式的获取锁和释放锁,相对现在基本都是用JDK7和JDK8的版本,从Synchronized引入了偏向锁,轻量级锁(自旋锁)后,ReentrantLock的效率和synchronized区别基本可以持平了,在两种方法都可用的情况下,官方甚至建议使用synchronized, 其实synchronized的优化我感觉就借鉴了ReenTrantLock中的CAS技术。
ReentrantLock等待可中断,当持有锁的线程长时间不释放锁的时候,等待中的线程可以选择放弃等待,转而处理其他的任务。阻塞与自旋锁、挂起
参考