JUC多线程锁——JUC随记5
创始人
2025-05-29 13:06:20

1、synchronized锁的8种情况

import java.util.concurrent.TimeUnit;
/*
@Description: 8锁
1标准访问,先打印短信还是邮件
----------sendSMS
----------sendEmail
分析:锁住了当前对象
2停4秒在短信方法内,先打印短信还是邮件
----------sendSMS
----------sendEmail
分析:锁住了当前对象(this)
3新增普通的hello方法,是先打短信还是hello
----------sendHello
----------sendSMS
分析:普通方法与锁无关,直接执行
4现在有两部手机,先打印短信还是邮件
----------sendEmail
----------sendSMS
分析:锁的是当前对象,两个不同对象各自的锁与另一个没关系
5两个静态同步方法,1部手机,先打却短信还是邮件
----------sendSMS
----------sendEmai
分析:锁的是Class Phone字节码对象
6两个静态同步方法, 2部手机,先打印短信还是邮件
----------sendSMS
----------sendEmail
分析:锁的是Class Phone字节码对象
7 1个静态同步方法, 1个普通同步方法, 1部手机,先打印短信还是邮件
----------sendEmail
----------sendSMS
分析:举例:加static的锁住了大门(锁的是Class Phone字节码对象),没加的锁住了里面的小门(当前对象this)
8 1个静态同步方法, 1个普通同步方法, 2部手机,先打印短信还是邮件
----------sendEmail
----------sendSMS
分析:举例:加static的锁住了大门(锁的是Class Phone字节码对象),没加的锁住了里面的小门(当前对象this)
*/
class Phone{public static synchronized void sendSMS() throws InterruptedException {//停四秒TimeUnit.SECONDS.sleep(4);System.out.println("----------sendSMS");}public synchronized void sendEmail(){System.out.println("----------sendEmail");}public void getHello(){System.out.println("----------sendHello");}
}public class Lock_8 {public static void main(String[] args) throws InterruptedException {Phone phone = new Phone();Phone phone2 = new Phone();new Thread(()->{try{phone.sendSMS();}catch (Exception e){e.printStackTrace();}},"AA").start();Thread.sleep(100);new Thread(()->{try{//phone.sendEmail();//phone.getHello();phone2.sendEmail();}catch (Exception e){e.printStackTrace();}},"BB").start();}
}

1.1、结论

synchronized实现同步的基础: Java中的每一个对象都可以作为锁具体表现为以下3种形式。
(1)对于普通同步方法,锁是当前实例对象。
(2)对于静态同步方法,锁是当前类的Class对象。
(3)对于同步方法块,锁是Synchonized括号里配置的对象

2、公平锁和非公平锁

2.1、非公平锁

会出现线程饿死的情况,但效率高。

mport java.util.concurrent.locks.ReentrantLock;//创建多个线程调用资源类的方法
public class LockSaleTicket {public static void main(String[] args) {LTicket lTicket = new LTicket();new Thread(()->{lTicket.sale();},"线程A").start();new Thread(()->{for (int i = 0; i <30 ; i++) {lTicket.sale();}},"线程B").start();new Thread(()->{lTicket.sale();},"线程C").start();}
}
//第一步:创建一个资源类
class LTicket{int number=30;private final ReentrantLock lock =new ReentrantLock();public void sale(){//上锁lock.lock();try{//判断是否有票if(number>0){System.out.println(Thread.currentThread().getName()+":卖出第"+number--+"张票,剩下"+number+"张票");}}finally {//解锁lock.unlock();}}
}

在这里插入图片描述
核心源码:

  /*** Creates an instance of {@code ReentrantLock}.* This is equivalent to using {@code ReentrantLock(false)}.*/public ReentrantLock() {sync = new NonfairSync();//非公平锁}

2.2、公平锁

每个线程基本都能分配到资源,但效率较低。

import java.util.concurrent.locks.ReentrantLock;//创建多个线程调用资源类的方法
public class LockSaleTicket {public static void main(String[] args) {LTicket lTicket = new LTicket();new Thread(()->{lTicket.sale();},"线程A").start();new Thread(()->{for (int i = 0; i <30 ; i++) {lTicket.sale();}},"线程B").start();new Thread(()->{lTicket.sale();},"线程C").start();}
}
//第一步:创建一个资源类
class LTicket{int number=30;private final ReentrantLock lock =new ReentrantLock(true);public void sale(){//上锁lock.lock();try{//判断是否有票if(number>0){System.out.println(Thread.currentThread().getName()+":卖出第"+number--+"张票,剩下"+number+"张票");}}finally {//解锁lock.unlock();}}
}

在这里插入图片描述
核心源码:

 /*** Creates an instance of {@code ReentrantLock} with the* given fairness policy.** @param fair {@code true} if this lock should use a fair ordering policy*/public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();}

3、可重入锁

可重入锁,指的是以线程为单位,当一个线程获取对象锁之后,这个线程可以再次获取本对象上的锁,而其他的线程是不可以的。
synchronized 和 ReentrantLock 都是可重入锁。
可重入锁的意义之一在于防止死锁。
实现原理实现是通过为每个锁关联一个请求计数器和一个占有它的线程。当计数为0时,认为锁是未被占有的;线程请求一个未被占有的锁时,JVM将记录锁的占有者,并且将请求计数器置为1 。
如果同一个线程再次请求这个锁,计数器将递增;
每次占用线程退出同步块,计数器值将递减。直到计数器为0,锁被释放。
在这里插入图片描述

3.1、synchronized代码块实现

public class SyncLockDemo {public static void main(String[] args) {Object o = new Object();new Thread(()->{synchronized (o){System.out.println(Thread.currentThread().getName()+":外层");synchronized (o){System.out.println(Thread.currentThread().getName()+":内层");synchronized (o){System.out.println(Thread.currentThread().getName()+":里层");}}}},"AA").start();}
}

在这里插入图片描述

3.2、synchronized方法

public class SyncLockDemo {public synchronized void add(){add();}public static void main(String[] args) {new SyncLockDemo().add();}
}

在这里插入图片描述

3.2、lock接口实现

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class SyncLockDemo {public static void main(String[] args) {//创建一把锁Lock lock=new ReentrantLock();//创建线程new Thread(()->{try{//上锁lock.lock();System.out.println(Thread.currentThread().getName()+":外层");try{//上锁lock.lock();System.out.println(Thread.currentThread().getName()+":里层");}finally {//释放锁lock.unlock();}}finally {//释放锁lock.unlock();}},"t1").start();}
}

在这里插入图片描述

4、死锁

4.1、什么是死锁

谓死锁,是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。 因此我们举个例子来描述,如果此时有一个线程A,按照先锁a再获得锁b的的顺序获得锁,而在此同时又有另外一个线程B,按照先锁b再锁a的顺序获得锁。如下图所示:
在这里插入图片描述

4.2、产生死锁的原因

4.2.1、竞争资源

系统中的资源可以分为两类:

(1)可剥夺资源,是指某进程在获得这类资源后,该资源可以再被其他进程或系统剥夺,CPU和主存均属于可剥夺性资源;
(2)不可剥夺资源,当系统把这类资源分配给某进程后,再不能强行收回,只能在进程用完后自行释放,如磁带机、打印机等。
a.产生死锁中的竞争资源之一指的是竞争不可剥夺资源(例如:系统中只有一台打印机,可供进程P1使用,假定P1已占用了打印机,若P2继续要求打印机打印将阻塞);
b.产生死锁中的竞争资源另外一种资源指的是竞争临时资源(临时资源包括硬件中断、信号、消息、缓冲区内的消息等),通常消息通信顺序进行不当,则会产生死锁;

4.2.2、进程间推进顺序非法

(1)若P1保持了资源R1,P2保持了资源R2,系统处于不安全状态,因为这两个进程再向前推进,便可能发生死锁。例如,当P1运行到P1:Request(R2)时,将因R2已被P2占用而阻塞;当P2运行到P2:Request(R1)时,也将因R1已被P1占用而阻塞,于是发生进程死锁。

4.3、示列

import java.util.concurrent.TimeUnit;public class DeadLock {static Object a= new Object();static Object b= new Object();public static void main(String[] args) {new Thread(()->{synchronized (a){System.out.println(Thread.currentThread().getName()+":已获得锁a,试图再去获得锁b");try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}synchronized (b){System.out.println(Thread.currentThread().getName()+":获得锁b");}}},"A").start();new Thread(()->{synchronized (b){System.out.println(Thread.currentThread().getName()+":已获得锁b,试图再去获得锁a");try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}synchronized (a){System.out.println(Thread.currentThread().getName()+":获得锁a");}}},"B").start();}
}

在这里插入图片描述

4.3.1、如何验证发生死锁呢?

(1)通过jsp -l命令找到进程号
在这里插入图片描述
(2)通过 jstack 18868 命令查看
在这里插入图片描述
在这里插入图片描述

相关内容

热门资讯

高二励志作文【推荐6篇】 高二励志作文 篇一:砥砺前行,追逐梦想的力量每个人都有自己的梦想,有的人梦想成为一名医生,有的人梦想...
高二温暖的作文(实用3篇) 高二温暖的作文 篇一爱,是一种温暖的力量人生中有许许多多的温暖瞬间,而其中最温暖的莫过于那些关于爱的...
高二作文:蝴蝶翅膀颜色(实用... 高二作文:蝴蝶翅膀颜色 篇一蝴蝶是大自然中美丽的造物,它们用它们独特的翅膀颜色吸引我们的目光。蝴蝶翅...
学会变通高二作文(最新3篇) 学会变通高二作文 篇一灵活运用变通的能力在学习和生活中,我们经常会遇到各种各样的问题和困难。如果我们...
捡拾高二作文【精简4篇】 捡拾高二作文 篇一:回忆与成长高二生活即将结束,回首这一年的点滴,我仿佛看到了自己成长的足迹。这一年...