并发编程---java锁
创始人
2024-06-01 01:09:28
0

java锁

  • 一 多线程锁synchronized案例分析
    • 1.1synchronized介绍
    • 1.2 synchronized案例分析
      • 1.2.1.标准访问,请问先打印邮件还是短信?
      • 1.2.2.邮件⽅法暂停4秒钟,请问先打印邮件还是短信?
        • 分析
      • 1.2.3.新增⼀个普通⽅法hello(),请问先打印邮件还是hello?
      • 1.2.4.两部⼿机,请问先打印邮件还是短信?
      • 1.2.5.两个静态同步⽅法,同⼀部⼿机,请问先打印邮件还是短信?
      • 1.2.6.两个静态同步⽅法,2部⼿机,请问先打印邮件还是短信?
      • 1.2.7.1个普通同步⽅法,1个静态同步⽅法,1部⼿机,请问先打印邮件还是短信?
      • 1.2.8.1个普通同步⽅法,1个静态同步⽅法,2部⼿机,请问先打印邮件还有短信?
      • 1.2.8 总结
  • 二⼈⼯窗⼝排队购票(回顾)
  • 三 公平锁⾮公平锁 (⽕⻋站⼈⼯窗⼝排队购票)
  • 四 可重⼊锁/递归锁
    • 锁的配对
  • 五 ⾃旋锁
  • 六 读写锁/独占/共享

一 多线程锁synchronized案例分析

1.1synchronized介绍

关键字synchronized可以保证在同一时刻,只有一个线程可以执行某个方法或某个代码块,同时synchronized可以保证一个线程的变化可见(可见性),即可以代替volatile使用方法

1.普通同步方法(实例方法),锁是当前实例对象 ,进入同步代码前要获得当前实例的锁
2.静态同步方法,锁是当前类的class对象 ,进入同步代码前要获得当前类对象的锁
3.同步方法块,锁是括号里面的对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁

1.2 synchronized案例分析

使用8个案例来详细说明synchronized的加锁用法:

1.标准访问,请问先打印邮件还是短信?
2.邮件⽅法暂停4秒钟,请问先打印邮件还是短信?
3.新增⼀个普通⽅法hello(),请问先打印邮件还是hello?
4.两部⼿机,请问先打印邮件还是短信?
5.两个静态同步⽅法,同⼀部⼿机,请问先打印邮件还是短信?
6.两个静态同步⽅法,2部⼿机,请问先打印邮件还是短信?
7.1个普通同步⽅法,1个静态同步⽅法,1部⼿机,请问先打印邮件还是短信?
8.1个普通同步⽅法,1个静态同步⽅法,2部⼿机,请问先打印邮件还有短信?

1.2.1.标准访问,请问先打印邮件还是短信?

答案:邮件

class Phone {public synchronized void sendEmail() {System.out.println("==========sendEmail");}public synchronized void sendMessage() {System.out.println("======sendMessage");}
}public class LockDemo {public static void main(String[] args) {Phone phone = new Phone();new Thread(() -> {phone.sendEmail();}, "t1").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {phone.sendMessage();}, "t2").start();//==========sendEmail//======sendMessage}
}

1.2.2.邮件⽅法暂停4秒钟,请问先打印邮件还是短信?

答案:邮件

