2019年互联网(1.5)
一.JUC多线程及并发包
1.5.公平锁/非公平锁/可重入锁/递归锁/自旋锁谈谈你的理解?请手写一个自旋锁
公平锁和非公平锁
公平锁 是指多个线程按照申请锁的顺序来获取锁类似排队打饭 先来后到
非公平锁 是指在多线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取到锁,在高并发的情况下,有可能造成优先级反转或者饥饿现象
两者的区别
公平锁/非公平锁 并发包ReentrantLock的创建可以指定构造函数的boolean类型来得到公平锁或者非公平锁 默认是非公平锁
Lock lock =new ReentrantLock(boolean b)//默认为非公平锁,false为非公平锁,true为公平锁
Java ReentrantLock而言,通过构造哈希数指定该锁是否是公平锁 默认是非公平锁 非公平锁的优点在于吞吐量必公平锁大.
对于synchronized而言 也是一种非公平锁.
可重入锁(又名递归锁)
可重入锁,指的是同一线程外层函数获取锁以后,内层递归函数任然会获取该锁代码,
再同一线程在外层方法获取锁的时候,在进入内层方法会自动获取锁
也就是说,线程可以进入任何一个它以及拥有的锁所同步着的代码块
参考8锁理解
ReentrantLock/synchronized就是一个典型的可重入锁
可重入锁最大的作用就是避免死锁
可重入锁 ReenterLockDemo_1_5_1
package com.ybzn._01.juc;
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: * 可重入锁(也叫做递归锁) * 指的是同一先生外层函数获得锁后,内层敌对函数任然能获取该锁的代码 * 在同一线程外外层方法获取锁的时候,在进入内层方法会自动获取锁 * * 也就是说,线程可以进入任何一个它已经标记的锁所同步的代码块 * * @author veliger@163.com * @date 2019-04-12 23:36
**/
public class ReenterLockDemo_1_5_1 {
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();
}
}
结果分析: 先输出线程A的所有内容,然后再输出线程B的所有函数
可重入锁ReenterLock_1_5_2.java
package com.ybzn._01.juc;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Phone_1 implements Runnable {
Lock lock = new ReentrantLock();
@Override
public void run () {
get();
}
private void get () {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "\tinvoked get()");
set();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
private void set () {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "\t invoked set()");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
/**
* @author Hugo
* @time 2021/2/14
* Description: * 可重入锁(也叫做递归锁) *
* 指的是同一先生外层函数获得锁后,内层敌对函数任然能获取该锁的代码 * 在同一线程外外层方法获取锁的时候,在进入内层方法会自动获取锁
* 也就是说,线程可以进入任何一个它已经标记的锁所同步的代码块
* Case two
* ReenterLock 可重入锁
*
*/
/** * * * @author veliger@163.com * @date 2019-04-12 23:36 **/
public class ReenterLockDemo_1_5_2 {
public static void main (String[] args) {
Phone_1 phone_1 = new Phone_1();
Thread thread3 = new Thread(phone_1, "t3");
Thread thread4 = new Thread(phone_1, "t4");
thread3.start();
thread4.start();
}
}
结果分析: 采用ReenterLock同样可以实现可重入锁
自旋锁
unsafe类+CAS思想(自旋)
spinlock:
指尝试获取锁的线程并不会立即阻塞,而是才用循环的方式去获取锁,这样的好处就是 减少了线程上下文之间的切换消耗,缺点是循环会消耗CPU
代码SpinLockDemo_1_6.java
package com.ybzn._01.juc;
import sun.awt.windows.ThemeReader;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
* @author Hugo
* @time 2021/2/15
* <p>
* 题目: 实现一个自旋锁
* 自旋锁的好处, 循环比较获取,直到成功为止,没有类型wait的阻塞
* 当CAS操作完成自旋锁,A线程先进来调用MyLock方法自己持有锁5秒中,B随后进来发现
* 当前有线程持有锁,不是null,所以只能通过自旋等待,直到A释放锁后,B才随后抢到
*/
public class SpinLockDemo_1_6 {
// 原子 引用线程
static AtomicReference <Thread> threadAtomic = new AtomicReference <>();
public void myLock () {
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName() + " \t come in");
while (!threadAtomic.compareAndSet(null, thread)) {
}
}
public void myUnlock () {
Thread thread = Thread.currentThread();
boolean b = threadAtomic.compareAndSet(thread, null);
System.out.println(Thread.currentThread().getName() + " \t invoked myUnlock()");
}
public static void main (String[] args) {
SpinLockDemo_1_6 demo16 = new SpinLockDemo_1_6();
new Thread(() -> {
demo16.myLock();
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
demo16.myUnlock();
}, "t1").start();
try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace(); }
new Thread(() ->
{
demo16.myLock();
demo16.myUnlock();
}, "t2").start();
}
}
独占锁(写)/共享锁(读)/互斥锁
多给线程同时读一个资源类没有任何问题,所以为了满洲并发量,读取共享资源可以同时进行,
但是
如果又一个线程想去写共享资源,就不应该再有其他线程可以对该资源进行读或者写
小总结:
-
读-读 可以共存
-
读-写 不能共存
-
写-写 不能共存
代码演示ReadWriteLockDemo_1_6_2.java