JAVA锁
公平锁和非公平锁
//默认是非公平锁,可以设置为公平锁
Lock lock = new ReentrantLock();
public ReentrantLock() {
sync = new NonfairSync();
}
公平锁:多个线程按照申请的顺序获取锁
非公平锁:多个线程获取锁的顺序并不是按照申请锁的顺序,有可能出现后申请的线程比先申请的线程优先获取到锁,上来就尝试占有锁,如果失败,排到队尾
synchronized 也是非公平锁
可重入锁和递归锁
可重入锁就是递归锁。
同一线程,外层函数获取到锁之后,内层函数仍然能获取到该锁
ReentrantLock 和 synchronized 都是可重入锁
可重入锁的优点:可以避免死锁
public static void main(String[] args) {
Phone phone = new Phone();
for (int i = 0; i < 10; i++) {
new Thread(()->{
phone.call();
}).start();
}
}
class Phone {
//外层同步方法
public synchronized void call(){
System.out.println(Thread.currentThread().getId()+"线程调用call方法");
//内层同步方法
senMessage();
}
public synchronized void senMessage(){
System.out.println(Thread.currentThread().getId()+"线程调用senMessage方法");
}
}
自旋锁
线程不会立即阻塞,
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
//这个其实就是一个自旋锁
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
手写一个自旋锁
public class SpinLock {
//原子引用的线程
AtomicReference<Thread> atomicReference = new AtomicReference<Thread>();
public void lock() {
Thread thread = Thread.currentThread();
//没有设置成功就会一直自旋
do {
System.out.println(Thread.currentThread().getName() + " try lock....");
//期望是null,就把当前的线程设置进去
} while (!atomicReference.compareAndSet(null, thread));
System.out.println(Thread.currentThread().getName() + " invoke lock method");
}
public void unLock() {
Thread thread = Thread.currentThread();
atomicReference.compareAndSet(thread, null);
System.out.println(Thread.currentThread().getName() + " invoke unLock method");
}
public static void main(String[] args) {
SpinLock spinLock = new SpinLock();
new Thread(() -> {
spinLock.lock();
// 获取到锁后 sleep 5秒钟
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 5秒钟之后释放锁
spinLock.unLock();
}, "AAA").start();
//保证AAA线程第一次获取锁
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
// 1秒钟之后BBB线程进来,一直尝试获取锁,直到AAA五秒钟之后释放锁才能获取到锁
spinLock.lock();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
//获取到锁1秒钟之后释放锁
spinLock.unLock();
}, "BBB").start();
}
}
自旋锁如果AAA线程执行的时间较长的话,BBB线程会一直尝试获取锁,从而消耗CPU的性能
读写锁
ReentrantReadWriteLock 读:共享锁 写:互斥锁
手写MyCache
public static void main(String[] args) throws InterruptedException {
MyCache myCache = new MyCache();
for (int i = 0; i < 10; i++) {
String finalI = String.valueOf(i);
new Thread(() -> {
myCache.add(finalI, UUID.randomUUID().toString());
}).start();
}
TimeUnit.SECONDS.sleep(5);
for (int i = 0; i < 10; i++) {
String finalI = String.valueOf(i);
new Thread(() -> {
myCache.get(finalI);
}).start();
}
}
class MyCache {
private volatile Map<String, Object> map = new HashMap<>();
//读写锁
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void add(String key, String value) {
//写锁
lock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "正在进行写操作:" + key);
TimeUnit.MILLISECONDS.sleep(300);
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "写入完成");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.writeLock().unlock();
}
}
public void get(String key) {
//读锁
lock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "正在进行读操作");
TimeUnit.MILLISECONDS.sleep(300);
Object o = map.get(key);
System.out.println(Thread.currentThread().getName() + "读取完成" + o);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.readLock().unlock();
}
}
}
执行结果:
Thread-0正在进行写操作:0
Thread-0写入完成
Thread-4正在进行写操作:4
Thread-4写入完成
Thread-7正在进行写操作:7
Thread-7写入完成
Thread-5正在进行写操作:5
Thread-5写入完成
Thread-6正在进行写操作:6
Thread-6写入完成
Thread-3正在进行写操作:3
Thread-3写入完成
Thread-2正在进行写操作:2
Thread-2写入完成
Thread-9正在进行写操作:9
Thread-9写入完成
Thread-1正在进行写操作:1
Thread-1写入完成
Thread-8正在进行写操作:8
Thread-8写入完成
Thread-10正在进行读操作
Thread-13正在进行读操作
Thread-15正在进行读操作
Thread-19正在进行读操作
Thread-18正在进行读操作
Thread-11正在进行读操作
Thread-17正在进行读操作
Thread-14正在进行读操作
Thread-12正在进行读操作
Thread-16正在进行读操作
Thread-19读取完成0b0f8d36-ec36-4877-9cac-d9f37059ea71
Thread-12读取完成1a502d57-f383-489f-9d33-c4c17017c35e
Thread-18读取完成6fae0d7e-9427-477b-9ace-c85f73d20fef
Thread-14读取完成a9816ce5-4e78-4c66-988b-a2faaeff7a0b
Thread-17读取完成da6509ba-1c38-4b94-b9e2-3ab50f0c4d85
Thread-11读取完成abdf55fc-7866-4a70-8f97-d9569c49cad1
Thread-13读取完成647bb4ca-ed09-4d71-8cce-2ca87d48a01d
Thread-15读取完成91f3c60c-bcee-4539-9b61-50521073989b
Thread-10读取完成f8d43475-afa2-41e7-971b-c34969237d3c
Thread-16读取完成3a74c83b-8d1b-4298-9c22-315741b5fe4c
写操作始终保证同一个线程