1.1 可重入锁
-
synchronized就是一个可重入锁
-
使用lock时,常用的ReentryLock就是可重入锁
-
当一个线程在获得a对象锁之后,可以继续重复获得对象锁
-
代码形式就是 线程调用同步代码段,在没有执行完毕前,又调用了该对象的另一个同步代码段
public class Test4 {
public static void main(String[] args) {
new ReentrantLock().unlock();
Thread t1 = new Thread(()->{
t1(); // 0 - 1
});
t1.start();
}
public static synchronized void t1(){
t2(); // 1 - 2
} // 1 - 0
public static synchronized void t2(){
} //2 - 1
}
-
当线程1进入同步代码段时,会记录线程1的信息,表示线程1获得了锁,同时锁状态0 - 1
-
当线程1重复进入需要当前对象锁的同步代码段时,会检测之前检录的线程信息
如果之前记录的线程信息和当前进入的线程信息相同,说明重入,允许,同时状态1 - 2
如果之前记录的线程信息和当前进入的线程信息不同,说明新线程访问,加入同步队列等待。
-
当前线程离开同步代码段时,会释放锁,本质就是状态 2 - 1 或 1 - 0
注意:
-
针对于Lock锁,加锁的数量和释放锁的数量应该保持相同。
-
否则会产生死锁。
-
synchronized 和 lock都可能会产生死锁
-
synchronized 两个线程同时需要彼此所占有的资源
-
lock最常见的死锁情况,就是获得锁后忘释放了。
-
1.2 公平锁和非公平锁
-
synchronized 属于非公平锁
-
Lock之ReentryLock创建锁对象时,可以通过传参指定公平或非公平。默认是非公平锁
new ReentryLock(true);//公平锁
new ReentryLock(false);//非平锁
-
假设, 有n个线程同时访问同步资源,只有1个线程可以获得锁,其他n-1个线程进入同步队列,等待
-
当然,这n-1个线程,进入同步队列后,会有一个先后顺序
-
当第一个线程释放锁资源后, 同步队列中那n-1个线程,会按顺序依次获得锁 (倒序 或 顺序)
-
当线程1释放锁的同时,又来了一个新线程要想要争抢锁
-
这个新线程,直接存于锁的争抢, 就称非公平锁
-
这个新线程,直接进入同步队列,等待前面的线程释放锁。 就称公平锁
-
1.3 排它锁 和 共享锁
-
排它锁又称为 互斥锁,也称为 独享锁,当一个线程获得该锁, 其他线程无法获得锁,需要等待
-
synchronized就是一个排它锁
-
Lock中提供了不同的实现
-
-
共享锁, 多个线程可以同时获得这个锁。
-
在Lock应用当中,有一个读写锁,就是对排它锁和共享锁的使用
-
读写就是 共享锁
-
写锁就是 排它锁
-
ReentrantReadWriteLock tool = new ReentrantReadWriteLock();
Lock lock1 = tool.readLock();
Lock lock2 = tool.writeLock();
-
两个线程可以同时获得读锁
-
两个线程只能有一个获得写锁,另一个等待
-
一个线程获得读锁, 另一个线程尝试获得写锁会失败,进入等待状态。
-
一个线程获得写锁, 另一个线程尝试获得读锁会失败,进入等待状态
public class Test5 {
public static void main(String[] args) throws InterruptedException {
ReentrantReadWriteLock tool = new ReentrantReadWriteLock();
Lock lock1 = tool.readLock();
Lock lock2 = tool.writeLock();
Thread t1 = new Thread(()->{
lock1.lock();
for(int i=1;i<=5;i++){
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
lock1.unlock();
});
Thread t2 = new Thread(()->{
lock2.lock();
for(int i=11;i<=15;i++){
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
lock2.unlock();
});
t2.start();
Thread.sleep(10);
t1.start();
}
}