锁---一些锁学习

article/2025/7/2 8:59:25

1、java锁之公平锁和非公平锁

公平锁 是指多个线程按照申请的顺序来获取,类似排队打饭,先来后到。

非公平锁  是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁,在高并发的情况下,有可能会造成优先级反转或者饥饿现象。

关于两者的区别:

  1. 公平锁:Thread acquire a fair lock in the order in which they requested it
  2. 公平锁,就是很公平,在并发环境中每个线程在获取锁时会查看此锁维护的等待队列,如果为空,或者当前线程是等待队列的第一个,就是占有锁,否则就会加入到等待队列中,以后会按照FIFO的规则从队列中取到自己。
  3. 非公平锁:a nonfair lock permits barging:threads requesting a lock can jump ahead of the queue of waiting threads if the lock happens to be available when it is requested
  4. 非公平锁比较粗鲁,上来就直接占有锁,如果尝试失败,就再采用类似公平锁那种方式。

Java ReentrantLock而言

通过构造函数指定该锁是否公平锁,默认是非公平锁。非公平锁的有点在于吞吐量比公平锁大。

Lock lock = new ReentrantLock();//非公平锁
Lock lock = new ReentrantLock(true);//公平锁

对了Synchronized 而言,也是一种非公平锁

2、可重入锁(又名递归锁)

指对是同一线程外层函数获得锁之后,内层递归函数仍然能获取该锁对代码

在同一个线程在外层方法获取锁对时候,在进入内层方法会自动获取锁

也即是说,线程可以进入任何一个它已经拥有的锁所同步着的代码块

ReentrantLock/Synchronized 就是一个典型的可重入锁

作用:可重入锁最大的作用是避免死锁

1、Synchronized是可重入锁,如下:

public class RentrantLockTest2 {public static void main(String[] args) {Phone phone = new  Phone();new Thread(()->{phone.sendSMS();},"线程1") .start();new Thread(()->{phone.sendSMS();},"线程2") .start();}
}class Phone{public synchronized void sendSMS(){System.out.println(Thread.currentThread().getName()+"\t sendSMS()");sendEmail();}public synchronized void sendEmail(){System.out.println(Thread.currentThread().getName()+"\t sendEmail()");}
}
结果:
线程1	 sendSMS()  ------线程1在外层方法获取锁
线程1	 sendEmail() ------线程1在进入内层方法会自动获取锁
线程2	 sendSMS()
线程2	 sendEmail()

2、ReentrantLock实现可重入锁,如下:

public class ReentrantLockTest3 {public static void main(String[] args) {Car car = new Car();new Thread(car,"线程1").start();new Thread(car,"线程2").start();}
}class Car implements Runnable{@Overridepublic void run() {test();}Lock lock = new ReentrantLock();private void test() {lock.lock();try {System.out.println(Thread.currentThread().getName()+"\t test()");method();}finally {lock.unlock();}}private void method() {lock.lock();try {System.out.println(Thread.currentThread().getName()+"\t method()");}finally {lock.unlock();}}
}
结果:
线程1	 test()
线程1	 method()
线程2	 test()
线程2	 method()

lock和unlock配对就可以了,不管加多少对都不会出现错误。 

3、独占锁/共享锁

独占锁:指该锁一次只能被一个线程锁所持有。(原子+独占,整个过程必须是一个完整的)

     对ReentrantLock和Synchronized而言都是独占锁

共享锁:指该锁可以被多个线程所持有。

ReentrantReadWriteLock其读锁是共享锁,其写锁是独占锁。

多个线程同时读一个资源类没有任何问题,所以为了满足并发量,读取共享资源应该可以同时进行。

但是如果一个线程想去写共享资源,就不应该再有线程可以对该资源进行读或写

