可重入锁:
可重入锁,也叫递归锁,指定是同一线程 外层函数获得锁之后,内层递归函数仍可以再次获取锁而不会出现死锁。
public class WhatReentrant{
public static void main(String[] args){
new Thread(() -> {
synchronized (this) {
System.out.println("第一次获取锁,这个锁是:" + this);
int index = 1;
while (true) {
synchronized (this) {
System.out.println("第" + (++index) + "次获取锁,这个锁是:" + this);
}
if (index == 10) {
break;
}
}
}
}).start();
}
}
public class WhatReentrant2 {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
new Thread(() -> {
try {
lock.lock();
System.out.println("第1次获取锁,这个锁是:" + lock);
int index = 1;
while (true) {
try {
lock.lock();
System.out.println("第" + (++index) +"次获取锁,这个锁是:" + lock);
try {
Thread.sleep(new Random().nextInt(200));
} catch (InterruptedException e) {
e.printStackTrace();
}
if (index == 10) {
break;
}
} finally {
lock.unlock();
}
}
} finally {
lock.unlock();
}
}).start();
}
}
public class Restaurant {
private Lock windows = new ReentrantLock();
public void getMeals() throws Exception {
try {
windows.lock();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "打饭");
} finally {
windows.unlock();
}
}
public void getSoup() throws Exception {
try {
windows.lock();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "打汤");
} finally {
windows.unlock();
}
}
public void today() throws Exception {
try {
windows.lock();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "打饭");
getMeals();
getSoup();
} finally {
windows.unlock();
}
}
public static void main(String[] args) {
Restaurant test = new Restaurant();
new Thread(() -> {
try {
test.today();
} catch (Exception e) {
e.printStackTrace();
}
}, "我").start();
new Thread(() -> {
try {
test.getSoup();
} catch (Exception e) {
e.printStackTrace();
}
}, "某人").start();
new Thread(() -> {
try {
test.getMeals();
} catch (Exception e) {
e.printStackTrace();
}
}, "另一个人").start();
}
}
输出:
我打饭
我打饭
我打汤
某人打汤
另一个人打饭
可以发现以上都没有发生死锁,可以多次获取相同的锁
可重入锁有:
- synchronized
- ReentrantLock
注意:
ReentrantLock和synchronized不一样,需要手动释放锁,所以使用ReentrantLock的时候一定要手动释放锁,并且加锁的次数和释放次数要一样
读写锁
- 为什么需要读写锁?
在多线程的环境下,对同一份数据进行读写,会涉及到线程安全问题。比如在一个线程读取数据的时候,另一个线程正在写数据,而导致前后数据的不一致性;一个线程在写数据的时候,另一个线程也在写数据,同样会导致数据的不一致性。
这时可以在读写方法中加入互斥锁,任何时候只能允许一个线程的一个读写操作,这样是可以解决问题,但是效率却大打折扣了。因为在真实的业务场景中,读取数据的操作次数通常高于写入数据的操作,而线程之间的读读操作不涉及线程安全问题,没有必要加入互斥锁,只要在读-写,写写期间上上锁即可。
读写锁机制:
- 读-读不互斥
- 读-写互斥
- 写-写互斥
public class ReadWriteLockTest {
private static ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
private static ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock(); //拿到读锁
private static ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock(); //写锁
public static void read() {
readLock.lock();
try{
System.out.println("read method ================");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}finally {
readLock.unlock();
}
}
// public static void read1() {
public static void write() {
writeLock.lock();
try{
System.out.println("read method >>>>>>>>>>>>>>>>>>>>>");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}finally {
writeLock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
ReadWriteLockTest.write();
}).start();
new Thread(() -> {
ReadWriteLockTest.read();
}).start();
Thread.sleep(10000);
}
}
网友评论