1.公平锁和非公平锁
(1) 是什么?
公平锁: 是指多个线程按照申请锁的顺序来获取锁,类似排队打饭,先来后到
Lock lock=new ReentrantLock(true);
非公平锁:是指多个线程获取锁的顺序不是按照申请锁的顺序,有肯可能后申请的线程比先申请的线程优先获取锁,在高并发情况下,有可能会造成优先级翻转或者饥饿现象
Lock lock=new ReentrantLock(); //ReentrantLock默认是非公平锁
(2)两者区别
并发包(JUC)中ReentrantLock的创建可以指定构造函数的boolean类型来得到公平锁和非公平锁,默认是非公平锁。
两者区别:
公平锁:就是很公平,在并发环境中,每个线程在获取锁是会先查看此锁维护的等待队列,如果为空,或者当前线程是等待队列的第一个,就占有锁,否则就会加入到等待队列中,以后会按照FIFO的规则从队列中取到自己
非公平锁:非公平锁比较粗鲁,上来就尝试占有锁,如果尝试失败,就在采用类似公平锁那种方式
(3)优缺点
非公平锁有点在于吞吐量比公平锁大
对于Synchronized也是一种公平锁
2.可重入锁(有名递归锁)
(1)是什么?
可重入锁又叫递归锁,指的是同一个线程外层函数获得锁之后,内存递归函数容然能够获取到该锁的代码,在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。也就是说,线程可以进入任何一个他拥有的锁所同步着的代码块
(2)ReentrantLock和Synchronized就是典型的可重入锁
(3).案例
synchronized案例
package register;
class Phone{
public synchronized void sendMsg() throws Exception{
System.out.println(Thread.currentThread().getName()+"\t invoked sendMsg");
sendEmail();
}
public synchronized void sendEmail() throws Exception{
System.out.println(Thread.currentThread().getName()+"\t invoked sendEmail");
}
}
public class ReenterLockDemo {
public static void main(String[] args) {
Phone phone=new Phone();
new Thread(()->{
try{
phone.sendMsg();
}catch (Exception e){
e.printStackTrace();
}
},"t1").start();
new Thread(()->{
try{
phone.sendMsg();
}catch (Exception e){
e.printStackTrace();
}
},"t2").start();
}
}
//输出信息
t1 invoked sendMsg
t1 invoked sendEmail
t2 invoked sendMsg
t2 invoked sendEmail
ReentrantLock案例
class Phone implements Runnable{
public void run(){
get();
}
Lock lock=new ReentrantLock();
public void get(){
lock.lock();
try{
System.out.println(Thread.currentThread().getName()+"\t invoked get()");
set();
}finally {
lock.unlock();
}
}
public void set(){
lock.lock();
try{
System.out.println(Thread.currentThread().getName()+"\t invoked set()");
}finally {
lock.unlock();
}
}
}
public class ReenterLockDemo {
public static void main(String[] args) {
Phone phone=new Phone();
Thread t3=new Thread(phone,"t3");
Thread t4=new Thread(phone,"t4");
t3.start();
t4.start();
}
}
3.自旋锁
(1)是什么?
是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU
(2)请手写一个自旋锁
package register;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
public class SpinLockDemo {
//原子引用线程
AtomicReference<Thread> atomicReference=new AtomicReference();
public void myLoack(){
Thread thread=Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"\t come in ");
while (!atomicReference.compareAndSet(null,thread)){
}
}
public void unLock(){
Thread thread=Thread.currentThread();
atomicReference.compareAndSet(thread,null);
System.out.println(Thread.currentThread().getName()+"\t invoked myLock()");
}
public static void main(String[] args) {
SpinLockDemo demo=new SpinLockDemo();
new Thread(()->{
demo.myLoack();
try {TimeUnit.SECONDS.sleep(5);}catch (Exception e){e.printStackTrace();}
demo.unLock();
},"AA").start();
try {TimeUnit.SECONDS.sleep(1);}catch (Exception e){e.printStackTrace();}
new Thread(()->{
demo.myLoack();
try {TimeUnit.SECONDS.sleep(1);}catch (Exception e){e.printStackTrace();}
demo.unLock();
},"BB").start();
}
}
4.独占锁(写锁)/共享锁(读锁)/互斥锁
(1)是什么?
独占锁:指改锁一次只能被一个线程所持有。synchronized和ReentrantLock都是独占锁
共享锁:指该锁可以被多个线程持有。
互斥锁:ReentrantReadWriteLock其读锁是共享锁,其写锁是独占锁
//未加锁
class MyCache{
private volatile Map<String,Object> map=new HashMap<>();
public void put(String key,Object object){
System.out.println(Thread.currentThread().getName()+"\t 正在写入"+key);
try {TimeUnit.MICROSECONDS.sleep(2000);}catch (Exception e){e.printStackTrace();}
map.put(key,object);
System.out.println(Thread.currentThread().getName()+"\t 写入完成");
}
public void get(String key){
System.out.println(Thread.currentThread().getName()+"\t 正在读取");
try {TimeUnit.MICROSECONDS.sleep(2000);}catch (Exception e){e.printStackTrace();}
Object result=map.get(key);
System.out.println(Thread.currentThread().getName()+"\t 读取完成"+result);
}
}
/**
* 读-读能共存
* 读-写不能共存
* 写-写不能共存
*
* 写操作:原子+独占,整个过程必须是一个完整的统一体,中间不能被分隔,被打断
*/
public class ReentrantReadWriteDemo {
public static void main(String[] args) {
MyCache myCache=new MyCache();
for (int i = 1; i <=5; i++) {
final int tempInt=i;
new Thread(()->{
myCache.put(tempInt+"",""+tempInt);
},"t"+i).start();
}
for (int i = 1; i <=5; i++) {
final int tempInt=i;
new Thread(()->{
myCache.get(tempInt+"");
},"t"+i).start();
}
}
}
//输出信息
t1 正在写入1
t4 正在写入4
t2 正在写入2
t3 正在写入3
t5 正在写入5
t1 正在读取
t2 正在读取
t4 正在读取
t5 正在读取
t3 正在读取
t5 写入完成
t1 读取完成1
t4 写入完成
t3 写入完成
t1 写入完成
t4 读取完成null
t2 写入完成
t2 读取完成null
t3 读取完成null
t5 读取完成null
//加锁
class MyCache{
private volatile Map<String,Object> map=new HashMap<>();
ReentrantReadWriteLock rwLock=new ReentrantReadWriteLock();
public void put(String key,Object object){
rwLock.writeLock().lock();
try{
System.out.println(Thread.currentThread().getName()+"\t 正在写入"+key);
try {TimeUnit.MICROSECONDS.sleep(2000);}catch (Exception e){e.printStackTrace();}
map.put(key,object);
System.out.println(Thread.currentThread().getName()+"\t 写入完成");
}catch (Exception e){
e.printStackTrace();
}finally {
rwLock.writeLock().unlock();
}
}
public void get(String key){
rwLock.readLock().lock();
try{
System.out.println(Thread.currentThread().getName()+"\t 正在读取");
try {TimeUnit.MICROSECONDS.sleep(2000);}catch (Exception e){e.printStackTrace();}
Object result=map.get(key);
System.out.println(Thread.currentThread().getName()+"\t 读取完成"+result);
}catch (Exception e){
e.printStackTrace();
}finally {
rwLock.readLock().unlock();
}
}
}
/**
* 读-读能共存
* 读-写不能共存
* 写-写不能共存
*
* 写操作:原子+独占,整个过程必须是一个完整的统一体,中间不能被分隔,被打断
*/
public class ReentrantReadWriteDemo {
public static void main(String[] args) {
MyCache myCache=new MyCache();
for (int i = 1; i <=5; i++) {
final int tempInt=i;
new Thread(()->{
myCache.put(tempInt+"",""+tempInt);
},"t"+i).start();
}
for (int i = 1; i <=5; i++) {
final int tempInt=i;
new Thread(()->{
myCache.get(tempInt+"");
},"t"+i).start();
}
}
}
//输出信息
t2 正在写入2
t2 写入完成
t1 正在写入1
t1 写入完成
t3 正在写入3
t3 写入完成
t4 正在写入4
t4 写入完成
t5 正在写入5
t5 写入完成
t1 正在读取
t2 正在读取
t3 正在读取
t5 正在读取
t4 正在读取
t1 读取完成1
t2 读取完成2
t3 读取完成3
t4 读取完成4
t5 读取完成5