当前课程知识点:JAVA程序设计进阶 >  第二章 线程(中) >  2.1 线程同步的思路 >  Video

返回《JAVA程序设计进阶》慕课在线视频课程列表

Video在线视频

Video

下一节:Video

返回《JAVA程序设计进阶》慕课在线视频列表

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

就会导致我们的售票线程

陷入了死循环 一直出不来

所以这个程序我们做这么点变化了以后

很有意思

同学们可以下去自己把这个代码

加进去以后看看这个效果

是不是出现死循环

一直出不来的情况

线程同步的思路就介绍到这里

谢谢大家

JAVA程序设计进阶课程列表:

第一章 线程(上)

-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

第六章 Java虚拟机

-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

第七章 深入集合Collection

-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

Video笔记与讨论

也许你还感兴趣的课程:

© 柠檬大学-慕课导航 课程版权归原始院校所有,
本网站仅通过互联网进行慕课课程索引,不提供在线课程学习和视频,请同学们点击报名到课程提供网站进行学习。