第十二节:深究内核模式锁的使用场景(自动事件锁、手动事件锁、信号量、互斥锁、读写锁、动态锁)
一. 整体介绍
温馨提示:内核模式锁,在不到万不得已的情况下,不要使用它,因为代价太大了,有很多种替代方案。
内核模式锁包括:
①:事件锁
②:信号量
③:互斥锁
④:读写锁
⑤:动态锁
二. 事件锁
事件锁包括:
A. 自动事件锁(AutoResetEvent)
使用场景:可以用此锁实现多线程环境下某个变量的自增.
现实场景: 进站火车闸机,我们用火车票来实现进站操作.
true: 表示终止状态,闸机中没有火车票
false: 表示非终止状态,闸机中此时有一张火车票
B.手动事件锁(ManualResetEvent)
现实场景:有人看守的铁道栅栏(和自动事件锁不一样,不能混用)
true: 栅栏没有合围,没有阻止行人通过铁路
false:栅栏合围了, 阻止行人通过
* 下面案例发现,锁不住,自增仍然是无序的输出了.
* 核心方法:WaitOne和Set
代码实践-自动事件锁:
1 static AutoResetEvent autoResetLock1 = new AutoResetEvent(true);2 static AutoResetEvent autoResetLock2 = new AutoResetEvent(false);3 static int num2 = 0;4 {5 //1. 能输出6 {7 autoResetLock1.WaitOne();8 Console.WriteLine("autoResetLock1检验通过,可以通行");9 autoResetLock1.Set();
10 }
11
12 //2. 不能输出
13 {
14 autoResetLock2.WaitOne();
15 Console.WriteLine("autoResetLock2检验通过,可以通行");
16 autoResetLock2.Set();
17 }
18
19 //3.下面代码的结果:num从0-249,有序的发现可以锁住。
20 {
21 for (int i = 0; i < 5; i++)
22 {
23 Task.Factory.StartNew(() =>
24 {
25 for (int j = 0; j < 50; j++)
26 {
27 try
28 {
29 autoResetLock1.WaitOne();
30 Console.WriteLine(num2++);
31 autoResetLock1.Set();
32 }
33 catch (Exception ex)
34 {
35 Console.WriteLine(ex.Message);
36 }
37
38 }
39 });
40 }
41 }
42 }
代码实践-手动事件锁:
1 static int num2 = 0;2 static ManualResetEvent mreLock = new ManualResetEvent(true);3 //下面代码锁不住,仍然是无序的输出了4 {5 for (int i = 0; i < 5; i++)6 {7 Task.Factory.StartNew(() =>8 {9 for (int j = 0; j < 50; j++)
10 {
11 try
12 {
13 mreLock.WaitOne();
14 Console.WriteLine(num2++);
15 mreLock.Set();
16 }
17 catch (Exception ex)
18 {
19 Console.WriteLine(ex.Message);
20 }
21
22 }
23 });
24 }
25 }
三. 信号量
信号量:
* 核心类:Semaphore,通过int数值来控制线程个数。
* 通过观察构造函数 public Semaphore(int initialCount, int maximumCount);:
* initialCount: 可以同时授予的信号量的初始请求数。
* maximumCount: 可以同时授予的信号量的最大请求数。
* static Semaphore seLock = new Semaphore(1, 1); //表示只允许一个线程通过
* 下面的案例可以有序的输出。
* 核心方法:WaitOne和Release
代码实践:
1 static Semaphore seLock = new Semaphore(1, 1); //只允许一个线程通过 2 //下面代码锁住了,可以有序的输出3 {4 for (int i = 0; i < 5; i++)5 {6 Task.Factory.StartNew(() =>7 {8 for (int j = 0; j < 50; j++)9 {
10 try
11 {
12 seLock.WaitOne();
13 Console.WriteLine(num2++);
14 seLock.Release();
15 }
16 catch (Exception ex)
17 {
18 Console.WriteLine(ex.Message);
19 }
20
21 }
22 });
23 }
24 }
四. 互斥锁
互斥锁:
核心方法:WaitOne和ReleaseMutex
下面案例可以锁住,有序输出
总结以上三种类型的锁,都有一个WaitOne方法,观察源码可知,都继承于WaitHandle类。
代码实践:
1 static Mutex mutex = new Mutex();2 //下面代码锁住了,可以有序的输出3 {4 for (int i = 0; i < 5; i++)5 {6 Task.Factory.StartNew(() =>7 {8 for (int j = 0; j < 50; j++)9 {
10 try
11 {
12 mutex.WaitOne();
13 Console.WriteLine(num2++);
14 mutex.ReleaseMutex();
15 }
16 catch (Exception ex)
17 {
18 Console.WriteLine(ex.Message);
19 }
20
21 }
22 });
23 }
24 }
五. 读写锁
读写锁(ReaderWriterLock):
背景:多个线程读,一个线程写,如果写入的时间太久,此时读的线程会被卡死,这个时候就要用到读写锁了。
锁读的两个核心方法:AcquireReaderLock和ReleaseReaderLock。
锁写的两个核心方法:AcquireWriterLock和ReleaseWriterLock。
代码实践:
1 static ReaderWriterLock rwlock = new ReaderWriterLock();2 private void button24_Click(object sender, EventArgs e)3 {4 #region 01-读写锁5 {6 //开启5个线程执行读操作7 for (int i = 0; i < 5; i++)8 {9 Task.Run(() =>
10 {
11 Read();
12 });
13 }
14 //开启1个线程执行写操作
15 Task.Factory.StartNew(() =>
16 {
17 Write();
18 });
19 }
20 #endregion
21
22 }
23 /// <summary>
24 /// 线程读
25 /// </summary>
26 static void Read()
27 {
28 while (true)
29 {
30 Thread.Sleep(10);
31 rwlock.AcquireReaderLock(int.MaxValue);
32 Console.WriteLine("当前 t={0} 进行读取 {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
33 rwlock.ReleaseReaderLock();
34 }
35 }
36 /// <summary>
37 /// 线程写
38 /// </summary>
39 static void Write()
40 {
41 while (true)
42 {
43 Thread.Sleep(300);
44 rwlock.AcquireWriterLock(int.MaxValue);
45 Console.WriteLine("当前 t={0} 进行写入 {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
46 rwlock.ReleaseWriterLock();
47 }
48 }
六. 动态锁
动态锁(CountdownEvent):
* 作用:限制线程数的一个机制。
* 业务场景:有Orders、Products、Users表,我们需要多个线程从某一张表中读取数据。
* 比如:Order表10w,10个线程读取。(每个线程读1w)
Product表5w,5个线程读取。(每个线程读1w)
User表2w,2个线程读取。(每个线程读1w)
三个核心方法:
①.Reset方法:重置当前的线程数量上限。(初始化的时候,默认设置一个上限)
②.Signal方法:将当前的线程数量执行减1操作。(使用一个thread,这个线程数量就会减1操作,直到为0后,继续下一步)
③.Wait方法:相当于我们的Task.WaitAll方法。
代码实践:
1 //初始化线程数量上限为10.2 static CountdownEvent cdLock = new CountdownEvent(10);3 private void button25_Click(object sender, EventArgs e)4 {5 //加载Orders搞定6 cdLock.Reset(10);7 for (int i = 0; i < 10; i++)8 {9 Task.Factory.StartNew(() =>
10 {
11 LoadOrder();
12 });
13 }
14 cdLock.Wait();
15 Console.WriteLine("所有的Orders都加载完毕。。。。。。。。。。。。。。。。。。。。。");
16
17 //加载Product搞定
18 cdLock.Reset(5);
19 for (int i = 0; i < 5; i++)
20 {
21 Task.Run(() =>
22 {
23 LoadProduct();
24 });
25 }
26 cdLock.Wait();
27 Console.WriteLine("所有的Products都加载完毕。。。。。。。。。。。。。。。。。。。。。");
28
29 //加载Users搞定
30 cdLock.Reset(2);
31 for (int i = 0; i < 2; i++)
32 {
33 Task.Factory.StartNew(() =>
34 {
35 LoadUser();
36 });
37 }
38 cdLock.Wait();
39 Console.WriteLine("所有的Users都加载完毕。。。。。。。。。。。。。。。。。。。。。");
40
41 Console.WriteLine("所有的表数据都执行结束了。。。恭喜恭喜。。。。");
42 Console.Read();
43 }
44 static void LoadOrder()
45 {
46 //书写具体的业务逻辑
47 Console.WriteLine("当前LoadOrder正在加载中。。。{0}", Thread.CurrentThread.ManagedThreadId);
48 //线程数量减1
49 cdLock.Signal();
50
51 }
52 static void LoadProduct()
53 {
54 //书写具体的业务逻辑
55 Console.WriteLine("当前LoadProduct正在加载中。。。{0}", Thread.CurrentThread.ManagedThreadId);
56 //线程数量减1
57 cdLock.Signal();
58 }
59 static void LoadUser()
60 {
61 //书写具体的业务逻辑
62 Console.WriteLine("当前LoadUser正在加载中。。。{0}", Thread.CurrentThread.ManagedThreadId);
63 //线程数量减1
64 cdLock.Signal();
65 }
第十二节:深究内核模式锁的使用场景(自动事件锁、手动事件锁、信号量、互斥锁、读写锁、动态锁)相关推荐
- 自旋锁/互斥锁/读写锁/递归锁的区别与联系
自旋锁 互斥锁 读写锁 递归锁 互斥锁(mutexlock): 最常使用于线程同步的锁:标记用来保证在任一时刻,只能有一个线程访问该对象,同一线程多次加锁操作会造成死锁:临界区和互斥量都可用来实现此锁 ...
- java读写锁降级_java的读写锁中锁降级的问题
读写锁是什么我就不多说了,下面说什么是锁降级 锁降级: 锁降级指的是写锁降级成为读锁.如果当前线程拥有写锁,然后将其释放,最后再获取读锁,这种分段完成的过程不能称之为锁降级.锁降级是指把持住(当前拥有 ...
- java dom4j读写锁,java锁的深度化-重入锁,读写锁,乐观锁,悲观锁
1.重入锁 目的:避免死锁的现象 锁作为并发共享数据,保证一致性的工具,在java平台有多种实现synchronized(重量级)和ReentrantLock(轻量级)等等,这些已经写好提供的锁为我们 ...
- JAVA 读写锁中锁降级的必要性
既然大家看到了这篇博客,也应该都大概了解了读写锁的一些概念,咱们直接上主题,读写锁中为什么要用锁降级: 先引用书上的一段话说说锁降级的概念: 锁降级指的是写锁降级成为读锁.如果当前线程 ...
- 从自旋锁、睡眠锁、读写锁到 Linux RCU 机制讲解
总结一下 O/S 课程里面和锁相关的内容. 本文是 6.S081 课程的相关内容总结回顾结合 Real World 的 Linux 讲解各种锁和 RCU lock free 机制原理, 前置知识是基本 ...
- 同步方法中的锁对象_互斥锁与读写锁:如何使用锁完成Go程同步?
图转自https://colobu.com/2018/12/18/dive-into-sync-mutex/ 这张图容易让人产生误解,容易让人误以为goroutine1获取的锁,只有goroutine ...
- 【剧前爆米花--爪哇岛寻宝】常见的锁策略——乐观锁、读写锁、重量级锁、自旋锁、公平锁、可重入锁等
作者:困了电视剧 专栏:<JavaEE初阶> 文章分布:这是关于操作系统锁策略的文章,包括乐观锁.读写锁.重量级锁.自旋锁.公平锁.可重入锁等,希望对你有所帮助! 目录 乐观锁和悲观锁 悲 ...
- 【Linux内核】RW读写锁机制
读写锁机制 Linux内核中读写锁的机制是一种多读单写的锁机制,它允许多个读操作同时进行,但只能有一个写操作进行.当有写操作时,所有读操作都会被阻塞,直到写操作完成. 在内核中,读写锁主要由以下两个结 ...
- java锁(公平锁和非公平锁、可重入锁(又名递归锁)、自旋锁、独占锁(写)/共享锁(读)/互斥锁、读写锁)
前言 本文对Java的一些锁的概念和实现做个整理,涉及:公平锁和非公平锁.可重入锁(又名递归锁).自旋锁.独占锁(写)/共享锁(读)/互斥锁.读写锁 公平锁和非公平锁 概念 公平锁是指多个线程按照申请 ...
最新文章
- Leetcode 199. 二叉树的右视图 解题思路及C++实现
- POJ - 3261 Milk Patterns(二分+后缀数组)
- cni k8s 插件安装_K8S 之 Flannel网络插件安装
- 利用SecureCRT在linux与Windows之间传输文件
- Redis 持久化——RDB
- Android OpenGLES2.0(十四)——Obj格式3D模型加载
- python做单元测试_如何使用python做单元测试?
- sparksql 操作hive_三十六、图解SparkSQL运行原理
- 全幅與APS-C MTF曲線解讀說明
- 《R语言初学指南》一1.2 向量
- Tomcat下载——tomcat7、tomcat8、tomcat9官网下载链接
- 前端如何播放m3u8格式的视频
- 填空什么的月牙_部编一年级上册语文第四单元知识梳理填空,附答案
- Linux 返回根目录,返回主目录
- 联想sr550服务器虚拟机,联想(Lenovo)SR550
- 阿里云OSS对象存储-图文详解
- [汇编语言] 循环与分支程序设计 例题
- 超级牛逼的立体画,太厉害了!
- Android app本地图片转换成Drawable对象的方法
- iOS 图片转base64编码
热门文章
- Golang tcp转发 remoteAddr错误
- C# Json转对象
- Python——assert(断言函数)
- ASP.NET MVC中的模型装配 封装方法 非常好用
- JVM JRE JDK,这些东西到底是什么?(转载)
- 用WebORB实现flex + .net后台的Remoting
- php对话框制作,织梦系统“提示窗口对话框类”详解,oxwindow.class.php、catalog_do.php...
- java的jsp要下载吗_jsp、java下载附件
- controller调用controller的方法_SpringCloud(5):Feign整合Ribbon和Hystrix来进行远程调用与服务熔断...
- 信号调制疑问_DSM 调制器simulink仿真分析