/*** ReentrantReadWriteLock  读写锁*/public class ReadWriteLockTest {public static void main(String[] args) {MyCache myCache = new MyCache();for (int i=0;i<5;i++){final int temp=i;new Thread(()->{myCache.write(temp+"",temp+"");},"write线程"+i).start();}for (int i=0;i<5;i++){final int temp=i;new Thread(()->{myCache.read(temp+"");},"read线程"+i).start();}}
}class MyCache{private volatile Map<String,Object> map = new HashMap<>(16);private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();public void read(String key){reentrantReadWriteLock.readLock().lock();try{System.out.println(Thread.currentThread().getName()+"\t 正在读++++");try {Thread.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}Object result = map.get(key);System.out.println(Thread.currentThread().getName()+"\t 读完了++++"+result);}catch (Exception e){}finally {reentrantReadWriteLock.readLock().unlock();}}public void write(String key,Object object){reentrantReadWriteLock.writeLock().lock();try{System.out.println(Thread.currentThread().getName()+"\t 正在写---"+key);try {Thread.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}map.put(key,object);System.out.println(Thread.currentThread().getName()+"\t 写完了----");}catch (Exception e){}finally {reentrantReadWriteLock.writeLock().unlock();}}}
没有使用ReentrantReadWriteLock的结果:
write线程0	 正在写---0
write线程1	 正在写---1
write线程2	 正在写---2
write线程3	 正在写---3
write线程4	 正在写---4
read线程0	 正在读++++
read线程1	 正在读++++
read线程2	 正在读++++
read线程3	 正在读++++
read线程4	 正在读++++
write线程0	 写完了----
write线程1	 写完了----
write线程2	 写完了----
write线程4	 写完了----
read线程2	 读完了++++2
write线程3	 写完了----
read线程3	 读完了++++3
read线程1	 读完了++++1
read线程0	 读完了++++0
read线程4	 读完了++++4
使用ReentrantReadWriteLock的结果:
write线程0	 正在写---0
write线程0	 写完了----
write线程2	 正在写---2
write线程2	 写完了----
write线程1	 正在写---1
write线程1	 写完了----
write线程3	 正在写---3
write线程3	 写完了----
write线程4	 正在写---4
write线程4	 写完了----
read线程0	 正在读++++
read线程1	 正在读++++
read线程2	 正在读++++
read线程3	 正在读++++
read线程4	 正在读++++
read线程2	 读完了++++2
read线程4	 读完了++++4
read线程3	 读完了++++3
read线程0	 读完了++++0
read线程1	 读完了++++1

4、自旋锁

自旋锁(spinLock)

是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗缺点是循环会消耗CPU

下面是AtomicInteger的compareAndSet调用的底层的UnSafe的getAndAddInt就是采用了自旋锁的机制,

public class SpinLock {public static void main(String[] args) {SpinLockDome spinLockDome = new SpinLockDome();new Thread(()->{spinLockDome.myLock();try {Thread.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}spinLockDome.myUnLock();},"线程1").start();try {Thread.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(()->{spinLockDome.myLock();spinLockDome.myUnLock();},"线程2").start();}
}class SpinLockDome{AtomicReference<Thread> atomicReference = new AtomicReference<>();//加锁public void myLock(){Thread thread = Thread.currentThread();System.out.println(thread.getName()+"\t come lock");while(!atomicReference.compareAndSet(null,thread)){}}//解锁public void myUnLock(){Thread thread = Thread.currentThread();System.out.println(thread.getName()+"\t come in unlock");atomicReference.compareAndSet(thread,null);}
}
结果:
线程1	 come lock
线程2	 come lock
线程1	 come in unlock
线程2	 come in unlock

5、CountDownLatch

