java锁机制
来源:互联网 发布:平板推荐知乎 编辑:程序博客网 时间:2024/06/05 19:02
Synchronized Lock
Java在JDK1.5之前都是靠synchronized关键字保证同步的,这种通过使用一致的锁定协议来协调对共享状态的访问,可以确保无论哪个线程持有守护变量的锁,都采用独占的方式来访问这些变量,如果出现多个线程同时访问锁,那第一些线线程将被挂起,当线程恢复执行时,必须等待其它线程执行完他们的时间片以后才能被调度执行,在挂起和恢复执行过程中存在着很大的开销。锁还存在着其它一些缺点,当一个线程正在等待锁时,它不能做任何事。如果一个线程在持有锁的情况下被延迟执行,那么所有需要这个锁的线程都无法执行下去。如果被阻塞的线程优先级高,而持有锁的线程优先级低,将会导致优先级反转(Priority Inversion)。
volatile的优势
与锁相比,volatile变量是一和更轻量级的同步机制,因为在使用这些变量时不会发生上下文切换和线程调度等操作,但是volatile变量也存在一些局限:不能用于构建原子的复合操作,因此当一个变量依赖旧值时就不能使用volatile变量
悲观锁和乐观锁
独占锁是一种悲观锁,synchronized就是一种独占锁,它假设最坏的情况,并且只有在确保其它线程不会造成干扰的情况下执行,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。而另一个更加有效的锁就是乐观锁。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。
CAS操作
Compare and Swap,比较并操作,CPU指令,在大多数处理器架构,包括IA32、Space中采用的都是CAS指令,CAS的语义是“我认为V的值应该为A,如果是,那么将V的值更新为B,否则不修改并告诉V的值实际为多少”,CAS是项乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
JVM对CAS的支持
在JDK1.5之前,如果不编写明确的代码就无法执行CAS操作,在JDK1.5中引入了底层的支持,在int、long和对象的引用等类型上都公开了CAS的操作,并且JVM把它们编译为底层硬件提供的最有效的方法,在运行CAS的平台上,运行时把它们编译为相应的机器指令,如果处理器不支持CAS指令,那么JVM将使用自旋锁。在原子类变量中,如java.util.concurrent.atomic中的AtomicXXX,都使用了这些底层的JVM支持为数字类型的引用类型提供一种高效的CAS操作,而在java.util.concurrent中的大多数类在实现时都直接或间接的使用了这些原子变量类。
下面代码说明了CAS的语义(并不是真正的实现,CAS的实现是调用native方法):
Java代码
1./**
2. * Huisou.com Inc.
3. * Copyright (c) 2011-2012 All Rights Reserved.
4. */
5.
6.package thread;
7.
8.import org.apache.http.annotation.GuardedBy;
9.import org.apache.http.annotation.ThreadSafe;
10.
11./**
12. * @description
13. *
14. * @author chenzehe
15. * @email hljuczh@163.com
16. * @create 2013-1-6 上午09:02:47
17. */
18.@ThreadSafe
19.public class SimulatedCAS {
20. @GuardedBy("this")
21. private int value;
22.
23. public synchronized int get() {
24. return value;
25. }
26.
27. public synchronized int comparedAndSwap(int expectedValue, int newValue) {
28. int oldValue = value;
29. if (oldValue == expectedValue) {
30. value = newValue;
31. }
32. return oldValue;
33. }
34.
35. public synchronized boolean compareAndSet(int expectedValue, int newValue) {
36. return (expectedValue == comparedAndSwap(expectedValue, newValue));
37. }
38.}
下面代码演示了非阻塞计数器:
Java代码
1./**
2. * Huisou.com Inc.
3. * Copyright (c) 2011-2012 All Rights Reserved.
4. */
5.
6.package thread;
7.
8./**
9. * @description
10. *
11. * @author chenzehe
12. * @email hljuczh@163.com
13. * @create 2013-1-6 上午09:48:52
14. */
15.
16.public class CasCounnter {
17. private SimulatedCAS value;
18.
19. public int getValue() {
20. return value.get();
21. }
22.
23. public int increment() {
24. int v;
25. do {
26. v = value.get();
27. } while (v != value.comparedAndSwap(v, v + 1));
28. return v + 1;
29. }
30.}
AtomicInteger中实现自增的代码为:
Java代码
1.public final int getAndIncrement() {
2. for (;;) {
3. int current = get();
4. int next = current + 1;
5. if (compareAndSet(current, next))
6. return current;
7. }
8.}
9.
10.public final boolean compareAndSet(int expect, int update) {
11. return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
12.}
表面上看起来,基于CAS的计数器似乎比基于锁的计数器在性能上更差一些,因为它还要执行更多的操作和更复杂的控制流,并且还依赖看似复杂的CAS操作,实际上,当竞争程度不高时,基于CAS的计数器在性能上远远超过了基于锁的计数器,而在没有竞争时甚至更高,如果要快速获取无竞争的锁,那么至少需要一次CAS操作加上与其它锁相关的操作,因此基于锁的计数即使在最好的情况下也会比基于CAS的计数器在一般情况下执行更多的操作。由于CAS在大多数情况下都能执行成功,因此硬件能够正确的预测while循环中的分支,从而把控制逻辑的开锁降到最低。
锁与原子变量的性能比较
在高度竞争的情况下,锁的性能将超过原子变量的性能,但在更真实的竞争情况下,原子变量的性能将超过锁的性能,这是因为锁在发竞争时会挂起线程,从而降低了CPU的使用率和共享内存总线上的同步通信量,另一方面,如果使用原子变量,那么发出调用的类负责对竞争进行管理,在遇到竞争时立即重试,这通常是种正确的做法,但是在竞争激烈环境下会导致更多的竞争。在实际的情况中,任何一个程序都不会除了竞争锁或原子变量而什么事都不做,不会达到很高的竞争,所以在更常见的情况下,原子变量的效率会更高,在可伸缩性上要高于锁。
非阻塞算法
如果在某个算法中,一个线程的失败或挂起不会引起其它线程也失败或挂起,那么这个算法就被称为非阻塞算法;如果在算法的每个步骤中都存在每个线程能够执行下去,那么这种算法称为无锁算法(Lock-Free)。如果在算法中仅将CAS用于协调线程之间的操作,并且能正确的实现,那么它即是一种非阻塞算法,也是一种无锁算法。在非阻塞算法中通常不会出现死锁和优先级反转问题,但可能出现饥饿和活锁问题,因为在算法中会反复的重试。
下面代码为非阻塞的栈(使用Treiber算法):
Java代码
1.package thread;
2.import java.util.concurrent.atomic.AtomicReference;
3.public class ConcurrentStack<T> {
4. private AtomicReference<Node<T>> stacks = new AtomicReference<Node<T>>();
5. public T push(T e) {
6. Node<T> oldNode, newNode;
7. for (;;) { // 这里的处理非常的特别,也是必须如此的。
8. oldNode = stacks.get();
9. newNode = new Node<T>(e, oldNode);
10. if (stacks.compareAndSet(oldNode, newNode)) {
11. return e;
12. }
13. }
14. }
15. public T pop() {
16. Node<T> oldNode, newNode;
17. do{
18. oldNode = stacks.get();
19. if(oldNode==null){
20. return null;
21. }
22. newNode = oldNode.next;
23. }while(!stacks.compareAndSet(oldNode, newNode));
24. return oldNode.object;
25. }
26. private static final class Node<T> {
27. private T object;
28. private Node<T> next;
29. private Node(T object, Node<T> next) {
30. this.object = object;
31. this.next = next;
32. }
33. }
34.}
ABA问题
如果在算法中的节点可以被循环使用,那么在使用CAS指令时就会出现这种问题,主要指在没有垃圾回收机制的环境中,在CAS操作中,在更新之前先判断V的值是否仍然A,如果是的话就继续执行更新操作,但是有的时候还需要知道“自从上次看到V的值为A以后,这个值是否发生了变化?”,在某些算法中,如果V的值先由A变成B,再由B变成A,那么仍然认为是发生了变化,并需要重新执行算法中的步骤。在这种情况下,即使链表的头节点仍然指向之前观察到的节点,但也不足以说明链表的内容没有变化。如果通过垃圾回收机制仍然无法避免ABA问题,那么还有下面简单方案:不是更新某个引用的值,而是更新两个值,包括一个引用和一个版本号,即使这个值由A变为B,然后为变为A,版本号也是不同的。AtomicStampedReference和AtomicMarkableReference支持在两个变量上执行原子的条件更新。AtomicStampedReference更新一个“对象-引用”二元组,通过在引用上加上“版本号”,从而避免ABA问题,AtomicMarkableReference将更新一个“对象引用-布尔值”的二元组。
Lock与synchronized 的区别
LockSynchronizedReentrantLock线程区别 .
多次思考过这个问题,都没有形成理论,今天有时间了,我把他总结出来,希望对大家有所帮助
1、ReentrantLock 拥有Synchronized相同的并发性和内存语义,此外还多了 锁投票,定时锁等候和中断锁等候
线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O的锁定,
如果使用 synchronized ,如果A不释放,B将一直等下去,不能被中断
如果 使用ReentrantLock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情
ReentrantLock获取锁定与三种方式:
a) lock(), 如果获取了锁立即返回,如果别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁
b) tryLock(), 如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false;
c)tryLock(long timeout,TimeUnit unit), 如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false;
d) lockInterruptibly:如果获取了锁定立即返回,如果没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断
2、synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定,但是使用Lock则不行,lock是通过代码实现的,要保证锁定一定会被释放,就必须将unLock()放到finally{}中
3、在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetrantLock,但是在资源竞争很激烈的情况下,Synchronized的性能会下降几十倍,但是ReetrantLock的性能能维持常态;
下面内容 是转载 http://zzhonghe.iteye.com/blog/826162
5.0的多线程任务包对于同步的性能方面有了很大的改进,在原有synchronized关键字的基础上,又增加了ReentrantLock,以及各种Atomic类。了解其性能的优劣程度,有助与我们在特定的情形下做出正确的选择。
总体的结论先摆出来:
synchronized:
在资源竞争不是很激烈的情况下,偶尔会有同步的情形下,synchronized是很合适的。原因在于,编译程序通常会尽可能的进行优化synchronize,另外可读性非常好,不管用没用过5.0多线程包的程序员都能理解。
ReentrantLock:
ReentrantLock提供了多样化的同步,比如有时间限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。在资源竞争不激烈的情形下,性能稍微比synchronized差点点。但是当同步非常激烈的时候,synchronized的性能一下子能下降好几十倍。而ReentrantLock确还能维持常态。
Atomic:
和上面的类似,不激烈情况下,性能比synchronized略逊,而激烈的时候,也能维持常态。激烈的时候,Atomic的性能会优于ReentrantLock一倍左右。但是其有一个缺点,就是只能同步一个值,一段代码中只能出现一个Atomic的变量,多于一个同步无效。因为他不能在多个Atomic之间同步。
所以,我们写同步的时候,优先考虑synchronized,如果有特殊需要,再进一步优化。ReentrantLock和Atomic如果用的不好,不仅不能提高性能,还可能带来灾难。
先贴测试结果:再贴代码(Atomic测试代码不准确,一个同步中只能有1个Actomic,这里用了2个,但是这里的测试只看速度)
==========================
round:100000 thread:5
Sync = 35301694
Lock = 56255753
Atom = 43467535
==========================
round:200000 thread:10
Sync = 110514604
Lock = 204235455
Atom = 170535361
==========================
round:300000 thread:15
Sync = 253123791
Lock = 448577123
Atom = 362797227
==========================
round:400000 thread:20
Sync = 16562148262
Lock = 846454786
Atom = 667947183
==========================
round:500000 thread:25
Sync = 26932301731
Lock = 1273354016
Atom = 982564544
代码如下:
Java代码
1.package test.thread;
2.
3.import static java.lang.System.out;
4.
5.import java.util.Random;
6.import java.util.concurrent.BrokenBarrierException;
7.import java.util.concurrent.CyclicBarrier;
8.import java.util.concurrent.ExecutorService;
9.import java.util.concurrent.Executors;
10.import java.util.concurrent.atomic.AtomicInteger;
11.import java.util.concurrent.atomic.AtomicLong;
12.import java.util.concurrent.locks.ReentrantLock;
13.
14.public class TestSyncMethods {
15.
16. public static void test(int round,int threadNum,CyclicBarrier cyclicBarrier){
17. new SyncTest("Sync",round,threadNum,cyclicBarrier).testTime();
18. new LockTest("Lock",round,threadNum,cyclicBarrier).testTime();
19. new AtomicTest("Atom",round,threadNum,cyclicBarrier).testTime();
20. }
21.
22. public static void main(String args[]){
23.
24. for(int i=0;i<5;i++){
25. int round=100000*(i+1);
26. int threadNum=5*(i+1);
27. CyclicBarrier cb=new CyclicBarrier(threadNum*2+1);
28. out.println("==========================");
29. out.println("round:"+round+" thread:"+threadNum);
30. test(round,threadNum,cb);
31.
32. }
33. }
34.}
35.
36.class SyncTest extends TestTemplate{
37. public SyncTest(String _id,int _round,int _threadNum,CyclicBarrier _cb){
38. super( _id, _round, _threadNum, _cb);
39. }
40. @Override
41. /**
42. * synchronized关键字不在方法签名里面,所以不涉及重载问题
43. */
44. synchronized long getValue() {
45. return super.countValue;
46. }
47. @Override
48. synchronized void sumValue() {
49. super.countValue+=preInit[index++%round];
50. }
51.}
52.
53.
54.class LockTest extends TestTemplate{
55. ReentrantLock lock=new ReentrantLock();
56. public LockTest(String _id,int _round,int _threadNum,CyclicBarrier _cb){
57. super( _id, _round, _threadNum, _cb);
58. }
59. /**
60. * synchronized关键字不在方法签名里面,所以不涉及重载问题
61. */
62. @Override
63. long getValue() {
64. try{
65. lock.lock();
66. return super.countValue;
67. }finally{
68. lock.unlock();
69. }
70. }
71. @Override
72. void sumValue() {
73. try{
74. lock.lock();
75. super.countValue+=preInit[index++%round];
76. }finally{
77. lock.unlock();
78. }
79. }
80.}
81.
82.
83.class AtomicTest extends TestTemplate{
84. public AtomicTest(String _id,int _round,int _threadNum,CyclicBarrier _cb){
85. super( _id, _round, _threadNum, _cb);
86. }
87. @Override
88. /**
89. * synchronized关键字不在方法签名里面,所以不涉及重载问题
90. */
91. long getValue() {
92. return super.countValueAtmoic.get();
93. }
94. @Override
95. void sumValue() {
96. super.countValueAtmoic.addAndGet(super.preInit[indexAtomic.get()%round]);
97. }
98.}
99.abstract class TestTemplate{
100. private String id;
101. protected int round;
102. private int threadNum;
103. protected long countValue;
104. protected AtomicLong countValueAtmoic=new AtomicLong(0);
105. protected int[] preInit;
106. protected int index;
107. protected AtomicInteger indexAtomic=new AtomicInteger(0);
108. Random r=new Random(47);
109. //任务栅栏,同批任务,先到达wait的任务挂起,一直等到全部任务到达制定的wait地点后,才能全部唤醒,继续执行
110. private CyclicBarrier cb;
111. public TestTemplate(String _id,int _round,int _threadNum,CyclicBarrier _cb){
112. this.id=_id;
113. this.round=_round;
114. this.threadNum=_threadNum;
115. cb=_cb;
116. preInit=new int[round];
117. for(int i=0;i<preInit.length;i++){
118. preInit[i]=r.nextInt(100);
119. }
120. }
121.
122. abstract void sumValue();
123. /*
124. * 对long的操作是非原子的,原子操作只针对32位
125. * long是64位,底层操作的时候分2个32位读写,因此不是线程安全
126. */
127. abstract long getValue();
128.
129. public void testTime(){
130. ExecutorService se=Executors.newCachedThreadPool();
131. long start=System.nanoTime();
132. //同时开启2*ThreadNum个数的读写线程
133. for(int i=0;i<threadNum;i++){
134. se.execute(new Runnable(){
135. public void run() {
136. for(int i=0;i<round;i++){
137. sumValue();
138. }
139.
140. //每个线程执行完同步方法后就等待
141. try {
142. cb.await();
143. } catch (InterruptedException e) {
144. // TODO Auto-generated catch block
145. e.printStackTrace();
146. } catch (BrokenBarrierException e) {
147. // TODO Auto-generated catch block
148. e.printStackTrace();
149. }
150.
151.
152. }
153. });
154. se.execute(new Runnable(){
155. public void run() {
156.
157. getValue();
158. try {
159. //每个线程执行完同步方法后就等待
160. cb.await();
161. } catch (InterruptedException e) {
162. // TODO Auto-generated catch block
163. e.printStackTrace();
164. } catch (BrokenBarrierException e) {
165. // TODO Auto-generated catch block
166. e.printStackTrace();
167. }
168.
169. }
170. });
171. }
172.
173. try {
174. //当前统计线程也wait,所以CyclicBarrier的初始值是threadNum*2+1
175. cb.await();
176. } catch (InterruptedException e) {
177. // TODO Auto-generated catch block
178. e.printStackTrace();
179. } catch (BrokenBarrierException e) {
180. // TODO Auto-generated catch block
181. e.printStackTrace();
182. }
183. //所有线程执行完成之后,才会跑到这一步
184. long duration=System.nanoTime()-start;
185. out.println(id+" = "+duration);
186.
187. }
188.
189.}
- java锁机制Synchronized
- java锁机制Synchronized
- java锁机制Synchronized
- java锁机制Synchronized
- java锁机制Synchronized
- java锁机制Synchronized
- java锁机制Synchronized
- java的锁机制
- java锁机制Synchronized
- java锁机制Synchronized
- java锁机制Synchronized
- java锁机制Synchronized
- Java锁机制synchronized
- java 锁机制
- java的锁机制
- Java锁机制
- java的锁机制
- java锁机制
- 深入理解HTTP协议及原理分析之Https
- 3月18日培训笔记
- Reverse Words in a String
- ARM指令详解之Load/Store指令
- linux 使用NSF 映射远程磁盘目录
- java锁机制
- 站在巨人的肩膀上--同时要怀疑巨人
- MySQL 存储过程中使用 WHILE 循环语句
- java从request中获取当前访问是从哪个完整的url来的
- 代理
- 设计模式
- Qt中创建excel文件
- win7 共享后取消文件夹小锁
- VS2013 编译运行 OTL 示例代码