//2. 邮件方法暂停4秒钟,请问先打印邮件还是短信? 邮件
class Phone {public synchronized void sendEmail() {try {TimeUnit.SECONDS.sleep(4);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("==========sendEmail");}public synchronized void sendMessage() {System.out.println("======sendMessage");}}public class LockDemo {public static void main(String[] args) {Phone phone = new Phone();new Thread(() -> {phone.sendEmail();}, "t1").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {phone.sendMessage();}, "t2").start();//==========sendEmail//======sendMessage}
}

分析

普通同步方法(实例方法),锁是当前实例对象 ,进入同步代码前要获得当前实例的锁

⼀个对象⾥⾯如果有多个synchronized⽅法,某⼀个时刻内,只要⼀个线程去调⽤其中的⼀个synchronized⽅法了,其他的线程都只能等待。换句话说,某⼀个时刻内,只能有唯⼀⼀个线程去访问这些 synchronized⽅法,锁的是当前对象this(new 的这个phone),被锁定后,其他的线程都不能进⼊到当前对象的其他的synchronized⽅法。
在这里插入图片描述

1.2.3.新增⼀个普通⽅法hello(),请问先打印邮件还是hello?

答案:hello
加个普通⽅法后发现和同步锁⽆关
在这里插入图片描述

//3. 新增一个普通方法hello(),请问先打印邮件还是hello?
class Phone {public synchronized void sendEmail() {try {TimeUnit.SECONDS.sleep(4);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("==========sendEmail");}public synchronized void sendMessage() {System.out.println("======sendMessage");}public void hello() {System.out.println("sayHello");}
}public class LockDemo {public static void main(String[] args) {Phone phone = new Phone();new Thread(() -> {phone.sendEmail();}, "t1").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {phone.sendMessage();}, "t2").start();new Thread(() -> {phone.hello();}, "t3").start();//sayHello//==========sendEmail//======sendMessage}
}

1.2.4.两部⼿机,请问先打印邮件还是短信?

答案:邮件
分析:换成两个对象后,不是同⼀把锁了,情况⽴刻变化

//4. 两部手机,请问先打印邮件还是短信?
class Phone {public synchronized void sendEmail() {try {TimeUnit.SECONDS.sleep(4);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("==========sendEmail");}public synchronized void sendMessage() {System.out.println("======sendMessage");}public void hello() {System.out.println("sayHello");}
}public class LockDemo {public static void main(String[] args) {Phone phone = new Phone();Phone phone2 = new Phone();new Thread(() -> {phone.sendEmail();}, "t1").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {phone2.sendMessage();}, "t2").start();//======sendMessage//==========sendEmail}
}

1.2.5.两个静态同步⽅法,同⼀部⼿机,请问先打印邮件还是短信?

答案:邮件
分析:见 1.2.6

//5. 两个静态同步方法,同一部手机,请问先打印邮件还是短信?
class Phone {public static synchronized void sendEmail() {try {TimeUnit.SECONDS.sleep(4);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("==========sendEmail");}public static synchronized void sendMessage() {System.out.println("======sendMessage");}public void hello() {System.out.println("sayHello");}
}public class LockDemo {public static void main(String[] args) {Phone phone = new Phone();new Thread(() -> {phone.sendEmail();}, "t1").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {phone.sendMessage();}, "t2").start();//==========sendEmail//======sendMessage}
}

1.2.6.两个静态同步⽅法,2部⼿机,请问先打印邮件还是短信?

答案: 邮件
分析: 静态同步方法,锁是当前类的class对象 ,进入同步代码前要获得当前类对象的锁
全局锁
在这里插入图片描述

//6. 两个静态同步方法,2部手机,请问先打印邮件还是短信?
class Phone {public static synchronized void sendEmail() {try {TimeUnit.SECONDS.sleep(4);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("==========sendEmail");}public static synchronized void sendMessage() {System.out.println("======sendMessage");}public void hello() {System.out.println("sayHello");}
}public class LockDemo {public static void main(String[] args) {Phone phone = new Phone();Phone phone2 = new Phone();new Thread(() -> {phone.sendEmail();}, "t1").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {phone2.sendMessage();}, "t2").start();//==========sendEmail//======sendMessage}
}

1.2.7.1个普通同步⽅法,1个静态同步⽅法,1部⼿机,请问先打印邮件还是短信?

答案: 短信
分析:
在这里插入图片描述

//7. 1个普通同步方法,1个静态同步方法,1部手机,请问先打印邮件还是短信?
class Phone {public  synchronized void sendEmail() {try {TimeUnit.SECONDS.sleep(4);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("==========sendEmail");}public static synchronized void sendMessage() {System.out.println("======sendMessage");}public void hello() {System.out.println("sayHello");}
}public class LockDemo {public static void main(String[] args) {Phone phone = new Phone();new Thread(() -> {phone.sendEmail();}, "t1").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {phone.sendMessage();}, "t2").start();//======sendMessage//==========sendEmail}
}

1.2.8.1个普通同步⽅法,1个静态同步⽅法,2部⼿机,请问先打印邮件还有短信?

答案: 短信
分析:

在这里插入图片描述

1.2.8 总结

(1)当⼀个线程试图访问同步代码块时,它⾸先必须得到锁,退出或抛出异常时必须释放锁。 也就是说如果⼀个实例对象的普通同步⽅法获取锁后,该实例对象的其他普通同步⽅法必须等待获取锁的⽅法释放锁后才能获取锁,可是别的实例对象的普通同步⽅法因为跟该实例对象的普通同步⽅法⽤的是不同的锁,所以⽆需等待该实例对象已获取锁的普通同步⽅法释放锁就可以获取他们⾃⼰的锁。
(2)所有的静态同步⽅法⽤的也是同⼀把锁–类对象本身, 这两把锁(this/class)是两个不同的对象,所以静态同步⽅法与⾮静态同步⽅法之间是 不会有静态条件的。 但是⼀旦⼀个静态同步⽅法获取锁后,其他的静态同步⽅法都必须等待该⽅法释放锁后才能获取锁,⽽不管是同⼀个实例对象的静态同步⽅法之间,还是不同的实例对象的静态同步⽅法之间,只要它们同⼀个类的实例对象

二⼈⼯窗⼝排队购票(回顾)

题目:三个售票员 卖出 30张票
多个线程共抢一个资源

/*** 题目:三个售票员   卖出   30张票**/
class Ticket{//资源类//票private int number = 30;public synchronized void saleTicket(){if (number > 0) {System.out.println(Thread.currentThread().getName()+"\t卖出第:"+(number--)+"\t还剩下:"+number);}}}public class SaleTicketDemo {public static void main(String[] args) {Ticket ticket = new Ticket();new Thread(()->{ for (int i = 1; i <= 30 ; i++) ticket.saleTicket(); }, "A").start();new Thread(()->{ for (int i = 1; i <= 30 ; i++) ticket.saleTicket(); }, "B").start();new Thread(()->{ for (int i = 1; i <= 30 ; i++) ticket.saleTicket(); }, "C").start();}
}

在这里插入图片描述
我们用synchronized同步代码块的方式来解决。随着juc并发编程。我们有更加轻量级的锁方式来解决问题。

三 公平锁⾮公平锁 (⽕⻋站⼈⼯窗⼝排队购票)

lcok锁

