目录
公平锁/非公平锁
两者区别
可重入锁(也叫递归锁)
理论知识
实例(synchronized)
实例(ReentrantLock)
自旋锁
实例
独占锁(写锁)/共享锁(读锁)/互斥锁
公平锁:是指多个线程按照申请锁的顺序来获取锁,类似于排队打饭,先来后到
非公平锁:是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁,在高并发的情况下,有可能会造成优先级反转或者饥饿现象。
公平锁:就是很公平,在并发环境下,每个线程在获取锁时会先查看此锁维护的等待队列,如果为空,或者当前线程是等待队列的第一个,就占有锁,否则会加入到等待队列中,以后会按照FIFO的规则从队列中取到自己
非公平锁:比较粗鲁,上来就直接尝试占有锁,如果尝试失败,就采用类似公平锁那种方式。
非公平锁的优点在于吞吐量比公平锁大
并发包中ReentrantLock的创建可以指定构造函数的boolean类型来得到公平锁,默认是非公平锁,
对于Synchronized而言,也是一种非公平锁
指同一个线程外层函数获得锁之后,内层递归函数任然能获取该锁的代码,在同一个线程的外层方法获取锁的时候,在进入内层方法会自动获取锁。也就是说,线程可以进入任何一个它已经拥有的锁所同步着的代码块。
public class Phone {
public synchronized void sendSMS() throws Exception
{
System.out.println(Thread.currentThread().getName()+" invoked send SMS");
sendEmail();
}
public synchronized void sendEmail() throws Exception
{
//睡五秒
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName()+" invoked send Email");
}
public static void main(String [] args){
Phone phone = new Phone();
new Thread(()->{
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
},"t2").start();
new Thread(()->{
try {
phone.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
},"t1").start();
}
}
停顿5s
t2 invoked send Email
t1 invoked send SMS
停頓5s
t1 invoked send Email
public class Phone {
Lock lock= new ReentrantLock();
public void sendSMS() throws Exception
{
try{
lock.lock();
System.out.println(Thread.currentThread().getName()+" invoked send SMS");
sendEmail();
}catch(Exception e){
e.getMessage();
}
finally {
lock.unlock();
}
}
public void sendEmail() throws Exception
{
try{
lock.lock();
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName()+" invoked send Email");
}catch(Exception e){
e.getMessage();
}
finally {
lock.unlock();
}
//睡五秒
}
public static void main(String [] args){
Phone phone = new Phone();
new Thread(()->{
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
},"t2").start();
new Thread(()->{
try {
phone.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
},"t1").start();
}
}
停顿5s
t2 invoked send Email
t1 invoked send SMS
停頓5s
t1 invoked send Email
Unsafe类加上CAS思想就是自旋锁
自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式尝试获取锁,这样的好处在于减少线程上下文切换的消耗,缺点是循环会消耗CPU。
public class SpinLockDemo {
AtomicReference atomicReference=new AtomicReference<>();
public void myLock(){
Thread thread= Thread.currentThread();
System.out.println(Thread.currentThread().getName()+ " "+ thread.toString()+" come in 000000");
while(!atomicReference.compareAndSet(null,thread)){
}
System.out.println(Thread.currentThread().getName()+ " "+ thread.toString()+" get lock 000000");
}
public void myUnLock(){
Thread thread= Thread.currentThread();
atomicReference.compareAndSet(thread,null);
System.out.println(Thread.currentThread().getName()+ " "+ thread.toString()+ " invoke myUnLock");
}
public static void main(String [] args){
SpinLockDemo spinLockDemo = new SpinLockDemo();
new Thread(()->{
spinLockDemo.myLock();
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
spinLockDemo.myUnLock();
},"AA").start();
//保证AA线程先启动
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
spinLockDemo.myLock();
spinLockDemo.myUnLock();
},"BB").start();
}
}
AA Thread[AA,5,main] come in 000000
AA Thread[AA,5,main] get lock 000000
BB Thread[BB,5,main] come in 000000
AA Thread[AA,5,main] invoke myUnLock
BB Thread[BB,5,main] get lock 000000
BB Thread[BB,5,main] invoke myUnLock
独占锁:指该锁一次只能被一个线程持有,对ReentrantLock和Synchronized 而言都是独占锁
共享锁:指该锁可以被多个线程所持有。
对ReentrantReadWriteLock 其读锁是共享锁,其写锁是独占锁。
读锁的共享锁可以保证并发读是非常高效的,读写,写写,写读的过程都是互斥的。
Before
/**
* Tips:
*
* @author Liuq
* @version 2019年10月21日
*/
public class ReadWriteLockDemo {
public static void main(String [] args){
MyCache myCache = new MyCache();
for(int i = 0; i < 5; i++) {
final int tempInt=i;
new Thread(()->{
myCache.put(tempInt+ " ",tempInt+ " ");
},String.valueOf(i)).start();
}
for(int i = 0; i < 5; i++) {
final int tempInt=i;
new Thread(()->{
myCache.get(tempInt+ " ");
},String.valueOf(i)).start();
}
}
}
class MyCache{
private volatile Map map=new HashMap<>();
public void put(String key ,Object value){
System.out.println(Thread.currentThread().getName() +" 正在写入:"+key);
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
map.put(key,value);
System.out.println(Thread.currentThread().getName() +" 写入完成");
}
public void get(String key){
System.out.println(Thread.currentThread().getName() +" 正在读取:"+key);
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
Object o = map.get(key);
System.out.println(Thread.currentThread().getName() +" 读取完成:"+o);
}
}
After
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* Tips:
*
* @author Liuq
* @version 2019年10月21日
*/
public class ReadWriteLockDemo {
public static void main(String [] args){
MyCache myCache = new MyCache();
for(int i = 0; i < 5; i++) {
final int tempInt=i;
new Thread(()->{
myCache.put(tempInt+ " ",tempInt+ " ");
},String.valueOf(i)).start();
}
for(int i = 0; i < 5; i++) {
final int tempInt=i;
new Thread(()->{
myCache.get(tempInt+ " ");
},String.valueOf(i)).start();
}
}
}
class MyCache{
private ReentrantReadWriteLock rwLock=new ReentrantReadWriteLock();
private volatile Map map=new HashMap<>();
public void put(String key ,Object value){
rwLock.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 {
rwLock.writeLock().unlock();
}
}
public void get(String key){
rwLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() +" 正在读取:"+key);
TimeUnit.MILLISECONDS.sleep(300);
Object o = map.get(key);
System.out.println(Thread.currentThread().getName() +" 读取完成:"+o);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
rwLock.readLock().unlock();
}
}
}
如果读写线程换一个位子就是这样的结果
public static void main(String [] args){
MyCache myCache = new MyCache();
for(int i = 0; i < 5; i++) {
final int tempInt=i;
new Thread(()->{
myCache.get(tempInt+ " ");
},String.valueOf(i)).start();
}
for(int i = 0; i < 5; i++) {
final int tempInt=i;
new Thread(()->{
myCache.put(tempInt+ " ",tempInt+ " ");
},String.valueOf(i)).start();
}
}