Lock

不积跬步,无以至千里。不积小流,无以成江海。

ReentrantLock

1
private Lock lock = new ReentrantLock();

调用 ReentrantLock 对象的 lock() 方法获取锁,调用 unlock() 方法释放锁。

使用 Condition 实现等待/通知:错误用法与解决

关键字 synchronized 与 wait() 和 notify()/notifyAll() 方法相结合可以实现等待/通知模式,类 ReentrantLock 也可以实现同样的功能,但需要借助于 Condition 对象。Condition 类有更好的灵活性,比如可以实现多路通知功能,也就是在一个 Lock 对象里面可以创建多个 Condition(即对象监视器)实例,线程对象可以注册在指定的 Condition中,从而可以有选择性地进行线程通知,在调度线程上更加灵活。(注意:在使用 Condition的await() 方法之前必须获得锁)

示例:

1
2
3
4
5
6
7
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void waitMethod() {
    lock.lock();
    condition.await();
    condition.signal();
}

Object 类中的 wait() 方法相当于 Condition 类中的 await() 方法。

Object 类中的 wait(longtimeout) 方法相当于 Condition 类中的 await(longtime,TimeUnit unit) 方法。

Object 类中的 notify() 方法相当于 Condition 类中的 signal() 方法。

Object 类中的 notifyAll() 方法相当于 Condition 类中的 signalAll() 方法。

使用多个 Condition 实现通知部分线程

示例:

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
public class Myservice{
public Lock lock = new ReentrantLock();
public Condition a = lock.newCondition();
public Condition b = lock.newCondition();
    public void awaitA(){
        lock.lock();
        a.await();
        lock.unlock();
    }
    
    public void awaitB(){
        lock.lock();
        b.await();
        lock.unlock();
    }
    
    public void signalAllA(){
        lock.lock();
        a.signalAll();
        lock.unlock();
    }
    
    public void signalAllB(){
        lock.lock();
        b.signalAll();
        lock.unlock();
    }
}

public class ThreadA extends Thread {
    private Myservice service;
    public ThreadA(MyService service){
        super();
        this.service = service;
    }
    
    public void run(){
        service.awaitA();
    }
}

B线程代码同理

public class Run {
public static void main(String[] args) throws InterruptedException {
    MyService service = new MyService();
    ThreadA a = new ThreadA(service);
    a.setName("A");
    a.start();
    ThreadB b = new ThreadB(service);
    b.setName("B");
    b.start();
    Thread.sleep(3000);
    service.signalAll_A();
}
}

结果:

1
只有A线程被唤醒

公平锁和非公平锁

1
2
3
4
公平锁:
public Lock lock = new ReentrantLock(true);
非公平锁:
public Lock lock = new ReentrantLock(false);

其他方法

1、方法 int getHoldCount() 的作用是查询当前线程保持此锁定的个数,也就是调用 lock() 方法的次数。

2、方法 int getQueueLength() 的作用是返回正等待获取此锁定的线程估计数,比如有5个线程,1个线程首先执行 await() 方法,那么在调用 getQueueLength() 方法后返回值是4,说明有4个线程同时在等待 lock 的释放。

3、方法 int getWaitQueueLength(Condition condition) 的作用是返回等待与此锁定相关的给定条件 Condition 的线程估计数,比如有5个线程,每个线程都执行了同一个 condition 对象的 await() 方法,则调用 getWaitQueueLength(Condition condition)方法时返回的 int 值是5。

4、方法 boolean hasQueuedThread(Thread thread) 的作用是查询指定的线程是否正在等待获取此锁定。

5、方法 boolean hasWaiters(Condition condition) 的作用是查询是否有线程正在等待与此锁定有关的 condition 条件。

6、方法 boolean isFair() 的作用是判断是不是公平锁。

7、方法 boolean isHeldByCurrentThread() 的作用是查询当前线程是否保持此锁定。

8、方法 boolean isLocked() 的作用是查询此锁定是否由任意线程保持。

9、方法 boolean tryLock() 的作用是,仅在调用时锁定未被另一个线程保持的情况下,才获取该锁定。

10、方法 boolean tryLock(long timeout,TimeUnit unit) 的作用是,如果锁定在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁定。

ReentrantReadWriteLock

类 ReentrantLock 具有完全互斥排他的效果,即同一时间只有一个线程在执行 ReentrantLock.lock() 方法后面的任务。这样做虽然保证了实例变量的线程安全性,但效率却是非常低下的。所以在 JDK 中提供了一种读写锁 ReentrantReadWriteLock 类,使用它可以加快运行效率。读写锁表示也有两个锁,一个是读操作相关的锁,也称为共享锁;另一个是写操作相关的锁,也叫排他锁。也就是多个读锁之间不互斥,读锁与写锁互斥,写锁与写锁互斥。在没有线程 Thread 进行写入操作时,进行读取操作的多个 Thread 都可以获取读锁,而进行写入操作的 Thread 只有在获取写锁后才能进行写入操作。即多个 Thread 可以同时进行读取操作,但是同一时刻只允许一个 Thread 进行写入操作。

获取读锁(不互斥)

1
2
3
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
lock.readLock().lock();
lock.readLock.unlock();

获取写锁(互斥)

1
2
3
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
lock.writeLock().lock();
lock.writeLock.unlock();

互斥规则

读读不互斥

写写互斥

读写互斥

写读互斥