目录
一、可重入锁
感觉可重入锁就类似一个嵌套锁,例如:
/**
* 可重入锁
*/
public class ThreadDemo4 {
public static void main(String[] args) {
Lock lock=new ReentrantLock();
new Thread(()->{
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "外层");
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "内层");
} finally {
lock.unlock();
}
} finally {
lock.unlock();
}
},"t1").start();
}
}
t1外层
t1内层
/**
* 可重入锁
*/
public static void main(String[] args) {
Object o = new Object();
new Thread(()->{
synchronized (o) {
System.out.println(Thread.currentThread().getName() + "外层");
synchronized (o) {
System.out.println(Thread.currentThread().getName() + "中层");
synchronized (o) {
System.out.println(Thread.currentThread().getName() + "内层");
}
}
}
},"A").start();
}
A外层
A中层
A内层
二、读写锁
现实中有这样一种场景:对共享资源有读和写的操作,且写操作没有读操作那 么频繁。在没有写操作的时候,多个线程同时读一个资源没有任何问题,所以应该允许多个线程同时读取共享资源;但是如果一个线程想去写这些共享资源, 就不应该允许其他线程对该资源进行读和写的操作了。
针对这种场景,JAVA 的并发包提供了读写锁 ReentrantReadWriteLock, 它表示两个锁,一个是读操作相关的锁,称为共享锁;一个是写相关的锁,称为排他锁
1. 线程进入读锁的前提条件:
• 没有其他线程的写锁
• 没有写请求, 或者==有写请求,但调用线程和持有锁的线程是同一个(可重入锁)。
2. 线程进入写锁的前提条件:
• 没有其他线程的读锁
• 没有其他线程的写锁
特点:
(1)公平选择性:支持非公平(默认)和公平的锁获取方式,吞吐量还是非公平优于公平。
(2)重进入:读锁和写锁都支持线程重进入。
(3)锁降级:遵循获取写锁、获取读锁再释放写锁的次序,写锁能够降级成为读锁。
• 在线程持有读锁的情况下,该线程不能取得写锁(因为获取写锁的时候,如果发现当前的读锁被占用,就马上获取失败,不管读锁是不是被当前线程持有)。
• 在线程持有写锁的情况下,该线程可以继续获取读锁(获取读锁时如果发现写 锁被占用,只有写锁没有被当前线程占用的情况才会获取失败)。
原因: 当线程获取读锁的时候,可能有其他线程同时也在持有读锁,因此不能把获取读锁的线程“升级”为写锁;而对于获得写锁的线程,它一定独占了读写锁,因此可以继续让它获取读锁,当它同时获取了写锁和读锁后,还可以先释放写锁继续持有读锁,这样一个写锁就“降级”为了读锁。
/** * 写锁可以将为读锁,但不能反过来 */ public class ReadWriteLock1 { public static void main(String[] args) { ReentrantReadWriteLock rwLock=new ReentrantReadWriteLock(); ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock(); ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock(); writeLock.lock();//上写锁 System.out.println("写..."); readLock.lock();//上读锁 System.out.println("读..."); writeLock.unlock();//放写锁 readLock.unlock();//放读锁 } } 写... 读...
下面是一个使用读写锁操作map的例子:
class MyCache{
private volatile Map<String,Object> map=new HashMap<>();
private ReentrantReadWriteLock rwLock=new ReentrantReadWriteLock();
public void put(String key,Object value) throws InterruptedException {
rwLock.writeLock().lock();//加写锁
try {
System.out.println(Thread.currentThread().getName()+"正在写操作..."+key);
TimeUnit.MILLISECONDS.sleep(300);
map.put(key,value);
System.out.println(Thread.currentThread().getName()+"写入完成..."+key);
} finally {
rwLock.writeLock().unlock();//解读锁
}
}
public Object get(String key) throws InterruptedException {
rwLock.readLock().lock();//加读锁
Object res;
try {
System.out.println(Thread.currentThread().getName()+"正在读操作..."+key);
TimeUnit.MILLISECONDS.sleep(300);
res = map.get(key);
System.out.println(Thread.currentThread().getName()+"读取完成..."+key);
} finally {
rwLock.readLock().unlock();//解读锁
}
return res;
}
}
public class ReadWriteLock {
public static void main(String[] args) {
MyCache myCache=new MyCache();
//5个线程写数据
for(int i=1;i<=5;i++) {
int num=i;
new Thread(()->{
try {
myCache.put(num+"",num+"");
} catch (InterruptedException e) {
e.printStackTrace();
}
},String.valueOf(i)).start();
}
//5个线程读数据
for(int i=1;i<=5;i++) {
int num=i;
new Thread(()->{
try {
myCache.get(num+"");
} catch (InterruptedException e) {
e.printStackTrace();
}
},String.valueOf(i)).start();
}
}
}
1正在写操作...1
1写入完成...1
2正在写操作...2
2写入完成...2
3正在写操作...3
3写入完成...3
4正在写操作...4
4写入完成...4
5正在写操作...5
5写入完成...5
1正在读操作...1
4正在读操作...4
2正在读操作...2
3正在读操作...3
5正在读操作...5
1读取完成...1
3读取完成...3
5读取完成...5
2读取完成...2
4读取完成...4