当前位置: 首页 > news >正文

JUC基础-0601

6 多线程锁

6.1 锁的八个问题演示

class Phone {public static synchronized void sendSMS() throws Exception {//停留4秒TimeUnit.SECONDS.sleep(4);System.out.println("------sendSMS");}public synchronized void sendEmail() throws Exception {System.out.println("------sendEmail");}public void getHello() {System.out.println("------getHello");}
}
  1. 标准访问,先打印短信还是邮件

    1. ------sendSMS
    2. ------sendEmail
  2. 停 4 秒在短信方法内,先打印短信还是邮件

    1. ------sendSMS
    2. ------sendEmail
  3. 新增普通的 hello 方法,是先打短信还是 hello

    1. ------getHello
    2. ------sendSMS
  4. 现在有两部手机,先打印短信还是邮件

    1. ------sendEmail
    2. ------sendSMS
  5. 两个静态同步方法,1 部手机,先打印短信还是邮件

    1. ------sendSMS
    2. ------sendEmail
  6. 两个静态同步方法,2 部手机,先打印短信还是邮件

    1. ------sendSMS
    2. ------sendEmail
  7. 1 个静态同步方法,1 个普通同步方法,1 部手机,先打印短信还是邮件

    1. ------sendEmail
    2. ------sendSMS
  8. 1 个静态同步方法,1 个普通同步方法,2 部手机,先打印短信还是邮件

    1. ------sendEmail
    2. ------sendSMS

分析:两点:

  1. 是否用的同一把锁
  2. 锁的范围是怎样的?

6.2 总结

  1. 两个线程的创建之间间隔了100ms,则短信线程先创建,邮件后创建
  2. sleep不会释放锁,两个线程一起等待4秒后,顺序和1一样
  3. hello不同步的,SMS等待4秒,所以先Hello
  4. 两部手机,两个同步监视器,互相不干扰,SMS睡4秒,所以先email
  5. 两个静态方法,锁是静态类的Class,所以顺序和1一样,因为同一把锁
  6. 两个静态方法,两部手机,但是锁还是一个,和5一样
  7. 一个锁是Class,一个锁是this,互相不影响,所以email快,SMS睡4秒所以慢
  8. 两把锁,几个手机都一样,锁不同,顺序和7一样。

具体表现为以下 3 种形式。
对于普通同步方法,锁是当前实例对象。
对于静态同步方法,锁是当前类的
Class 对象。
对于同步方法块,锁是
Synchonized 括号里配置的对象

6.3 公平锁和非公平锁

例如:ReentrantLock的无参构造,就是一个非公平锁,会出现一个线程消耗所有资源,其他线程饿死的问题。

//	非公平锁演示
private Lock lock =  new ReentrantLock();
//	或这样也是非公平锁
private Lock lock =  new ReentrantLock(false);//	公平锁
private Lock lock =  new ReentrantLock(true);

公平锁与非公平锁优缺点:

  1. 非公平锁:
    1. 效率高
    2. 线程饿死
  2. 公平锁:
    1. 阳光普照
    2. 效率相对低
  3. 想要效率高:选非公平锁
  4. 想要公平:公平锁

公平锁与非公平锁源码实现

  1. 非公平锁:占据锁之后之间进行后续操作
static final class NonfairSync extends Sync {private static final long serialVersionUID = 7316153563782823691L;/*** Performs lock.  Try immediate barge, backing up to normal* acquire on failure.*/final void lock() {if (compareAndSetState(0, 1))setExclusiveOwnerThread(Thread.currentThread());elseacquire(1);}protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);}
}
  1. 公平锁:里面有一个acquires 参数,并且判断时先执行hasQueuedPredecessors()方法,相当于先礼貌的问一句,这里有人吗;没人就继续操作,有人就会排队。
static final class FairSync extends Sync {private static final long serialVersionUID = -3000897897090466540L;final void lock() {acquire(1);}/*** Fair version of tryAcquire.  Don't grant access unless* recursive call or no waiters or is first.*/protected final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) {if (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0)throw new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;}
}public final boolean hasQueuedPredecessors() {// The correctness of this depends on head being initialized// before tail and on head.next being accurate if the current// thread is first in queue.Node t = tail; // Read fields in reverse initialization orderNode h = head;Node s;return h != t && ((s = h.next) == null || s.thread != Thread.currentThread());
}

