公平锁和非公平锁
- 公平锁
是指多个线程按照申请锁的顺序来获取锁类似排队打饭 先来后到 - 非公平锁
是指在多线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取到锁,在高并发的情况下,有可能造成优先级反转或者饥饿现象 - 区别
可重入锁(又名递归锁)
是啥:![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/6abb9e576ab185439e038b2b185dba47.png)
实例:
ReentrantLock/synchronized就是一个典型的可重入锁
作用:
可重入锁最大的作用就是避免死锁
Demo
package cn.atguigu.interview.study.thread;
class Phone{
public synchronized void sendSms() throws Exception{
System.out.println(Thread.currentThread().getName()+"\tsendSms");
sendEmail();
}
public synchronized void sendEmail() throws Exception{
System.out.println(Thread.currentThread().getName()+"\tsendEmail");
}
}
/**
* Description:
* 可重入锁(也叫做递归锁)
* 指的是同一先生外层函数获得锁后,内层敌对函数任然能获取该锁的代码
* 在同一线程外外层方法获取锁的时候,在进入内层方法会自动获取锁
*
* 也就是说,线程可以进入任何一个它已经标记的锁所同步的代码块
*
**/
public class ReenterLockDemo {
/**
* t1 sendSms
* t1 sendEmail
* t2 sendSms
* t2 sendEmail
* @param args
*/
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
try {
phone.sendSms();
} catch (Exception e) {
e.printStackTrace();
}
},"t1").start();
new Thread(()->{
try {
phone.sendSms();
} catch (Exception e) {
e.printStackTrace();
}
},"t2").start();
}
}
自旋锁
独占锁(写)/共享锁(读)/互斥锁
Demo
/**
* 资源类
*/
class MyCaChe {
/**
* 保证可见性
*/
private volatile Map map = new HashMap();
private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
/**
* 写
*
* @param key
* @param value
*/
public void put(String key, Object value) {
reentrantReadWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "\t正在写入" + key);
//模拟网络延时
try {
TimeUnit.MICROSECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "\t正在完成");
} finally {
reentrantReadWriteLock.writeLock().unlock();
}
}
/**
* 读
*
* @param key
*/
public void get(String key) {
reentrantReadWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "\t正在读取");
//模拟网络延时
try {
TimeUnit.MICROSECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
Object result = map.get(key);
System.out.println(Thread.currentThread().getName() + "\t正在完成" + result);
} finally {
reentrantReadWriteLock.readLock().unlock();
}
}
public void clearCaChe() {
map.clear();
}
}
/**
* Description:
* 多个线程同时操作 一个资源类没有任何问题 所以为了满足并发量
* 读取共享资源应该可以同时进行
* 但是
* 如果有一个线程想去写共享资源来 就不应该有其他线程可以对资源进行读或写
*
* 小总结:
* 读 读能共存
* 读 写不能共存
* 写 写不能共存
* 写操作 原子+独占 整个过程必须是一个完成的统一整体 中间不允许被分割 被打断
*
* @author veliger@163.com
* @date 2019-04-13 0:45
**/
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCaChe myCaChe = new MyCaChe();
for (int i = 1; i 5; i++) {
final int temp = i;
new Thread(() -> {
myCaChe.put(temp + "", temp);
}, String.valueOf(i)).start();
}
for (int i = 1; i 5; i++) {
int finalI = i;
new Thread(() -> {
myCaChe.get(finalI + "");
}, String.valueOf(i)).start();
}
}
}
读写锁
ReadWriteLock 是JDK5中提供的读写分离锁。读写锁能有效的帮助减少锁竞争,以提升系统性能。用锁分离的机制来提升性能非常容易理解,比如县城A1、A2、A3进行写操作,线程B1、B2、B3进行读操作,如果使用重入锁或者内部锁,从理论上说所有读之间、读和写之间、写和写之间都是串行操作。当B1进行读时,B2、B3则需要等待锁。由于读操作并不对数据的完整性造成破坏,这种等待显然是不合理的。因此读写所就用了发挥的余地。
在这种情况下,读写所容许多个线程同时读,是的B1、B2、B3之间并行。但是,考虑到数据完整性,写写,和读写操作依然是需要相互等待和持有锁的。总的来说读写锁约束访问情况如下表
读 | 写 | |
---|---|---|
读 | 非阻塞 | 阻塞 |
写 | 阻塞 | 阻塞 |
- 读-读不互斥:读读之间不阻塞。
- 读-写互斥:读阻塞写,写也会阻塞读。
- 写-写互斥:写写阻塞。