多线程
synchronized、lock和Atomic的区别:
synchronized:
synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定;
在资源竞争不是很激烈的情况下,偶尔会有同步的情形下,synchronized是很合适的。原因在于,编译程序通常会尽可能的进行优化synchronize;
ReentrantLock:
lock是通过代码实现的,要保证锁定一定会被释放,就必须将unLock()放到finally{}中;
ReentrantLock提供了多样化的同步,它是有时间限制的同步,是可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等;
在资源竞争不激烈的情形下,性能稍微比synchronized差点。但是当同步非常激烈的时候,synchronized的性能一下子能下降好几十倍。而ReentrantLock却还能维持常态;
Atomic:
和上面的类似,不激烈情况下,性能比synchronized略逊,而激烈的时候,也能维持常态。激烈的时候,Atomic的性能会优于ReentrantLock一倍左右。
但是其有一个缺点,就是只能同步一个值,一段代码中只能出现一个Atomic的变量,多于一个同步无效。因为他不能在多个Atomic之间同步。
关于ReentrantLock:
公平性
ReentrantLock的构造器提供了两种公平性选择:创建非公平的锁(默认)或者一个公平锁。
在利用ReentrantLock构造函数的时候,可以传递一个boolean,当不传入模式是false,即创建一个非公平锁,当传入true的时候,创建一个公平的锁。
所谓的公平的锁,线程按照它们发出请求的顺序来获得锁。但在非公平的锁上,允许刚请求的锁直接马上获取锁。一般非公平锁的性能要高于公平锁的性能,这是因为恢复一个被挂起的线程以及真正开始运行需要一点时间,如果在此刻能将这段时间让立刻要获取锁的线程,则就会提高吞吐量。
在公平锁上,线程将按照它们发出请求的顺序来获得锁,但在非公平的锁上,则允许“插队”:当一个线程请求非公平锁时,如果在发出请求的同时该锁的状态变为可用,那么这个线程将跳过队列中所有的等待线程并获得这个锁。
其实非公平的ReentrantLock并不提倡“插队”。在公平锁中,如果有另一个线程持有这个锁或者有其他线程在队列中等待这个锁,那么新发出的请求的线程将被放入队列中。非公平的锁中,只有当锁被某个线程持有时,新发出的请求的线程才会被放入队列中。
多数情况下,非公平锁的性能要高于公平锁。原因就在于,在激烈竞争的情况下,在恢复一个被挂起的线程与该线程真正开始运行之间存在着严重的延迟。
当持有锁的时间相对较长,或者请求锁的平均时间间隔较长,那么应该使用公平锁。
ReentrantLock的几个关键方法说明:
void lock(); //尝试获取锁,若得不到着等待(不可中断,类似于synchronized方式)
void lockInterruptibly() ; //可中断的尝试获取锁
boolean tryLock();尝试获取锁,不管得到与否立即返回
boolean tryLock(long time, TimeUnit unit)尝试获取锁,若得不到等到一段时间
void unlock();// 释放锁
Condition newCondition();//创建于该锁相关的条件变量,实现精确等待/唤醒
lock():阻塞的方法
如果该锁没有被任何线程所拥有,则获取锁,立即返回,锁计数器加1.
如果该锁已被当前线程所持有,则立即返回,锁计数器加1.
如果该锁已被其他线程所拥有,则禁用当前线程,使得其休眠阻塞。
tryLock(time):可阻塞可中断的方法
如果该锁在给定时间内没有被任何线程所持有,且当前线程没有被中断,则获取锁,立即返回true,锁计数器加1.
如果该锁已被当前线程所持有,则立即返回true,锁计数器加1.
如果该锁已被其他线程所拥有,则禁用当前线程,使其休眠阻塞,直到发生以下情况:
当前线程获取锁,当前线程被中断,时间到了
返回状态:成功获取锁或者已经持有,则返回true。时间到了还没有获取锁,则返回false
lockInterruptibly():可阻塞可中断的方法
这种锁的获取必须在当前线程没有被中断的时候才能尝试获取锁
如果该锁没有被任何线程所拥有,则获取锁,立即返回,锁计数器加1.
如果该锁已被当前线程所持有,则立即返回,锁计数器加1.
如果该锁已被其他线程所拥有,则禁用当前线程,使得其休眠阻塞,直到发生以下情况:
当前线程获取锁,当前线程被中断
tryLock():非阻塞的方法
如果该锁没有被任何线程保持,则获取锁,立即返回true,锁计数器加1.
如果该锁已被当前线程所持有,则立即返回true,锁计数器加1.
如果该锁已被其他线程所拥有,则立即返回false。
unlock():释放锁
如果当前线程是此锁所有者,则将保持计数减 1。如果保持计数现在为 0,则释放该锁
关于ReentrantReadWriteLock:
说到ReentrantReadWriteLock,首先要做的是与ReentrantLock划清界限。它和后者都是单独的实现,彼此之间没有继承或实现的关系。
特性:
(a).重入方面其内部的WriteLock可以获取ReadLock,但是反过来ReadLock想要获得WriteLock则永远都不要想。
(b).WriteLock可以降级为ReadLock,顺序是:先获得WriteLock再获得ReadLock,然后释放WriteLock,这时候线程将保持Readlock的持有。反过来ReadLock想要升级为WriteLock则不可能,为什么?参看(a)
(c).ReadLock可以被多个线程持有并且在作用时排斥任何的WriteLock,而WriteLock则是完全的互斥。这一特性最为重要,因为对于高读取频率而相对较低写入的数据结构,使用此类锁同步机制则可以提高并发量。
(d).不管是ReadLock还是WriteLock都支持Interrupt,语义与ReentrantLock一致。
(e).WriteLock支持Condition并且与ReentrantLock语义一致,而ReadLock则不能使用Condition,否则抛出UnsupportedOperationException异常。