Lock 的使用
使用 ReentrantLock 类
lock():获取锁
unlock():释放锁
效果和 synchronized 关键字一样。
使用 Condition 实现等待/通知
- Object.wait() == Condition.await()
- Object.wait(long timeout) == Condition.await(long time,TimeUnit unit)
- Object.notify() == Condition.signal()
- Object.notifyAll == Condition.signalAll()
以上的方法,必须在调用 lock.lock() 方法获取同步监视器之后调用。
如何实现通知部分线程:使用多个 Condition,各自通知自己。
实现生产者/消费者模式:一对一交替打印
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class MyService {
private ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
private boolean hasValue = false;
public void set(){
try {
lock.lock();
while(hasValue){
condition.await();
}
System.out.println("★");
hasValue = true;
condition.signal();
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void get(){
try {
lock.lock();
while(!hasValue){
condition.await();
}
System.out.println("☆");
hasValue = false;
condition.signal();
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
public class MyThreadA implements Runnable {
private MyService myService;
public MyThreadA(MyService myService) {
super();
this.myService = myService;
}
@Override
public void run() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
myService.set();
}
}
}
public class MyThreadB implements Runnable {
private MyService myService;
public MyThreadB(MyService myService) {
super();
this.myService = myService;
}
@Override
public void run() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
myService.get();
}
}
}
public class Run {
public static void main(String[] args) {
MyService myService = new MyService();
MyThreadA myThreadA = new MyThreadA(myService);
new Thread(myThreadA).start();
MyThreadB myThreadB = new MyThreadB(myService);
new Thread(myThreadB).start();
}
}
公平锁与非公平锁
公平锁:线程获取锁的顺序是按照线程加载的顺序来分配的(先来先得 FIFO)
非公平锁:一种获取锁的抢占机制,是随机获得锁的
- new ReentrantLock(true); // 公平锁
- new ReentrantLock(false); // 非公平锁(默认这种,空构造函数)
Lock 常用方法
getHoldCount()、getQueueLength()和getWaitQueueLength()
- int getHoldCount():查询当前线程保持此锁定的个数(调用 lock() 方法的次数)
- int getQueueLength():返回正等待获取此锁定的线程估计数(等待 lock 释放的线程数)
- int getWaitQueueLength(Condition condition):返回等待与此锁定相关的给定条件 Condition 的线程估计数(执行同一个condition.await()方法的线程数)
hasQueuedThread()、hasQueuedThreads()和hasWaiters()
- boolean hasQueuedThread(Thread thread):查询指定的线程是否正在等待获取此锁定
- boolean hasQueuedThreads():查询是否有线程正在等待获取此锁定
- boolean hasWaiters(Condition condition):查询是否有线程正在等待与此锁定有关的 condition 条件
isFair()、isHeldByCurrentThread()和isLocked()
- boolean isFair():判断是不是公平锁
- boolean isHeldByCurrentThread():查询当前线程是否保持此锁定
- boolean isLocked():查询此锁定是否由任意线程保持
lockInterruptibly()、tryLock()和tryLock(long timeout,TimeUnit unit)
- void lockInterruptibly():如果当前线程未被中断,则获取此锁定,如果已经被中断则出现异常
- boolean tryLock():仅在调用时锁定未被另一个线程保持的情况下,才获取该锁定。
- boolean tryLock(long timeout,TimeUnit unit):如果锁定在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁定。
使用 Condition 实现顺序执行
使用 Condition 对象,可以对线程执行的业务进行排序规划
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Run {
volatile private static int nextPrintWho = 1;
private static ReentrantLock lock = new ReentrantLock();
final private static Condition conditionA = lock.newCondition();
final private static Condition conditionB = lock.newCondition();
final private static Condition conditionC = lock.newCondition();
public static void main(String[] args) {
Runnable runnableA = new Runnable() {
@Override
public void run() {
try {
lock.lock();
while (nextPrintWho != 1) {
conditionA.await();
}
for (int i = 0; i < 3; i++) {
System.out.println("ThreadA " + (i + 1));
}
nextPrintWho = 2;
conditionB.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
};
Runnable runnableB = new Runnable() {
@Override
public void run() {
try {
lock.lock();
while (nextPrintWho != 2) {
conditionB.await();
}
for (int i = 0; i < 3; i++) {
System.out.println("ThreadB " + (i + 1));
}
nextPrintWho = 3;
conditionC.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
};
Runnable runnableC = new Runnable() {
@Override
public void run() {
try {
lock.lock();
while (nextPrintWho != 3) {
conditionC.await();
}
for (int i = 0; i < 3; i++) {
System.out.println("ThreadC " + (i + 1));
}
nextPrintWho = 1;
conditionA.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
};
for (int i = 0; i < 5; i++) {
new Thread(runnableA).start();
new Thread(runnableB).start();
new Thread(runnableC).start();
}
}
}
使用 ReentrantReadWriteLock 类
读写锁有两个锁:一个读操作相关锁(共享锁),一个写操作相关锁(排他锁)。
- 多个 Thread 可以同时进行读取操作
- 同一时刻只允许一个 Thread 进行写入操作
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Run {
public static void main(String[] args) throws InterruptedException {
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
Runnable read = new Runnable() {
@Override
public void run() {
try {
try {
lock.readLock().lock();
System.out.println("获得读锁 " + Thread.currentThread().getName() + " " + System
.currentTimeMillis());
Thread.sleep(3000);
} finally {
lock.readLock().unlock();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Runnable write = new Runnable() {
@Override
public void run() {
try {
try {
lock.writeLock().lock();
System.out.println("获得写锁 " + Thread.currentThread().getName() + " " + System
.currentTimeMillis());
Thread.sleep(3000);
} finally {
lock.writeLock().unlock();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
System.out.println("读读共享:");
new Thread(read).start();
new Thread(read).start();
Thread.sleep(10000);
System.out.println("写写互斥");
new Thread(write).start();
new Thread(write).start();
Thread.sleep(10000);
System.out.println("读写互斥");
new Thread(read).start();
Thread.sleep(1000);
new Thread(write).start();
Thread.sleep(10000);
System.out.println("写读互斥");
new Thread(write).start();
Thread.sleep(1000);
new Thread(read).start();
}
}