Lock 显示锁

锁是用来控制多个线程访问共享资源的方式,一般来说,一个锁能够防止多个线程同时访问共享资源(但是有些锁可以允许多个线程并发的访问共享资源,比如读写锁)。

在Lock接口出现之前,Java程序是靠synchronized关键字实现锁功能的,而Java SE 5之后,并发包中新增了Lock接口(以及相关实现类)用来实现锁功能,它提供了与synchronized关键字类似的同步功能,只是在使用时需要显式地获取和释放锁。

虽然它缺少了(通过synchronized块或者方法所提供的)隐式获取释放锁的便捷性,但是却拥有了锁获取与释放的可操作性、可中断的获取锁以及超时获取锁等多种synchronized关键字所不具备的同步特性。

 

Java并发包中的显式锁接口和类位于包java.util.concurrent.locks下,主要接口和类有:

❑ 锁接口Lock,主要实现类是ReentrantLock;

❑ 读写锁接口ReadWriteLock,主要实现类是ReentrantReadWriteLock。

 

Lock接口

显式锁接口Lock的定义为:

1
2
3
4
5
6
7
8
public interface Lock {
    void lock();
    void lockInterruptibly() throws InterruptedException;
    boolean tryLock();
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    void unlock();
    Condition newCondition();
}

1)lock()/unlock():就是普通的获取锁和释放锁方法,lock()会阻塞直到成功。

2)lockInterruptibly():与lock()的不同是,它可以响应中断,如果被其他线程中断了,则抛出InterruptedException。

3)tryLock():只是尝试获取锁,立即返回,不阻塞,如果获取成功,返回true,否则返回false。

4)tryLock(long time, TimeUnit unit):先尝试获取锁,如果能成功则立即返回true,否则阻塞等待,但等待的最长时间由指定的参数设置,在等待的同时响应中断,如果发生了中断,抛出InterruptedException,如果在等待的时间内获得了锁,返回true,否则返回false。

5)newCondition:新建一个条件,一个Lock可以关联多个条件。

可以看出,相比synchronized,显式锁支持以非阻塞方式获取锁、可以响应中断、可以限时,这使得它灵活得多。

 

重入锁ReentrantLock

Lock接口的主要实现类是ReentrantLock,它的基本用法lock/unlock实现了与synchronized一样的语义,包括:

❑ 可重入,一个线程在持有一个锁的前提下,可以继续获得该锁;

❑ 可以解决竞态条件问题;

❑ 可以保证内存可见性。

ReentrantLock有两个构造方法:

1
2
public ReentrantLock()
public ReentrantLock(boolean fair)

参数fair表示是否保证公平,不指定的情况下,默认为false,表示不保证公平。所谓公平是指,等待时间最长的线程优先获得锁。保证公平会影响性能,一般也不需要,所以默认不保证,synchronized锁也是不保证公平的

使用显式锁,一定要记得调用unlock。一般而言,应该将lock之后的代码包装到try语句内,在finally语句内释放锁。比如,使用ReentrantLock实现Counter,代码可以为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Counter {
    private final Lock lock = new ReentrantLock();
    private volatile int count;
    public void incr() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }
    public int getCount() {
        return count;
    }
}

 

读写锁ReentrantReadWriteLock

ReadWriteLock仅定义了获取读锁和写锁的两个方法,即readLock()方法和writeLock()方法

ReentrantReadWriteLock使用方式如下:

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
public class Cache {
    private static final Map<String, Object>    map = new HashMap<String, Object>();
    private static final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    private static final Lock                   r   = rwl.readLock();
    private static final Lock                   w   = rwl.writeLock();
    public static final Object get(String key) {
        r.lock();
        try {
            return map.get(key);
        } finally {
            r.unlock();
        }
    }
    public static final Object put(String key, Object value) {
        w.lock();
        try {
            return map.put(key, value);
        } finally {
            w.unlock();
        }
    }
    public static final void clear() {
        w.lock();
        try {
            map.clear();
        } finally {
            w.unlock();
        }
    }
}

当使用 ReadWriteLock 的时候,并不是直接使用,而是获得其内部的读锁和写锁,然后分别调用lock/unlock。

从表面来看,ReadLock和WriteLock是两把锁,实际上它只是同一 把锁的两个视图而已。什么叫两个视图呢?可以理解为是一把锁,线程分成两类:读线程和写线程。读线程和读线程之间不互斥(可以同时拿到这把锁),读线程和写线程互斥,写线程和写线程也互斥。

 

 

 

 

参考: Java编程的逻辑 16.2 显式锁

  Java并发编程的艺术 5.1 Lock接口

  Java并发实现原理 3.1 互斥锁

posted @   草木物语  阅读(75)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
历史上的今天:
2018-01-25 js判断一个字符串是以某个字符串开头
2018-01-25 如何 通过 userAgent 区别微信小程序
点击右上角即可分享
微信分享提示