当前课程知识点:JAVA程序设计进阶 > 第二章 线程(中) > 2.1 线程同步的思路 > Video
这一节我们将介绍
线程同步的思路
我们将通过几个例子
来介绍线程同步的思想
多线程的同步控制
我们在运行线程过程当中
经常碰到的情况就是这个多个线程
在同时运行
同时运行的话
它们之间是有一些状态需要交互的
比如说同时运行几个线程
它需要共享一些数据
也就是说它们有可能会共同访问
一些数据
但这个数据
为了保证它访问的正确性和安全性
可能在某一个时刻
只允许一个线程来对这个数据进行操作
那我们这种现象我们称之为
线程之间的这个互斥
那么这里边最经典的一个问题
就是生产者消费者问题
也就是说有两个线程
一个是模拟生产者
另外一个线程模拟消费者
那生产者生产出来的东西
要存到某一个数据当中
而消费者需要消费的数据
然后也是从刚才这个保存数据
去进行消费
也就是说我们假设
我们一个生产者线程
负责往数据区里写数据
那消费者从这个同一个数据读数据
两个线程它们是可以同时执行
但是如果这个数据区已经
这个满的话
生产者要等消费者
取走一些数据后才能写
如果数据区是空的话
消费者也不能够直接消费
因为没有数据可消费
它又必须等待生产者
写入一些数据后
再去把这个数据读出来
那这个生产者消费者问题的话
就会很好的反映出
我们多线程之间的同步控制的方式
那我们后面马上会举几个例子
来帮助大家理解
我们现在要用两个线程
来模拟这个存票和售票的这个过程
我们假设我们的售票处
一开始没有票
然后有一个线程专门去产生票
然后把票存到这个售票处
另外一个线程就负责说
把拿到的票就存的票往外卖票
那这时候我们首先要创造一个
票类的对象
票类的对象是来模拟我们的这个票的
然后存票的线程和售票的线程
都去访问它
那访问它的过程当中
大家就可以体会到
多线程对一个共同数字
进行操作的一个过程
我们来看一下这个代码吧
public class producer AndCnsumer
然后看到主方法
Tickets t=new Tickets(10)
好了 我们建立了一个票的对象
票的一个总数是10
也就是说我们这个想让
产生票的这个线程
要最多产生10张票
然后这个卖票的话
它最后要把这10张票给卖掉
然后紧接着我们看这一行代码
new Consumer(t).start
启动卖票线程
new producer (t).start
启动这个存票线程
那我们下面看一下
这个consumer和producer这两个类
都分别会干什么事情
那之前的话
我们先看一下这个票这个类
class tickets
class ticket里面有一个
成人变量int number等于0
也就是说票号从0开始计数
int size也就是总的票数是多少
boolean available=false
表示目前是不是有票可以进行销售
那我们看一下这里面的构造方法
public tickets(int size)
这个里面就是代码就是this.size=size
也就是说通过构造方法
把我们的票的总数传递进来
那刚刚在我们上一个类当中
已经是定义了票的总数就是10张
我们现在看一下这个producer
这个类的功能
class producer extends Thread
它继承了所有类
表明它想充当一个线程
我们看一下它的成人变量
tickets(t)=null
然后它的构造方法
是把tickets(t)进行一个赋值
如果我们看它的run方法
while t. number小于t.size
这是什么意思呢
也就是说这个要产生的票号
比我们需要产生票的总数还小
就意味着我们可以生产这个票
所以它就打印出来
system out println
(producer puts ticket)
加上这个++t.number
也就是把这个票号这个打印出来
然后这个票号再累计往上加1
那生产出票来以后
马上就是说t.available=true
也就是说有票可供销售了
所以把这个available置为真
我们马上看一下这个consumer这个类
这是一个售票线程
class consumer extends Thread
然后tickets t=null
然后int i=0
然后我们再看一下这个
它的构造方法
public consumer(ticketst)
那也就是把
通过各种方法把这个tickets这个对象
把它传进来
那我们下面看一下
它的run方法
while i小于t.size
就是说它想要卖的票的话
这个数量的话
比我们总票数还要小
那他应该是说打算要卖票了
那它怎么判断呢
if(t available true)
也就是说生产出来票里面有可卖的
并且小于t.number
那就是它想要卖的票
它的票号比我们这个存票
当中的这个票号还要小的话
它就可以卖了
那怎么卖呢
那就打印出来
system out println
consumer buys ticket加上++i
那什么时候票卖完了呢
if i==t.number
也就是说它自己要卖的这个票的
那个票号和我们这个tickets
这个对象中
它的这个票号
最大的票号是一样的
就表明它那个没有票可卖了
这时候它就会把这个available
就是票中的available这个标志
设置为false
也就是表明无票可卖
我们看一下这几个类执行的结果
可以告诉大家每一次运行结果
都不太一样的
我们先看一下左边
某次运行的结果
那左边的话是产生了八张票了以后
他又立马卖掉了8张票
紧接着又产生第9张第10张票
最后又卖了这个第9第10张
那另外一次运行结果
大家可以看到
这个就相对有点意思
它先产生了两张票
紧接着又消耗掉了
这个相应的票
最后再把它这个
整体上都消耗结束了
我们再看这个例子
我们如果做一些改变的话
会有什么有意思的现象出现
首先我们把存票售票
两个线程的启动迅速稍微改变一下
原来是什么呢
原来是先new consumer再start
然后再new producer再start
那现在我们把这个程序变成什么样
大家看那个最后面这个两行代码
我们先new producer然后再启动
然后再new consumer再启动
那改了一下的话会发现
它的运行结果始终是先生成10张票
然后再卖掉10张票
那这个运行结果
基本上都是这样
那原因就是在于说这两个线程的话
它们是同一优先级
只不过是说这个producer
先这个排在前面
所以的话从调度上
往往会调度它这个producer先执行
那它一执行呢就把这个票都生产完了
然后再等待着卖票的程序
把它去卖掉
这是一种有意思的这个现象
那我们再把程序
我们再做一点小改动
会有什么变化呢
比如说我们希望售票程序
它不要那么快的这个卖完
我们在这个售票程序的那个
就是consumer那个代码里面
加上一行加上什么呢
就是if i==t.number
我们try Thread .sleep(1)
让这个线程休眠一毫秒
让它稍微休息一下
然后接着再把状态
t.available把它设置为false
如果我们这么改的话
会出现什么情况呢
实际上这么改的话
就会容易让程序出现错误
错误的原因是什么
原因是当票这个 这个
售票线程运行到
t.available=false之前的话
它会休眠一毫秒
当这个售票线程休眠的时候
我们的cpu就会被线程调度去
调度给我们的存票线程
存票线程它就可以这个执行
执行过程的话
它就会把available设置为true
然后整个这个存票线程它就会结束
一直到它结束的时候
它的available还是true
那这个时候呢
存票线程运行结束了
然后同时我们这个售票线程
经过一毫秒的休眠它又醒过来了
醒过来的时候
这时候就会java的线程调度去
就会让这个售票线程来执行
售票线程执行的话
它就会立刻执行t.available=false
就把available设置为false
那这个时候情况是怎么样的呢
情况是这样的
就是我们的售票号是小于存票数
而且由于存票线程已经结束了
它再不能把available设置为true
就会导致我们的售票线程
陷入了死循环 一直出不来
所以这个程序我们做这么点变化了以后
很有意思
同学们可以下去自己把这个代码
加进去以后看看这个效果
是不是出现死循环
一直出不来的情况
线程同步的思路就介绍到这里
谢谢大家
-1.0 导学
--Video
-1.1 线程的基本概念
--Video
-1.1 线程的基本概念--作业
-1.2 通过Thread类创建线程
--Video
-1.2 通过Thread类创建线程--作业
-1.3 线程的休眠
--Video
-1.3 线程的休眠--作业
-1.4 Thread类详解
--Video
-1.5 通过Runnable接口创建线程
--Video
-1.5 通过Runnable接口创建线程--作业
-1.6 线程内部的数据共享
--Video
-2.0 导学
--Video
-2.1 线程同步的思路
--Video
-2.2 线程同步的实现方式—Synchronization
--Video
-2.3 线程的等待与唤醒
--Video
-2.4 后台进程
--Video
-2.5 线程的生命周期与死锁
--Video
-2.6 线程的调度
--Video
-3.0 导学
--Video
-3.1 线程安全与线程兼容与对立
--Video
-3.2 线程的安全实现-互斥同步
--Video
-3.3 线程的安全实现-非阻塞同步
--Video
-3.4 线程的安全实现-无同步方案
--Video
-3.5 锁优化
--Video
-4.0 导学
--Video
-4.1 URL对象
--Video
-4.2 URLConnection对象
--Video
-4.3 Get请求与Post请求
--Video
-4.4 Socket通信原理
--Video
-4.5 Socket通信实现
--Video
-5.0 导学
--Video
-5.1 Socket 多客户端通信实现
--Video
-5.2 数据报通信
--Video
-5.3 使用数据报进行广播通信
--Video
-5.4 网络聊天程序
--Video
-6.0 导学
--Video
-6.1 Java虚拟机概念
--Video
-6.2 Java虚拟机内存划分
--Video
-6.3 Java虚拟机类加载机制
--Video
-6.4 判断对象是否存活算法及对象引用
--Video
-6.5 分代垃圾回收
--Video
-6.6 典型的垃圾收集算法
--Video
-6.7典型的垃圾收集器
--Video
-7.0 导学
--Video
-7.1 集合框架与ArrayList
--Video
-7.2 LinkedList
--Video
-7.3 HashMap与HashTable
--Video
-7.4 TreeMap与LinkedHashMap
--Video
-7.5 HashSet
--Video
-8.0 导学
--Video
-8.1 Java反射机制
--Video
-8.2 Java静态代理
--Video
-8.3 Java动态代理
--Video
-8.4 Java 反射扩展-jvm加载类原理
--Video
-8.5 Java进阶课程总结
--Video