转自(侵删):http://blog.csdn.net/a314773862/article/details/54095819 1、自旋锁 可能引起的问题: 2、阻塞锁 3、可重入锁 1. public class Test implements Runnable{ 3. public synchronized void get(){ 4. System.out.println(Thread.currentThread().getId()); 5. set(); 6. } 8. public synchronized void set(){ 9. System.out.println(Thread.currentThread().getId()); 10. } 12. @Override 13. public void run() { 14. get(); 15. } 16. public static void main(String[] args) { 17. Test ss=new Test(); 18. new Thread(ss).start(); 19. new Thread(ss).start(); 20. new Thread(ss).start(); 21. } 22. } 24. public class Test implements Runnable { 25. ReentrantLock lock = new ReentrantLock(); 27. public void get() { 28. lock.lock(); 29. System.out.println(Thread.currentThread().getId()); 30. set(); 31. lock.unlock(); 32. } 34. public void set() { 35. lock.lock(); 36. System.out.println(Thread.currentThread().getId()); 37. lock.unlock(); 38. } 40. @Override 41. public void run() { 42. get(); 43. } 45. public static void main(String[] args) { 46. Test ss = new Test(); 47. new Thread(ss).start(); 48. new Thread(ss).start(); 49. new Thread(ss).start(); 50. } 51. } public class Test implements Runnable{ public synchronized void get(){ System.out.println(Thread.currentThread().getId()); set(); } public synchronized void set(){ System.out.println(Thread.currentThread().getId()); } @Override public void run() { get(); } public static void main(String[] args) { Test ss=new Test(); new Thread(ss).start(); new Thread(ss).start(); new Thread(ss).start(); }} public class Test implements Runnable { ReentrantLock lock = new ReentrantLock(); public void get() { lock.lock(); System.out.println(Thread.currentThread().getId()); set(); lock.unlock(); } public void set() { lock.lock(); System.out.println(Thread.currentThread().getId()); lock.unlock(); } @Override public void run() { get(); } public static void main(String[] args) { Test ss = new Test(); new Thread(ss).start(); new Thread(ss).start(); new Thread(ss).start(); }} 两个例子最后的结果都是正确的,即 同一个线程id被连续输出两次。 1. public class SpinLock { 2. private AtomicReference public class SpinLock { private AtomicReference 对于自旋锁来说, 1. public class SpinLock1 { 2. private AtomicReference public class SpinLock1 { private AtomicReference 该自旋锁即为可重入锁。 4 悲观锁和乐观锁 乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。使用CAS来保证,保证这个操作的原子性 两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果经常产生冲突,上层应用会不断的进行retry,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。 参考:http://www.cnblogs.com/softidea/p/5309312.html 5 轮询锁和定时锁 boolean tryLock(long time, TimeUnit unit) throws InterruptedException: 如果锁可用,则此方法将立即返回值 true。如果锁不可用,出于线程调度目的,将禁用当前线程,并且在发生以下三种情况之一前,该线程将一直处于休眠状态: 锁由当前线程获得;或者 如果当前线程: 在进入此方法时已经设置了该线程的中断状态;或者 6 显示锁和内置锁 7 读-写锁 在读写锁的加锁策略中,允许多个读操作同时进行,但每次只允许一个写操作。读写锁是一种性能优化的策略。 RentrantReadWriteLock在构造时也可以选择是一个非公平的锁(默认)还是公平的锁。 8 对象锁和类锁 调用对象wait()方法时,会释放持有的对象锁,以便于调用notify方法使用。notify()调用之后,会等到notify所在的线程执行完之后再释放锁 9:锁粗化(Lock Coarsening): 1. 1 package com.paddx.test.string; 2. 2 3. 3 public class StringBufferTest { 4. 4 StringBuffer stringBuffer = new StringBuffer(); 5. 5 6. 6 public void append(){ 7. 7 stringBuffer.append('a'); 8. 8 stringBuffer.append('b'); 9. 9 stringBuffer.append('c'); 10. 10 } 11. 11 } 1 package com.paddx.test.string; 2 3 public class StringBufferTest { 4 StringBuffer stringBuffer = new StringBuffer(); 5 6 public void append(){ 7 stringBuffer.append('a'); 8 stringBuffer.append('b'); 9 stringBuffer.append('c');10 }11 } 这里每次调用stringBuffer.append方法都需要加锁和解锁,如果虚拟机检测到有一系列连串的对同一个对象加锁和解锁操作,就会将其合并成一次范围更大的加锁和解锁操作,即在第一次append方法时进行加锁,最后一次append方法结束后进行解锁。 10 互斥锁 15 无锁状态-》偏向锁-》轻量级锁-》重量级锁。锁膨胀 无锁状态:在代码进入同步块的时候,如果同步对象锁状态为无锁状态。 重量级锁、轻量级锁和偏向锁之间转换: 11 锁消除(Lock Elimination):锁消除即删除不必要的加锁操作。根据代码逃逸技术,如果判断到一段代码中,堆上的数据不会逃逸出当前线程,那么可以认为这段代码是线程安全的,不必要加锁。看下面这段程序: 3. public class SynchronizedTest02 { 5. public static void main(String[] args) { 6. SynchronizedTest02 test02 = new SynchronizedTest02(); 7. //启动预热 8. for (int i = 0; i < 10000; i++) { 9. i++; 10. } 11. long start = System.currentTimeMillis(); 12. for (int i = 0; i < 100000000; i++) { 13. test02.append('abc', 'def'); 14. } 15. System.out.println('Time=' + (System.currentTimeMillis() - start)); 16. } 18. public void append(String str1, String str2) { 19. StringBuffer sb = new StringBuffer(); 20. sb.append(str1).append(str2); 21. } 22. } public class SynchronizedTest02 { public static void main(String[] args) { SynchronizedTest02 test02 = new SynchronizedTest02(); //启动预热 for (int i = 0; i < 10000; i++) { i++; } long start = System.currentTimeMillis(); for (int i = 0; i < 100000000; i++) { test02.append('abc', 'def'); } System.out.println('Time=' + (System.currentTimeMillis() - start)); } public void append(String str1, String str2) { StringBuffer sb = new StringBuffer(); sb.append(str1).append(str2); } } 虽然StringBuffer的append是一个同步方法,但是这段程序中的StringBuffer属于一个局部变量,并且不会从该方法中逃逸出去,所以其实这过程是线程安全的,可以将锁消除。下面是我本地执行的结果 12、信号量 |
|