1. Lock接口
1
| public interface Lock {}
|
提供更多实用性方法,功能更强大、性能更优越。
常用方法:
- void lock() // 获取锁,如锁被占用,则等待
- boolean trylock() // 尝试获取锁(成功true,失败false,不阻塞)
- void unlock() // 释放锁
2. ReentrantLock类(重入锁/递归锁)
1
| public class ReentrantLock extends Object implements Lock, Serializable
|
- Lock接口的实现类,与synchronized一样具有互斥锁功能。
注意:
1)使用Lock,需要显式的获取锁和释放锁;
2)为了避免拿到锁的线程在运行期间出现异常,导致程序终止没有释放锁!应用try-finally来保证无论是否出现异常,最终必须释放锁;
3)ReentrantLock为重入锁,避免递归,如果必须递归,必须正确控制退出条件。
(此锁支持同一线程的2147483647个递归锁的最大值。试图在Error超过这个限制的结果将从锁定的方法。)
主要的API接口:
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
| ReentrantLock()
ReentrantLock(boolean fair)
int getHoldCount()
protected Thread getOwner()
protected Collection<Thread> getQueuedThreads()
int getQueueLength()
protected Collection<Thread> getWaitingThreads(Condition condition)
int getWaitQueueLength(Condition condition)
boolean hasQueuedThread(Thread thread)
boolean hasQueuedThreads()
boolean hasWaiters(Condition condition)
boolean isFair()
boolean isHeldByCurrentThread()
boolean isLocked()
void lock()
void lockInterruptibly()
Condition newCondition()
boolean tryLock()
boolean tryLock(long timeout, TimeUnit unit)
void 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 51 52
| import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class TestLocks { public static void main(String[] args) { Test obj = new Test(); Thread t1 = new Thread(new MyTask(obj)); Thread t2 = new Thread(new MyTask2(obj)); t1.start(); t2.start(); } } class Test{ Lock lock = new ReentrantLock(); public void method() { System.out.println(Thread.currentThread().getName() + "进入上锁的方法中..."); try { lock.lock(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "退出上锁的方法中..."); } finally { lock.unlock(); } } } class MyTask implements Runnable { Test obj; public MyTask(Test obj) { super(); this.obj = obj; } @Override public void run() { obj.method(); } } class MyTask2 implements Runnable { Test obj; public MyTask2(Test obj) { super(); this.obj = obj; } @Override public void run() { obj.method(); } }
|
3. ReentrantReadWriteLock类
(读写锁) - ReadWriteLock接口实现类
1
| public class ReentrantReadWriteLock extends Object implements ReadWriteLock, Serializable
|
- 一种支持一写多读的同步锁,读写分离,可分别分配读锁、写锁;
- 支持多次分配读锁,使多个读操作可以并发执行(写锁同步,读锁异步)。
互斥规则:
① 写-写:互斥,阻塞;
② 读-写:互斥,读阻塞写、写阻塞读;
③ 读-读:不互斥、不阻塞;
- 在读操作远远高于写操作的环境下,可在保证线程安全的情况下,提高运行效率。
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 77 78 79 80 81 82 83 84 85 86 87
| import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; public class TestReadWriteLock { public static void main(String[] args) { Student s = new Student(); ExecutorService es = Executors.newFixedThreadPool(20); WriteTask write = new WriteTask(s); ReadTask read = new ReadTask(s); long start = System.currentTimeMillis(); es.submit(write); es.submit(write); for (int i = 1; i <= 18; i++) { es.submit(read); } es.shutdown(); while(true) { System.out.println("结束了吗?"); if (es.isTerminated() == true) { break; } } long end = System.currentTimeMillis(); System.out.println("运行时间:" + (end-start)); } } class WriteTask implements Callable<Object> { Student stu; public WriteTask(Student stu) { this.stu = stu; } @Override public Object call() throws Exception { this.stu.setAge(20); return null; } } class ReadTask implements Callable<Object> { Student stu; public ReadTask(Student stu) { this.stu = stu; } @Override public Object call() throws Exception { this.stu.getAge(); return null; } } class Student { private int age; ReentrantReadWriteLock rrwl = new ReentrantReadWriteLock(); ReadLock read = rrwl.readLock(); WriteLock write = rrwl.writeLock(); public void setAge(int age) throws InterruptedException { write.lock(); try { Thread.sleep(1000); this.age = age; } finally { write.unlock(); } } public int getAge() throws InterruptedException { read.lock(); try { Thread.sleep(1000); return this.age; } finally { read.lock(); } } }
|
4. ReentrantLock 的3个高级功能
由于ReentrantLock是java.util.concurrent包下提供的一套互斥锁,相比Synchronized,ReentrantLock类提供了3个高级功能(表格中也有锁体现)。
功能①:等待可中断
持有锁的线程长期不释放的时候,正在等待的线程可以选择放弃等待,这相当于Synchronized来说可以避免出现死锁的情况。通过lock.lockInterruptibly()来实现这个机制。
三个API说明:
1)lock(), 拿不到lock就不罢休,不然线程就一直block,死等。
2)tryLock(),马上返回,拿到lock就返回true,不然返回false,不等。带时间限制的tryLock(),拿不到lock,就等一段时间,超时返回false。
3)lockInterruptibly():
- 线程在sleep或wait,join此时如果别的进程调用此进程的 interrupt()方法,此线程会被唤醒并被要求处理InterruptedException;
- 此线程在运行中,则不会收到提醒。但是此线程的 “打扰标志”会被设置, 可以通过isInterrupted()查看并作出处理。线程在请求lock并被阻塞时,如果被interrupt,则“此线程会被唤醒并被要求处理InterruptedException”。并且如果线程已经被interrupt,再使用lockInterruptibly的时候,此线程也会被要求处理interruptedException。
代码示例:
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
| import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;
public class TestInterrupt { public static void main(String[] args) throws Exception { new TestInterrupt().test(); }
public void test() throws Exception { final Lock lock = new ReentrantLock(); lock.lock();
Thread t1 = new Thread(new Runnable() { @Override public void run() { try { int i = 5; while (i-- > 0) { System.out.println("I'm running " + i); Thread.sleep(1000); } lock.lockInterruptibly(); } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName() + " interrupted(我被中断了)."); System.out.println("被打断后所做的事情..."); } } }, "子线程-1");
t1.start(); Thread.sleep(3000); t1.interrupt(); } }
|
释义:线程中断(interrupt)
中断一个线程,其本意是**给这个线程一个通知信号,会影响这个线程内部的一个中断标识位。这个线程本身并不会因此而改变状态(如阻塞,终止等)**。
- 调用 interrupt()方法并不会中断一个正在运行的线程。也就是说处于 Running 状态的线程并不会因为被中断而被终止,仅仅改变了内部维护的中断标识位而已。
- 若调用 sleep()而使线程处于 TIMED-WATING 状态,这时调用 interrupt()方法,会抛出InterruptedException,从而使线程提前结束 TIMED-WATING 状态。
- 许多声明抛出 InterruptedException 的方法(如 Thread.sleep(long mills 方法)),抛出异常前,都会清除中断标识位,所以抛出异常后,调用 isInterrupted()方法将会返回 false。
- 中断状态是线程固有的一个标识位,可以通过此标识位安全的终止线程。比如,你想终止一个线程 thread 的时候,可以调用 thread.interrupt()方法,在线程的 run 方法内部可以根据 thread.isInterrupted()的值来优雅的终止线程。
功能②:公平锁机制
多个线程等待同一个锁时,必须按照申请锁的时间顺序获得锁,Synchronized锁非公平锁,ReentrantLock默认的构造函数是创建的非公平锁,可以通过参数true设为公平锁,但公平锁表现的性能不是很好。
公平锁:加锁前检查是否有排队等待的线程,优先排队等待的线程,先来先得;
非公平锁:加锁时不考虑排队等待问题,直接尝试获取锁,获取不到自动到队尾等待。
公平锁、非公平锁的创建方式:
1 2 3 4 5 6
| Lock lock = new ReentrantLock(); Lock lock = new ReentrantLock(false);
Lock lock = new ReentrantLock(true);
|
功能③:锁绑定多个条件
一个ReentrantLock对象可以同时绑定对个对象。ReentrantLock提供了一个Condition(条件)类,用来实现分组唤醒需要唤醒的线程,而不是像synchronized要么随机唤醒一个线程要么唤醒全部线程。
代码示例:
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 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
| import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock;
public class TestCondition { public static void main(String[] args) throws InterruptedException { MyService service = new MyService(); Thread t1 = new Thread(new MyServiceThread1(service), "a"); Thread t2 = new Thread(new MyServiceThread2(service), "b"); t1.start(); t2.start();
Thread.sleep(2000); service.signallA();
Thread.sleep(3000); service.signallB(); } }
class MyServiceThread1 implements Runnable { private MyService service; public MyServiceThread1(MyService service) { this.service = service; } @Override public void run() { service.awaitA(); } }
class MyServiceThread2 implements Runnable { private MyService service; public MyServiceThread2(MyService service) { this.service = service; } @Override public void run() { service.awaitB(); } }
class MyService { private ReentrantLock lock = new ReentrantLock(); public Condition conditionA = lock.newCondition(); public Condition conditionB = lock.newCondition();
public void awaitA() { try { lock.lock(); System.out.println(Thread.currentThread().getName() + "进入了awaitA方法"); long timeBefore = System.currentTimeMillis(); conditionA.await(); long timeAfter = System.currentTimeMillis(); System.out.println(Thread.currentThread().getName() + "被唤醒"); System.out.println(Thread.currentThread().getName() + "等待了: " + (timeAfter - timeBefore) / 1000 + "s"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } }
public void awaitB() { try { lock.lock(); System.out.println(Thread.currentThread().getName() + "进入了awaitB方法"); long timeBefore = System.currentTimeMillis(); conditionB.await(); long timeAfter = System.currentTimeMillis(); System.out.println(Thread.currentThread().getName() + "被唤醒"); System.out.println(Thread.currentThread().getName() + "等待了: " + (timeAfter - timeBefore) / 1000 + "s"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } }
public void signallA() { try { lock.lock(); System.out.println("启动唤醒程序"); conditionA.signalAll(); } finally { lock.unlock(); } }
public void signallB() { try { lock.lock(); System.out.println("启动唤醒程序"); conditionB.signalAll(); } finally { lock.unlock(); } } }
|
补充:Condition 类和 Object 类锁方法区别
- Condition 类的 awiat 方法和 Object 类的 wait 方法等效;
- Condition 类的 signal 方法和 Object 类的 notify 方法等效;
- Condition 类的 signalAll 方法和 Object 类的 notifyAll 方法等效;
- ReentrantLock 类可以唤醒指定条件的线程,而 Object 的唤醒是随机的。
补充:trylock 和 lock 和 lockInterruptibly 方法区别
- tryLock 能获得锁就返回 true,不能就立即返回 false,tryLock(long timeout,TimeUnit unit),可以增加时间限制,如果超过该时间段还没获得锁,返回 false;
- lock 能获得锁就返回 true,不能的话一直等待获得锁;
- lock 和 lockInterruptibly,如果两个线程分别执行这两个方法,但此时中断这两个线程,lock 不会抛出异常,而 lockInterruptibly 会抛出异常。
5. 什么时候使用ReentrantLock?
答案是:
- 原子操作的颗粒度更小的加锁操作或跨方法释放锁时(灵活);
- 如果你需要使用ReentrantLock的三个高级功能时。