JUC(四)多线程锁
多线程锁
Synchronized锁的八种情况
以一个手机类为例,包含两个synchronized方法和一个普通方法。
public class Phone { public synchronized void sendSMS() throws InterruptedException { System.out.println("-----send SMS-----"); } public synchronized void sendEmail() { System.out.println("-----send email-----"); } public void getHello() { System.out.println("-----get hello-----"); } }
- 1 标准访问
public static void main(String[] args) { Phone phone = new Phone(); new Thread(() -> { phone.sendSMS(); }).start(); new Thread(() -> { phone.sendEmail(); }).start(); }
-----send SMS----- -----send email-----
- 2 短信方法设置4s停止
public class Phone { public synchronized void sendSMS() { try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("-----send SMS-----"); } public synchronized void sendEmail() { System.out.println("-----send email-----"); } public void getHello() { System.out.println("-----get hello-----"); } }
-----send SMS-----
-----send email-----
会先等待四秒,短信发送结束才会发送邮件
- 3 短信停止四秒,同时运行getHello
public class Lock_8 { public static void main(String[] args) { Phone phone = new Phone(); new Thread(() -> { phone.sendSMS(); }).start(); new Thread(() -> { phone.getHello(); }).start(); } }
-----get hello-----
4s。。。
-----send SMS-----
- 4 两部手机,一部邮件另一部短信
public static void main(String[] args) { Phone phone = new Phone(); Phone phone2 = new Phone(); new Thread(() -> { phone.sendSMS(); }).start(); new Thread(() -> { phone2.sendEmail(); }).start(); }
-----send email-----
4s...
-----send SMS-----
- 5 两个静态方法,一部手机,先打印邮件还是短信(短信停4s,然后邮件)
- 6 两个静态方法,两部手机,先打印邮件还是短信(短信停4s,然后邮件)
- 7 一个静态短信方法,一个邮件方法,1部手机(先邮件,停4s短信)
- 8 一个静态短信方法,一个邮件方法,2部手机(先邮件,停4s短信)
总结
- synchronized加在普通方法上,是对this进行加锁,作用范围为当前实例对象
- synchronized加在静态方法上,是对类的类加载的Class对象加锁,因此作用范围是全部的实例对象
公平锁和非公平锁
当锁被一个线程释放的时候,其他线程抢占锁的机会是否是公平的,分为公平锁和非公平锁。如之前卖票的例子,导致ABC卖票全被A卖了:
- 公平锁可以防止线程饥饿,但是效率低
A sale 1 ticket, there are 29 tickets left A sale 1 ticket, there are 28 tickets left A sale 1 ticket, there are 27 tickets left A sale 1 ticket, there are 26 tickets left A sale 1 ticket, there are 25 tickets left A sale 1 ticket, there are 24 tickets left A sale 1 ticket, there are 23 tickets left A sale 1 ticket, there are 22 tickets left ... Process finished with exit code 0
通过ReetranLock构造器设置公平锁
lock = new ReentrantLock(true);
A sale 1 ticket, there are 29 tickets left B sale 1 ticket, there are 28 tickets left C sale 1 ticket, there are 27 tickets left A sale 1 ticket, there are 26 tickets left B sale 1 ticket, there are 25 tickets left C sale 1 ticket, there are 24 tickets left A sale 1 ticket, there are 23 tickets left B sale 1 ticket, there are 22 tickets left C sale 1 ticket, there are 21 tickets left
可重入锁
可重入锁:可以重复使用的锁,即某个线程已经获得了某个锁,再次获取锁而不会出现死锁,可重入锁有:
- synchronized
- ReentrantLock:ReentrantLock 和 synchronized 不一样,需要手动释放锁,所以使用 ReentrantLock的时候一定要手动释放锁,并且加锁次数和释放次数要一样
synchronized
public static void main(String[] args) { var obj = new Object(); new Thread(() -> { synchronized (obj) { System.out.println("外层"); synchronized (obj) { System.out.println("中层"); synchronized (obj) { System.out.println("内层"); } } } }).start(); }
可重入锁又称为递归锁
,能够实现同步方法的递归调用而不产生死锁
static synchronized void add() { add(); } public static void main(String[] args) { new Thread(ThreadDemo4::add).start(); }
Lock
class ShareSource { private final Lock lock = new ReentrantLock(); public void lockTest() { lock.lock(); System.out.println("waiceng"); lock.lock(); System.out.println("zhongceng"); lock.lock(); System.out.println("neiceng"); lock.unlock(); lock.unlock(); } } public class ThreadDemo4 { public static void main(String[] args) { ShareSource shareSource = new ShareSource(); new Thread(shareSource::lockTest).start(); new Thread(shareSource::lockTest).start(); } }
Lock不会自动释放锁,上锁和解锁不对应的话会导致死锁的发生,如这里的第二个线程就一直执行不了
死锁
def:两个或两个以上的线程在执行过程中,由于竞争资源而造成的相互等待的现象,如果没有外力作用,则会一直等待下去。
死锁产生的原因:
- 竞争资源
- 进程间推进顺序非法
- 资源分配不当
public static void main(String[] args) { var a = new Object(); var b = new Object(); new Thread(() -> { synchronized (a) { System.out.println("已获取锁A,尝试获取锁B"); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (b) { System.out.println("已获取锁B"); } } }).start(); new Thread(() -> { synchronized (b) { System.out.println("已获取锁B,尝试获取锁A"); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (a) { System.out.println("已获取锁A"); } } }).start(); }
检查死锁
- jsp:查看系统中运行的java进程
- jstack:追踪java进程的堆栈情况
"Thread-0": at com.hikaru.juc.lock.DeadLockDemo.lambda$main$0(DeadLockDemo.java:19) - waiting to lock <0x0000000711818d28> (a java.lang.Object) - locked <0x0000000711818d18> (a java.lang.Object) at com.hikaru.juc.lock.DeadLockDemo$$Lambda$14/0x00000008010031f0.run(Unknown Source) at java.lang.Thread.run(java.base@19.0.2/Thread.java:1589) "Thread-1": at com.hikaru.juc.lock.DeadLockDemo.lambda$main$1(DeadLockDemo.java:33) - waiting to lock <0x0000000711818d18> (a java.lang.Object) - locked <0x0000000711818d28> (a java.lang.Object) at com.hikaru.juc.lock.DeadLockDemo$$Lambda$15/0x0000000801003400.run(Unknown Source) at java.lang.Thread.run(java.base@19.0.2/Thread.java:1589) Found 1 deadlock.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步