 Lock lock = new ReentrantLock();

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
概念
公平锁,就是多个线程按照申请锁的顺序来获取锁,类似排队,先到先得。
⾮公平锁,则是多个线程抢夺锁,会导致优先级反转或饥饿现象。
区别:

  • 公平锁在获取锁时先查看此锁维护的等待队列,为空或者当前线程是等待队列的队⾸,则直接占有锁,否则插⼊到等待队列,FIFO原则。
  • ⾮公平锁⽐较粗鲁,上来直接先尝试占有锁,失败则采⽤公平锁⽅式。⾮公平锁的优点是吞吐量⽐公平锁更⼤。

synchronizedjuc.ReentrantLock默认都是⾮公平锁ReentrantLock在构造的时候传⼊ true 则是公平锁

四 可重⼊锁/递归锁

在这里插入图片描述
可重⼊锁⼜叫递归锁指的同⼀个线程在外层⽅法获得锁时,进⼊内层⽅法会⾃动获取锁。也就是说,线程可以进⼊任何⼀个它已经拥有锁的代码块。
⽐如method01⽅法⾥⾯有method02⽅法,两个⽅法都有同⼀把锁,得到了method01的锁。就⾃动得到了method02的锁,就像有了家⻔的锁,厕所、书房、厨房就为你敞开了⼀样。可重⼊锁可以避免死锁的问题。
在这里插入图片描述


/*** 可重入锁/递归锁*/class PhonePlus implements Runnable{//Synchronized Testpublic synchronized void sendEmail(){System.out.println(Thread.currentThread().getName()+"\t"+"sendEmail");sendSMS();}public synchronized void sendSMS(){System.out.println(Thread.currentThread().getName()+"\t"+"sendSMS");}//ReenTrantLock TestLock lock = new ReentrantLock();public void method1(){lock.lock();try {System.out.println(Thread.currentThread().getName()+"\t"+"method1");method2();} finally {lock.unlock();}}public void method2() {lock.lock();try {System.out.println(Thread.currentThread().getName()+"\t"+"method2");} finally {lock.unlock();}}@Overridepublic void run() {method1();}
}public class ReentrantLockDemo {public static void main(String[] args) {PhonePlus phonePlus = new PhonePlus();new Thread(()->{phonePlus.sendEmail();}, "t1").start();new Thread(()->{phonePlus.sendEmail();}, "t2").start();Thread t3 = new Thread(phonePlus);Thread t4 = new Thread(phonePlus);t3.start();t4.start();}
}

锁的配对

锁之间要配对,加了⼏把锁,最后就得解开⼏把锁,下⾯的代码编译和运⾏都没有任何问题。但锁的数量不匹配会导致死循环。

lock.lock();
lock.lock();
try{someAction(); 
}finally{lock.unlock(); 
}

五 ⾃旋锁

所谓⾃旋锁,就是尝试获取锁的线程不会⽴即阻塞,⽽是采⽤循环的⽅式去尝试获取。⾃⼰在那⼉⼀直循环获取,就像“⾃旋”⼀样。这样的好处是减少线程切换的上下⽂开销,缺点是会消耗CPU。CAS底层的 getAndAddInt就是⾃旋锁思想。

//跟CAS类似,⼀直循环⽐较。
while (!atomicReference.compareAndSet(null, thread)) { }

在这里插入图片描述

/*** 题目:实现一个自旋锁* 自旋锁好处:循环比较获取直到成功为止,没有类似wait的阻塞。* 

* 通过CAS操作完成自旋锁,A线程先进来调用myLock方法自己持有锁5秒钟,* B随后进来后发现当前有线程持有锁,不是null,所以只能通过自选等待,直到A释放锁后B随后抢到。*/ public class SpinLockDemo {//原子引用(线程)AtomicReference atomicReference = new AtomicReference<>(); //Thread ==> null//获取锁public void myLock() {Thread currentThread = Thread.currentThread();System.out.println(Thread.currentThread().getName() + "\t com in...");while (!atomicReference.compareAndSet(null, currentThread)) {}}//释放锁public void myUnLock() {Thread currentThread = Thread.currentThread();atomicReference.compareAndSet(currentThread, null);System.out.println(Thread.currentThread().getName() + "\t unlock....");}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();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {spinLockDemo.myLock();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}spinLockDemo.myUnLock();}, "BB").start();} }

结果
在这里插入图片描述
过程分析
在这里插入图片描述

六 读写锁/独占/共享

读锁是共享的,写锁是独占的。 juc.ReentrantLocksynchronized都是独占锁,独占锁就是⼀个锁只能被⼀个线程所持有。有的时候,需要读写分离,那么就要引⼊读写锁,即 juc.ReentrantReadWriteLock

独占锁:指该锁⼀次只能被⼀个线程所持有。对ReentrantLockSynchronized⽽⾔都是独占锁
共享锁:指该锁可被多个线程所持有
ReenntrantReadWriteLock其读锁是共享锁,其写锁是独占锁。
读锁的共享锁可保证并发读是⾮常⾼效的,读写、写读、写写的过程是互斥的。

⽐如缓存,就需要读写锁来控制。缓存就是⼀个键值对,以下Demo模拟了缓存的读写操作,读的 get ⽅法使⽤了 ReentrantReadWriteLock.ReadLock(),写的 put⽅法使⽤了ReentrantReadWriteLock.WriteLock()。这样避免了写被打断,实现了多个线程同时读。

相关内容

热门资讯

电视安卓系统哪个品牌好,哪家品... 你有没有想过,家里的电视是不是该升级换代了呢?现在市面上电视品牌琳琅满目,各种操作系统也是让人眼花缭...
安卓会员管理系统怎么用,提升服... 你有没有想过,手机里那些你爱不释手的APP,背后其实有个强大的会员管理系统在默默支持呢?没错,就是那...
安卓系统软件使用技巧,解锁软件... 你有没有发现,用安卓手机的时候,总有一些小技巧能让你玩得更溜?别小看了这些小细节,它们可是能让你的手...
安卓系统提示音替换 你知道吗?手机里那个时不时响起的提示音,有时候真的能让人心情大好,有时候又让人抓狂不已。今天,就让我...
安卓开机不了系统更新 手机突然开不了机,系统更新还卡在那里,这可真是让人头疼的问题啊!你是不是也遇到了这种情况?别急,今天...
安卓系统中微信视频,安卓系统下... 你有没有发现,现在用手机聊天,视频通话简直成了标配!尤其是咱们安卓系统的小伙伴们,微信视频功能更是用...
安卓系统是服务器,服务器端的智... 你知道吗?在科技的世界里,安卓系统可是个超级明星呢!它不仅仅是个手机操作系统,竟然还能成为服务器的得...
pc电脑安卓系统下载软件,轻松... 你有没有想过,你的PC电脑上安装了安卓系统,是不是瞬间觉得世界都大不一样了呢?没错,就是那种“一机在...
电影院购票系统安卓,便捷观影新... 你有没有想过,在繁忙的生活中,一部好电影就像是一剂强心针,能瞬间让你放松心情?而我今天要和你分享的,...
安卓系统可以写程序? 你有没有想过,安卓系统竟然也能写程序呢?没错,你没听错!这个我们日常使用的智能手机操作系统,竟然有着...
安卓系统架构书籍推荐,权威书籍... 你有没有想过,想要深入了解安卓系统架构,却不知道从何下手?别急,今天我就要给你推荐几本超级实用的书籍...
安卓系统看到的炸弹,技术解析与... 安卓系统看到的炸弹——揭秘手机中的隐形威胁在数字化时代,智能手机已经成为我们生活中不可或缺的一部分。...
鸿蒙系统有安卓文件,畅享多平台... 你知道吗?最近在科技圈里,有个大新闻可是闹得沸沸扬扬的,那就是鸿蒙系统竟然有了安卓文件!是不是觉得有...
宝马安卓车机系统切换,驾驭未来... 你有没有发现,现在的汽车越来越智能了?尤其是那些豪华品牌,比如宝马,它们的内饰里那个大屏幕,简直就像...
p30退回安卓系统 你有没有听说最近P30的用户们都在忙活一件大事?没错,就是他们的手机要退回安卓系统啦!这可不是一个简...
oppoa57安卓原生系统,原... 你有没有发现,最近OPPO A57这款手机在安卓原生系统上的表现真是让人眼前一亮呢?今天,就让我带你...
安卓系统输入法联想,安卓系统输... 你有没有发现,手机上的输入法真的是个神奇的小助手呢?尤其是安卓系统的输入法,简直就是智能生活的点睛之...
怎么进入安卓刷机系统,安卓刷机... 亲爱的手机控们,你是否曾对安卓手机的刷机系统充满好奇?想要解锁手机潜能,体验全新的系统魅力?别急,今...
安卓系统程序有病毒 你知道吗?在这个数字化时代,手机已经成了我们生活中不可或缺的好伙伴。但是,你知道吗?即使是安卓系统,...
奥迪中控安卓系统下载,畅享智能... 你有没有发现,现在汽车的中控系统越来越智能了?尤其是奥迪这种豪华品牌,他们的中控系统简直就是科技与艺...