文章目录
Lock (Synchronized)
在Lock接口出现之前,Java中的应用程序对于多线程的并发安全处理只能基于synchronized关键字来解决。但是synchronized在有些场景中会存在一些短板,也就是它并不适合于所有的并发场景。但是在 Java5以后,Lock的出现可以解决synchronized在某些场景中的短板,它比synchronized更加灵活。
public class AtomicDemo {
private static int count=0;
static Lock lock=new ReentrantLock();
public static void inc(){
lock.lock(); //获得锁(互斥锁)
try {
Thread.sleep(1);
count++;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();//释放锁
}
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 1000; i++) {
new Thread(()->AtomicDemo.inc()).start();
}
Thread.sleep(4000);
System.out.println("result:"+count);
}
}
输出结果:
result:1000
ReentrantLock(重入锁)
ReentrantLock是一种互斥锁。
重入锁:已经获得锁的线程,在不释放锁的前提下可以再次获得锁。
例如:
public class AtomicDemo {
private static int count=0;
//重入锁(如何实现的?)
static Lock lock=new ReentrantLock(true);
public static void inc(){
lock.lock(); //获得锁(互斥锁) ThreadA 获得了锁
try {
Thread.sleep(1);
count++;
decr();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();//释放锁 ThreadA释放锁 state=1-1=0
}
}
public static void decr(){
lock.lock(); //state=2 //ThreadA再次来抢占锁 : 不需要再次抢占锁,而是只增加重入的次数
try{
count--;
}finally {
lock.unlock(); //state=1
}
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 1000; i++) {
new Thread(()->AtomicDemo.inc()).start();
}
Thread.sleep(4000);
System.out.println("result:"+count);
}
}
假如锁不能重入,ThreadA在调用inc()方法时,将会出现死锁。因为A第一次已经获得了锁,在调用decr()时候如果获取不到锁就会发生阻塞,从而死锁。
ReentrantReadWriteLock(重入读写锁)
适用于读多写少的情况下.
1、读和读不互斥
2、读和写互斥
3、写和写互斥
ReentrantReadWriteLock里面维护了2把锁,一把读锁,一把写锁。
public class Demo {
static Map<String,Object> cacheMap=new HashMap<>();
static ReentrantReadWriteLock rwl=new ReentrantReadWriteLock();
static Lock read=rwl.readLock();
static Lock write=rwl.writeLock();
public static Object get(String key){
read.lock(); //读锁 ThreadA 阻塞
try{
return cacheMap.get(key);
}finally {
read.unlock(); //释放读锁
}
}
public static Object write(String key,Object value){
write.lock(); //Other Thread 获得了写锁
try{
return cacheMap.put(key,value);
}finally {
write.unlock();
}
}
}
StampedLock
读多写少的情况下. 读和读不互斥 读和写互斥 写和写互斥
思考锁的实现(设计思维)
{互斥}
1、锁的互斥特性 -> 共享资源()-> 标记 (0 无锁, 1代表有锁)
2、没有抢占到锁的线程?-> 释放CPU资源 , [等待 -> 唤醒]
3、等待的线程怎么存储? -> 数据结构去存储一些列等待中的线程,FIFO (等待队列) 4、公平和非公平(能否插队),synchronized是一个非公平锁
5、重入的特性(识别是否是同一个人?ThreadID)
{技术方案}
1、volatile state =0 (无锁) , 1代表是持有锁 , >1代表重入
2、wait/notify | condition 无法唤醒指定线程。
而需要唤醒指定线程。[LockSupport.park(); ->unpark(thread)] unsafe类中提供的一个方法。
3、双向链表
4、逻辑层面去实现
5、在某一个地方存储当前获得锁的线程的ID,判断下次抢占锁的线程是否为同一个。