    public static void main(String[] args) {CountDownLatch countDownLatch = new CountDownLatch(5);System.out.println("我要锁门了");for (int i=0;i<5;i++){new Thread(()->{System.out.println(Thread.currentThread().getName()+"\t班长等我出去在锁门...");countDownLatch.countDown();//每次减1},"同学"+i).start();}try {countDownLatch.await();//只有当countDownLatch.getCount()等于0当时候才会释放,} catch (InterruptedException e) {e.printStackTrace();}System.out.println("我锁门了");}
结果:
我要锁门了
同学0	班长等我出去在锁门...
同学1	班长等我出去在锁门...
同学2	班长等我出去在锁门...
同学3	班长等我出去在锁门...
同学4	班长等我出去在锁门...
我锁门了

6、CyclicBarrier

和CountDownLock有点相反:CyclicBarrier的字面意思是可循环(Cyclic)使用的屏障(Barrier)。他要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活,线程进入屏障通过Cyclicbarrier的await()方法

public class CyclibarrierTest {public static void main(String[] args) {CyclicBarrier cyclicBarrier = new CyclicBarrier(5,()->{System.out.println("既然人都到齐了,我们就开始开会吧...");});for (int i=0;i<5;i++) {new Thread(() -> {System.out.println(Thread.currentThread().getName() + "\t等待其他学生开会...");try {cyclicBarrier.await();} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}}, "同学" + i).start();}}
}
结果:同学0	等待其他学生开会...
同学3	等待其他学生开会...
同学2	等待其他学生开会...
同学1	等待其他学生开会...
同学4	等待其他学生开会...
既然人都到齐了,我们就开始开会吧...
public class CyclicBarrier {/*** Each use of the barrier is represented as a generation instance.* The generation changes whenever the barrier is tripped, or* is reset. There can be many generations associated with threads* using the barrier - due to the non-deterministic way the lock* may be allocated to waiting threads - but only one of these* can be active at a time (the one to which {@code count} applies)* and all the rest are either broken or tripped.* There need not be an active generation if there has been a break* but no subsequent reset.*/private static class Generation {//内部类,这个类很重要boolean broken = false;}/** The lock for guarding barrier entry */private final ReentrantLock lock = new ReentrantLock();/** Condition to wait on until tripped */private final Condition trip = lock.newCondition();/** The number of parties */private final int parties;/* The command to run when tripped */private final Runnable barrierCommand;/** The current generation */private Generation generation = new Generation();//构建CyclicBarrier对象public CyclicBarrier(int parties, Runnable barrierAction) {if (parties <= 0) throw new IllegalArgumentException();this.parties = parties;//总共需要等几个this.count = parties;//计数的,每次调用await减1this.barrierCommand = barrierAction;}//这个方法就是await方法的实现private int dowait(boolean timed, long nanos)throws InterruptedException, BrokenBarrierException,TimeoutException {final ReentrantLock lock = this.lock;lock.lock();//加锁,防止被打断try {final Generation g = generation;if (g.broken)throw new BrokenBarrierException();if (Thread.interrupted()) {breakBarrier();throw new InterruptedException();}int index = --count;//在等线程的总量上一直减if (index == 0) {  // tripped 当减到0时boolean ranAction = false;try {final Runnable command = barrierCommand;if (command != null)//如果barrierCommand这个不为null,就执行command.run();ranAction = true;nextGeneration();return 0;} finally {if (!ranAction)breakBarrier();}}// loop until tripped, broken, interrupted, or timed outfor (;;) {try {if (!timed)trip.await();else if (nanos > 0L)nanos = trip.awaitNanos(nanos);} catch (InterruptedException ie) {if (g == generation && ! g.broken) {breakBarrier();throw ie;} else {// We're about to finish waiting even if we had not// been interrupted, so this interrupt is deemed to// "belong" to subsequent execution.Thread.currentThread().interrupt();}}if (g.broken)throw new BrokenBarrierException();if (g != generation)return index;if (timed && nanos <= 0L) {breakBarrier();throw new TimeoutException();}}} finally {lock.unlock();}}

7、Semaphore

信号量主要用于两个目的,一个是用于多个共享资源的互斥使用,另外一个用于并发线程数的控制。

public class SemaphreTest {public static void main(String[] args) {Semaphore semaphore = new Semaphore(3);for (int i=0;i<6;i++) {new Thread(() -> {try {
// 减1semaphore.acquire();System.out.println(Thread.currentThread().getName()+"\t抢到了车位...");Thread.sleep(3);System.out.println(Thread.currentThread().getName()+"\t停了3秒钟就走了...");} catch (InterruptedException e) {e.printStackTrace();}finally {
// 加1semaphore.release();}}, "车" + i).start();}}
}
acquire()  
获取一个令牌,在获取到令牌、或者被其他线程调用中断之前线程一直处于阻塞状态。
​
acquire(int permits)  
获取一个令牌,在获取到令牌、或者被其他线程调用中断、或超时之前线程一直处于阻塞状态。acquireUninterruptibly() 
获取一个令牌,在获取到令牌之前线程一直处于阻塞状态(忽略中断)。tryAcquire()
尝试获得令牌,返回获取令牌成功或失败,不阻塞线程。
​
tryAcquire(long timeout, TimeUnit unit)
尝试获得令牌,在超时时间内循环尝试获取,直到尝试获取成功或超时返回,不阻塞线程。
​
release()
释放一个令牌,唤醒一个获取令牌不成功的阻塞线程。
​
hasQueuedThreads()
等待队列里是否还存在等待线程。
​
getQueueLength()
获取等待队列里阻塞的线程数。
​
drainPermits()
清空令牌把可用令牌数置为0,返回清空令牌的数量。
​
availablePermits()
返回可用的令牌数量。

http://chatgpt.dhexx.cn/article/mKybjZlp.shtml

相关文章

C# 实现锁屏

首先&#xff0c;将窗体的FormBorderStyle设置为none&#xff0c;WindowState设为Maximized 让窗体占据整个页面。 form窗体代码&#xff1a; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using …

win11中睡眠唤醒后如何禁止锁屏界面及登录界面

第一步禁止锁屏界面 gpedit.msc 不显示锁屏-》已启用 第二步&#xff1a;不显示登陆框 netplwiz 经过以上设置以后&#xff0c;打开笔记本就可以直接显示桌面了。

办公计算机锁屏方法,电脑快速锁屏,办公室神技能,再也不怕别人看我的电脑了!...

原标题&#xff1a;电脑快速锁屏&#xff0c;办公室神技能&#xff0c;再也不怕别人看我的电脑了&#xff01; 现在快节奏的社会&#xff0c;电脑的更方面性能与功能是非常之大&#xff0c;我们对电脑的使用频率也越来越高&#xff0c;可见它对我们生活越来越重要。在我们平时使…

Android锁屏的解锁(九个点),使用画的方式,大致的步骤

这篇博客是全部代码(没有解释,纯代码): https://blog.csdn.net/weixin_44614751/article/details/103101104 效果: 第一步:添加背景 第二步:创建九个点 先读取每一个点的图片: creatDot 创建九个点: initNin

ubuntu 18.04取消自动锁屏功能

有时候几分钟不用Ubuntu&#xff0c;系统就自动锁屏了&#xff0c;这是一种安全措施&#xff0c;防止别人趁你不在时使用你的系统。但对于大部分人而言&#xff0c;这是没有必要的&#xff0c;尤其是Ubuntu虚拟机&#xff0c;里面没啥重要的东西&#xff0c;每次锁屏后需要重新…

Android 通知栏,锁屏播放音乐,类似音乐播放器

项目中需要用到播放音频的功能&#xff0c;想做一个类似酷狗、酷我这样的音频播放功能&#xff0c;在通知栏和锁屏时都可以操控音乐&#xff0c;开发中发现oppo reno手机在锁屏时不显示通知栏&#xff0c;研究了整整一天终于解决&#xff0c;特作记录&#xff0c;给遇到同样问题…

Android自定义锁屏实现----仿正点闹钟滑屏解锁

本文原创&#xff0c;转载请注明出处&#xff1a;http://blog.csdn.net/qinjuning 前几周看了下解锁的框架&#xff0c;基本上算是弄了个脸熟。看着别人花哨的解锁界面&#xff0c;心里也很痒痒的。于是&#xff0c;画了一天时间&#xff0c; 捣鼓出了这个成果----仿正点闹钟解…

易安卓打开Android系统中的解锁方式选择页面(锁屏方式选择)

感谢名单 感谢fylfyl2写的https://blog.csdn.net/fyilun/article/details/21257595 E4A打开锁屏方式页面 Intent intent new Intent(); ComponentName cm new ComponentName("com.android.settings","com.android.settings.ChooseLockGeneric"); inte…

Android锁屏的解锁(九个点),使用画的方式

一、效果展示: 这篇博客有解释大概的步骤: https://blog.csdn.net/weixin_44614751/article/details/103101199 二、代码部分: MainActivity.java中的代码: package com.example.drawunlock1;import androidx.appcompat.app.AppCompatActivity;import android.content.re…

让电脑不被锁屏的方法,亲测有效

通过JS来控制键盘&#xff0c;定时按下SCROLLLOCK键&#xff0c;达到电脑不会被锁屏的效果。 通常公司电脑都会自动锁屏&#xff0c;只是时间有长短。有时候闲着了不用电脑&#xff0c;但是却不想让电脑锁屏。那么可以用js代码来控制键盘的按键循环按下实现不锁屏的效果&#x…

android 强制锁屏app,自制力app强制锁屏

自制力app强制锁屏非常适合在学习工作中没有自律性的用户们&#xff0c;当打开app后开启锁屏状态&#xff0c;手机就打不开了&#xff0c;重启也不可能解除锁屏&#xff1b;在此期间&#xff0c;就可以免于手机的打扰&#xff0c;专注学习&#xff1b;感兴趣的小伙伴们快来下载…

android系统密码设置功能,手机锁屏密码怎么设置 三种安卓手机锁屏方式推荐

手机中有很多应用都是与金钱挂钩&#xff0c;特别是微信与支付宝等等既涉及到隐私又与财产关联&#xff0c;这是后手机的安全就尤为重要的&#xff0c;而手机的锁屏密码就是一道最基本的防护措施&#xff0c;那么手机锁屏密码怎么设置?来看看小编推荐的三种安卓手机锁屏方式吧…

Mac锁屏的几种方式

刚换了工作&#xff0c;公司里给配了MacBook&#xff0c;第一次使用&#xff0c;很多常见操作都不知道快捷键&#xff0c;今天来记录下锁屏的几种方式&#xff1a;电脑为MacBook Pro&#xff0c;OS为MacOS Sierra 10.12.3 1. ctrl shift 右上角开关机键 2. option comm…

C语言for循环结构经典练习

文章目录 一、for循环基本知识二、经典例题及解析1.水仙花数2.求规定范围内的完数3.求规定范围内质数4.计算阶乘之和5.计算55555555555555(类型)6.计算112123123412345(类型)7.判断用户输入正整数的位数8.判断某正整数是否为回文数9.九九乘法表10.统计用户输入的字符中&#xf…

Java基础语法——循环结构

每日正能量 趁你现在还有时间&#xff0c;尽你自己最大的努力&#xff0c;努力做成你最想做的那件事&#xff0c;成为你最想成为的那种人&#xff0c;过着你最想过的那种生活。这个世界永远比你想的要更精彩&#xff0c;不要败给生活。 循环结构 【本章内容】1. while循环 2. d…

Python - 循环结构

循环结构 &#x1f40d;While循环&#x1f40d;While…else…循环&#x1f40d;for循环&#x1f40d;for…else…循环&#x1f40d;循环体结束语句&#x1f40d;嵌套循环 本次主要介绍的是程序的循环结构逻辑。 循环就是按照一定的条件重复的去做一件事情&#xff0c;当条件不成…

While循环结构

1.while循环语句 1.1循环概念 循环是程序设计语言中反复执行某些代码的一种计算机处理过程 1.2 while循环的作用 重复执行某些代码 1.3 while循环的基本语法 while 条件&#xff1a; ____反复执行的代码 …… while及while中的代码块就是while循环代码块。看以下示例&#…

三种循环结构

循环结构&#xff1a;循环结构是指在程序中需要反复执行某个功能而设置的一种程序结构。它由循环体中的条件&#xff0c;判断继续执行某个功能还是退出循环。根据判断条件,循环结构又可细分为以下两种形式:先判断后执行的循环结构和先执行后判断的循环结构。下面将对各个循环结…

c语言中循环结构有什么作用,浅谈C语言中循环结构程序设计

高茂婵 吕雪 彭星星 孙新杰 摘要:现在人们对计算机中的算法的要求越来越高,顺序结构已经满足不了人们的需求,我们需要探索更高层次的操作算法。在程序设计中,我们操作的时候经常遇到需要重复执行的情况,而循环结构就恰恰满足了这个要求。在程序设计中,循环结构是算法中必…

Python循环结构

今天我们讲一下python的循环结构&#xff0c;习题比较多&#xff0c;大家多联系&#xff0c;有问题可以给我留言。 目录 一、Python中循环的介绍1、什么是循环&#xff1f;2、循环的作用3、循环的种类 二、while循环基本语法及其应用1、while循环的基本语法2、while循环的执行…