1 ReentrantLock与synchronized对比 ReentrantLock与synchronized都是为了同步加锁,但ReentrantLock相对效率比synchronized高,量级较轻。 synchronized在JDK1.5版本开始,尝试优化。到JDK1.7版本后,优化效率已经非常好了。在绝对效率上,不比reentrantLock差多少。使用ReentrantLock,必须手工释放锁标记 。一般都是在finally代码块中定义释放锁标记的unlock方法。
2.示例用法 2.1 基本用法 lock()与unlock()就像synchronized同步代码块的开始与结束,使用ReentrantLocky一定要记得unlock()解锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package com.bernardlowe.concurrent.t03; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Test_01 { Lock lock = new ReentrantLock(); void m1(){ try{ lock.lock(); // 加锁 for(int i = 0; i < 10; i++){ TimeUnit.SECONDS.sleep(1); System.out.println("m1() method " + i); } }catch(InterruptedException e){ e.printStackTrace(); }finally{ lock.unlock(); // 解锁 } } void m2(){ lock.lock(); System.out.println("m2() method"); lock.unlock(); } public static void main(String[] args) { final Test_01 t = new Test_01(); new Thread(new Runnable() { @Override public void run() { t.m1(); } }).start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(new Runnable() { @Override public void run() { t.m2(); } }).start(); } }
2.2 尝试锁 尝试锁,顾名思义是尝试获取锁标记trylock(),有两种方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 /** * 尝试锁 */ package com.bernardlowe.concurrent.t03; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Test_02 { Lock lock = new ReentrantLock(); void m1(){ try{ lock.lock(); for(int i = 0; i < 10; i++){ TimeUnit.SECONDS.sleep(1); System.out.println("m1() method " + i); } }catch(InterruptedException e){ e.printStackTrace(); }finally{ lock.unlock(); } } void m2(){ boolean isLocked = false; try{ // 尝试锁, 如果有锁,无法获取锁标记,返回false。 // 如果获取锁标记,返回true // isLocked = lock.tryLock(); // 阻塞尝试锁,阻塞参数代表的时长,尝试获取锁标记。 // 如果超时,不等待。直接返回。 isLocked = lock.tryLock(5, TimeUnit.SECONDS); if(isLocked){ System.out.println("m2() method synchronized"); }else{ System.out.println("m2() method unsynchronized"); } }catch(Exception e){ e.printStackTrace(); }finally{ if(isLocked){ // 尝试锁在解除锁标记的时候,一定要判断是否获取到锁标记。 // 如果当前线程没有获取到锁标记,会抛出异常。 lock.unlock(); } } } public static void main(String[] args) { final Test_02 t = new Test_02(); new Thread(new Runnable() { @Override public void run() { t.m1(); } }).start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } new Thread(new Runnable() { @Override public void run() { t.m2(); } }).start(); } }
2.3 可打断 先解释下线程的几种状态: 阻塞状态: 包括普通阻塞,等待队列,锁池队列。 普通阻塞: sleep(10000), 可以被打断。调用thread.interrupt()方法,可以打断阻塞状态,抛出异常。 等待队列: wait()方法被调用,也是一种阻塞状态,只能由notify唤醒。无法打断 锁池队列: 无法获取锁标记。不是所有的锁池队列都可被打断
使用ReentrantLock的lock方法,获取锁标记的时候,如果需要阻塞等待锁标记,无法被打断。
使用ReentrantLock的lockInterruptibly方法,获取锁标记的时候,如果需要阻塞等待,可以被打断。
示例代码 主线程启动了两个线程t1,t2,t1调用m1(),t2调用m2() 当主线程调用interrupt()方法,可以打断t2线程的阻塞等待锁,抛出异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 package com.bernardlowe.concurrent.t03; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Test_03 { Lock lock = new ReentrantLock(); void m1(){ try{ lock.lock(); for(int i = 0; i < 5; i++){ TimeUnit.SECONDS.sleep(1); System.out.println("m1() method " + i); } }catch(InterruptedException e){ e.printStackTrace(); }finally{ lock.unlock(); } } void m2(){ try{ lock.lockInterruptibly(); // 可尝试打断,阻塞等待锁。可以被其他的线程打断阻塞状态 System.out.println("m2() method"); }catch(InterruptedException e){ System.out.println("m2() method interrupted"); }finally{ try{ lock.unlock(); }catch(Exception e){ e.printStackTrace(); } } } public static void main(String[] args) { final Test_03 t = new Test_03(); new Thread(new Runnable() { @Override public void run() { t.m1(); } }).start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } Thread t2 = new Thread(new Runnable() { @Override public void run() { t.m2(); } }); t2.start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } t2.interrupt();// 打断线程休眠。非正常结束阻塞状态的线程,都会抛出异常。 } }
结果如图
2.4 公平锁 操作系统cpu,为了保证效率,线程的执行机制是竞争机制,或者说是随机机制,是不公平的,使用ReentrantLock实现公平锁,是非常简单的,只需要在创建ReentrantLock的时候传一个参数ReentrantLock lock = new ReentrantLock(true);
示例代码: TestReentrantlock是公平锁 TestSync是非公平锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 /** * 公平锁 */ package com.bernardlowe.concurrent.t03; import java.util.concurrent.locks.ReentrantLock; public class Test_04 { public static void main(String[] args) { TestReentrantlock t = new TestReentrantlock(); //TestSync t = new TestSync(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); t1.start(); t2.start(); } } class TestReentrantlock extends Thread{ // 定义一个公平锁 private static ReentrantLock lock = new ReentrantLock(true); public void run(){ for(int i = 0; i < 5; i++){ lock.lock(); try{ System.out.println(Thread.currentThread().getName() + " get lock"); }finally{ lock.unlock(); } } } } class TestSync extends Thread{ public void run(){ for(int i = 0; i < 5; i++){ synchronized (this) { System.out.println(Thread.currentThread().getName() + " get lock in TestSync"); } } } }
公平锁结果:
非公平锁结果:
更多精彩内容:mrxccc