6.4 可重入锁(递归锁)

synchronized(隐式)和Lock(显示)都是可重入锁

synchronized的加锁和释放都是自动的,所以是隐式的;Lock是显式的。

简单讲:如果一个线程拿到了锁,那么所有需要这个锁的地方,他都可以进入了

可重入锁,又称为递归锁,是一种递归无阻塞的同步机制。"可重入"的意思是如果一个线程已经持有了一个锁,那么这个线程可以再次获取同一把锁而不会被锁阻塞。这个特性可以在递归或者需要多次访问同步资源的场合简化编程。

在Java中,synchronized和ReentrantLock都是可重入锁。比如:

public class Example {public synchronized void method1() {method2();}public synchronized void method2() {// do something}
}

在这个例子中,一个线程进入method1方法后,得到了对象锁,然后它可以再次进入method2方法而不会阻塞,因为它已经持有了对象锁。

同样的,使用ReentrantLock也能达到类似的效果:

import java.util.concurrent.locks.ReentrantLock;public class Example {private final ReentrantLock lock = new ReentrantLock();public void method1() {lock.lock();try {method2();} finally {lock.unlock();}}public void method2() {lock.lock();try {// do something} finally {lock.unlock();}}
}

在这个例子中,线程进入method1方法后,获取了锁,然后它可以进入method2方法并再次获取锁,而不会被阻塞。需要注意的是,每次调用lock()方法,引用计数就加1,每次调用unlock()方法,引用计数就减1。只有当引用计数为0时,锁才真正被释放。

这种锁的好处是,同一个线程可以多次获取同一把锁,避免了死锁。缺点是可能导致锁保持得过久,从而影响系统性能。

LocK可重入锁演示:

//Lock演示可重入锁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();

在这段代码里面,如果不释放锁,后面的会获取不到锁。

6.5 死锁

  1. 定义:两个或者两个以上进程在执行过程中,因为争夺资源而造成一种互相等待的现象,如果没有外力干涉,他们无法再执行下去
  2. 产生死锁原因:
    1. 系统资源不足
    2. 进程运行推进顺序不合适
    3. 资源分配不当
  3. 验证死锁命令
    1. jps:ps -ef
    2. jstack :jvm自带堆栈工具
http://www.lryc.cn/news/92094.html

相关文章:

  • bash特性
  • [Flink] Flink On Yarn(yarn-session.sh)启动错误
  • 玩转css逐帧动画,努力成为更优质的Ikun~
  • Linux Capabilities
  • 【自制C++深度学习框架】前言
  • 【高危】泛微 e-cology9 存在任意用户登录漏洞
  • 1TB文本的实时全文检索系统搭建
  • RHCA---DO477---变量实验
  • 毕业生高频常用材料线上签,高校毕业季契约锁电子签章一站式助力
  • .ini配置文件介绍与解析库使用
  • 牛客网Linux错题七
  • 牛课刷题Day5(编程题)
  • javascript基础二十五:说说你对函数式编程的理解?优缺点?
  • 常见JavaScript加密算法、JS加密算法
  • 题解2023.6.5
  • 与声音计算研究相关的挑战赛——DCASE和L3DAS
  • 实训总结-----Scrapy爬虫
  • 前端开发职业规划指南:如何做好职业规划与发展
  • 创业第一步:如何写好商业计划书
  • 【Linux驱动】字符设备驱动相关宏 / 函数介绍(module_init、register_chrdev)
  • axios解决跨域问题
  • R语言作图——热图聚类及其聚类结果输出
  • Tomcat优化
  • 我的GIT练习TWO
  • 个人器件库整理
  • javascript——内存管理
  • Qt5.15.2安卓Android项目开发环境配置
  • 第四十三章 弹跳训练2(灵识扫描)
  • 【location对象的方法,history对象,navigator--BOM】
  • 论文笔记:Normalizing Flows for Probabilistic Modeling and Inference