《线程》——多线程同步实例剖析
来源:互联网 发布:发改委大数据专项2016 编辑:程序博客网 时间:2024/05/22 14:06
线程这个名词我们在学习操作系统的时候就接触过了,线程又称为轻量级进程,那进程是什么哪?大家可以跟随我的超链接看一下百度百科的解释。
1、简单线程实例,解决两个售票窗口售票问题。
具体的业务逻辑是:有两个售票台共同售票,票的总数是一定的(count),售票台1和售票台2共同访问票的总数count,我们开启两个线程使两个售票台共同售票,那么会出现什么情况那?请看下面的代码
1.1、两个线程没有同步前的代码——读脏数据
class Program { static void Main(string[] args) { Tick t1 = new Tick("1号售票台 "); Tick t2 = new Tick("2号售票台 "); Console.ReadLine(); } }
Tick类
public class Tick { public static int count = 100; public Tick(string name) { Thread t = new Thread(sell); t.Name = name; t.IsBackground = true; t.Start(); } public void sell() { while (count > 1) { Tick.count--; Console.WriteLine(System.Threading.Thread.CurrentThread.Name + ",剩余{0}张!", count); } } }效果图如下所示
问题一:我将前七条记录编号,从上往下看这七条记录,1号位置余票是99张,2号位置余票是97张,更搞笑的是都是售票台1在卖票,没道理啊!那第98张票去哪了?
问题二:再看4号位与5号位,4号位余票95张,是售票台1在卖票,5号位余票98张,是售票台2在卖票,哎,第98张票终于找到了,可是问题又来了,票的总数是一定的,售票台1和售票台2都是在同一个售票点卖票,从4号位的95张余票再到5号位的98张余票,车票竟然越卖越多了,这是怎么回事?
问题三:再看5号位与6号位,都是售票台2在卖票,就卖了一次票(一次卖一张),余票从98变成了93,不应变成97才对嘛,余票怎么少了4张,这究竟是怎么回事?
1.2、问题剖析
仔细看看代码,按照卖票的思维逻辑走一趟,先解屡屡问题一,余票从99张变成了97张,这是因为当售票台1卖完第一张票,剩余99张票的时候,售票台2插进来卖票了,也就是说线程2开始卖票了,线程2的代码执行到了Tick.count--;这个地方,此时count变成98,还没等线程2执行Console.WriteLine(System.Threading.Thread.CurrentThread.Name + ",剩余{0}张!", count)这段代码的时候,线程1又开始卖票,也就是说线程2的第一次循环还没有执行完就被迫让出CPU(单核)让线程一执行,线程2只好将此时的现场信息保存到自己的工作缓存中去(此时余票是98),这是为了等下次线程2在CPU中运行的时候根据现场信息再执行代码,也就是说当线程2再次接管CPU的时候是直接运行第一次循环的还没有执行的那段代码,也就是这段代码Console.WriteLine(System.Threading.Thread.CurrentThread.Name + ",剩余{0}张!", count)。所以余票剩余98张会出现在5号位置。
用分析问题一的思维逻辑分析问题二和问题三,就不用我写出来了吧!这就是多线程访问共享变量出现的问题,那么怎样才能解决这个问题哪?那么,线程同步该上场了,什么是线程同步?这个问题可以追溯的进程同步,点击超链接看看进程同步。
2、 现在给线程加锁试试,看下面的代码
public class Tick { public static int count = 100; public static object locker = new object(); public Tick(string name) { Thread t = new Thread(sell); t.Name = name; t.IsBackground = true; t.Start(); } public void sell() { while (count > 1) { lock (locker) { Tick.count--; Console.WriteLine(System.Threading.Thread.CurrentThread.Name + ",剩余{0}张!", count); } } } }看看效果图
从图中可以看出,当线程t1和线程t2切换的时候,也就是1号售票口与2号售票切换的时候,余票的数据是正确的。简单的加了一个锁就可以变成这样了。那加锁的原理是什么哪?先看一张图。
2.1、每个对象其实在内存中都会分配一个对象头空间,其中最后2个bit 用来存放锁标识,当线程1给某一代码块加锁后,对象头标识里面数据就变成了1,在线程1没有执行完具体的操作之前,也就是线程1没有跳出上面的那个锁之前(跳出锁后标识自动变为0),线程2是不能访问被锁住的代码块的,因为线程2在访问这个代码块之前,也会访问这个锁对象,判断锁标识,此时锁标识是1,不是0,所以线程2不能进入被锁住的代码块。对应上面的问题一和问题2,也就是说当线程1每次循环执行过程中,只要每次循环没有执行完毕,锁就不会释放,其它线程就不会访问共享变量count,也就不会造成数据不同步问题了。
2.2、lock的本质:调用Monitor对象的Enter()方法来加锁,调用Monitor的Exit()方法解锁。
3、小结
将上面的问题总结一下,线程有同步也需要前提:同步需要两个或者两个以上的线程、多个线程使用的是同一个锁。线程同步的特点:即使获取了CPU的时间片,也无法执行。线程同步缺点:当线程相当多时,因为每个线程都会去判断同步上的锁,这很耗费资源,降低程序的运行效率。所以说,线程也不是越多越好,要适当着用。
- 《线程》——多线程同步实例剖析
- 多线程——线程同步
- 多线程同步互斥实例——使用synchronized实现线程通信和互斥
- 多线程同步互斥实例——多个线程共享数据
- 多线程同步互斥实例——使用synchronized实现线程通信和互斥
- 多线程编程实例----线程同步之事件
- 多线程编程实例----线程同步之互斥量
- java多线程——线程同步问题
- MFC多线程 —— 线程同步
- MFC多线程 —— 线程同步
- 多线程编程——线程同步方法
- Java多线程——线程同步
- Java多线程——线程同步(2)
- C++多线程——线程同步
- 多线程编程2——线程同步
- Java多线程编程— 线程同步问题
- 线程同步问题,线程上锁---多线程“卖票”实例
- Thread(线程)详解2—多线程同步和线程池
- json-handle:json可视化工具
- 刷题、OJ 1903 有假币
- HDU 1242 Rescue(dfs深搜)
- C#成神之路<19> C#使用磁盘数据文件(3)
- km入门-----hdu2255
- 《线程》——多线程同步实例剖析
- Windows 下免费的 C++ 开发平台 EasilyGCC 正式发布
- USACO 1.2 namenum
- iPone应用开发 UIView 常用属性和方法
- JVM栈溢出
- leetcode(2) Add Two Numbers
- LVS_DR 模式
- poj 1230(贪心)
- HDU-1021-Fibonacci Again ( 找规律 + Fibonacci )