公平锁、非公平锁
所谓公平锁,就是多个线程按照申请锁的顺序来获取锁,类似排队,先到先得。而非公平锁,则是多个线程抢夺锁,会导致优先级反转或饥饿现象。
区别:公平锁在获取锁时先查看此锁维护的等待队列,为空或者当前线程是等待队列的队首,则直接占有锁,否则插入到等待队列,FIFO原则。非公平锁比较则不然,上来直接先尝试占有锁,失败则采用公平锁方式。非公平锁的优点是吞吐量比公平锁更大。
其中synchronized
和ReentrantLock
默认都是非公平锁,ReentrantLock
可以通过在构造中传入true而变成公平锁。
可重入锁
指同一个线程在外层方法获得锁时,进入内层方法会自动获得锁。也就是说,线程可以进入任何一个它已经拥有锁的代码块。
可重入锁能避免死锁
举例如下:
public class ReentrantLockDemo {
public static void main(String[] args) {
ReentrantLockDemo demo = new ReentrantLockDemo();
new Thread(() -> {
try {
demo.openDoor();
Thread.sleep(2*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "T1").start();
new Thread(() -> {
try {
demo.openDoor();
Thread.sleep(2*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "T2").start();
}
public synchronized void openDoor() {
System.out.println(Thread.currentThread().getName()+"打开家门");
sit();
}
public synchronized void sit() {
System.out.println(Thread.currentThread().getName()+"坐下休息");
}
}
输出结果:
T1打开家门
T1坐下休息
T2打开家门
T2坐下休息
线程在外层方法openDoor
获得锁时,进入内层方法sit
也会自动获取锁。
其中synchronized
和ReentrantLock
都是可重入锁。
自旋锁
所谓自旋锁,就是尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取。自己在那儿一直循环获取,就像“自旋”一样。这样的好处是减少线程切换的上下文开销,缺点是会消耗CPU。之前介绍的CAS,内部就是自旋锁实现的。
读写锁、独占锁、共享锁
独占锁是指一个锁只能被一个线程所持有。其中ReentrantLock
和synchronized
都是独占锁。
读写锁即ReentrantReadWriteLock
,内含读锁和写锁,读锁是共享的,写锁是独占的。
举例说明:
public class ReentrantReadWriteLockDemo {
private volatile Map<String, String> map = new HashMap<>();
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public static void main(String[] args) {
ReentrantReadWriteLockDemo demo = new ReentrantReadWriteLockDemo();
for (int i = 1; i <= 5; i++) {
final int tempInt = i;
new Thread(() -> demo.write(tempInt + "", tempInt + ""), String.valueOf(i)).start();
}
for (int i = 1; i <= 5; i++) {
final int tempInt = i;
new Thread(() -> demo.read(tempInt + ""), String.valueOf(i)).start();
}
}
public void read(String key) {
ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
try {
readLock.lock();
Thread.sleep(1 * 1000);
System.out.println("线程 " + Thread.currentThread().getName() + "读取数据:" + map.get(key));
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
readLock.unlock();
}
}
public void write(String key, String value) {
ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
try {
System.out.println("线程 " + Thread.currentThread().getName() + " \t" + "正在写入: " + key);
writeLock.lock();
Thread.sleep(1 * 1000);
map.put(key, value);
System.out.println("线程 " + Thread.currentThread().getName() + "\t" + "写入完成");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
writeLock.unlock();
}
}
}
输出结果:
线程 1 正在写入: 1
线程 2 正在写入: 2
线程 3 正在写入: 3
线程 4 正在写入: 4
线程 5 正在写入: 5
线程 1 写入完成
线程 2 写入完成
线程 3 写入完成
线程 4 写入完成
线程 5 写入完成
线程 1读取数据:1
线程 2读取数据:2
线程 3读取数据:3
线程 4读取数据:4
线程 5读取数据:5