当前课程知识点:JAVA程序设计进阶 >  第二章 线程(中) >  2.2 线程同步的实现方式—Synchronization >  Video

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

Video在线视频

Video

下一节:Video

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

Video课程教案、知识点、字幕

在上一节当中

我们已经介绍了线程同步的思想

那在这一节当中

我们将介绍采用何种方式

来实现线程同步

那怎么样才能够避免这个情况呢

就会用到我们线程同步的这个知识

线程同步往往是和我们线程的互斥

这个在一起的

什么叫互斥呢

许多线程在同一个共享上操作

而互不干扰

这就要求同一时刻

只有一个线程访问该共享数据

那因此有些方法或程序

在同一时间只能被一个线程执行

我们把它称之为监视区

那其实线程之间也是可以协作的

也就是说多个线程

可以有条件的同时操作

有一些共享数据

执行监视区代码的线程

在条件满足的情况下

它也可以允许其他线程

进入到这个监视区

那我们怎么实现这种互斥与同步

我们就需要用到关键字synchronized

synchronized的作用

是用于指定需要同步的代码段

或者方法

那这个synchronized

它是实现了一个

这个对象锁的这个交互

那一般它的这个代码格式是这样的

你先写synchronized

后面一个小括号

里面是你要交互的这个对象

也就是你要访问的这个对象

然后再把代码段放进去

也就是你访问这个对象的时候

你要做一些操作

那这个从语法上这么写完了以后

那synchronized的功能是干嘛呢

它首先要判断

你这个对象的锁是否存在

如果这个锁存在的话

就可以执行后面这个代码段

如果这个锁不存在

为什么不存在呢

因为这个锁已经被其他线程拿到了

然后其他线程正在访问这个对象

所以你这个当前对象

想访问这个数据的话

你又访问不了

那你这个线程就得进入等待状态

一直到其他线程释放出这个锁以后

你才能得到锁

那什么情况下会释放锁呢

也就是说我这个线程

在synchronized那个代码段

一执行完了以后

就会把这个对象的锁给释放出来

那有了synchronized这个关键字的

这种作用

我们就考虑说

怎么把它运用到我们刚才

这个售票存票的这个过程当中

那我们就希望说

把我们的存票线程和售票线程

让它设置成一种互斥关系

也就是说这两个线程

任意一个去访问ticket这个对象的时候

就不允许另外一个线程

去访问ticket这个对象

那对于ticket这样的对象的话

它就有一个锁

其他线程想访问ticket这样对象的话

它就必须争夺这个锁

那比如说当某个线程A

获得这个对象的锁后

线程B就必须等待线程A完成

自己规定的操作

释放出锁以后

这个线程B才能获得这个对象的锁

然后它才会执行线程B中的

自己希望的操作

那我们利用synchronized这个关键字

来去改造一下

刚才我们上述的这个存票售票的过程

我们看一下这个类

class producer extend Thread

然后tickets t=null

然后这个producer的构造方法

就是把这个tickets t对象传进去

然后我们看一下public void run方法

然后while t.number小于t.size

也就是我们产生了这个票号

比我们的这个票的总量小

然后这个时候

我们看看这个语句怎么改进来

synchronized(t)也就是说

我在我这个线程producer当中

我现在要访问我的这个tickets

这个对象t

我用synchronized那个加上以后

我要申请对象t的锁

而假定运行过程中

我申请到了 我做什么事情呢

就是这system.out.println

然后producer puts ticket

然后++t.number

也就是说我生产出一张票来

然后t.available=true

然后说有票可卖了

其实说白了最重要就是

把这两行代码变成一个(原子)操作

就是在执行过程中

不可能被打散执行

然后当这个synchronized范围

这些语句执行完了以后

它自然而然就会释放对象t的锁

再往下那句代码

system.out println(producer ends)

所以大家看看我们这里已经

用synchronized来改造了一下

我们producer的这个代码

那我们再看看怎么用synchronized

来改造一下这个consumer这个代码

好 我们直接就看改造的那部分了

while i小于t.size

也就是说我要卖的票

比我们的这个票的这个总数

这个还是要小

说明我可以卖

好 我卖票的时候我synchronized(t)

也就是说申请对象t的锁

也就是申请对象t的锁的话

就要求我这时候就是我这些人

去访问这个ticket这个对象

别的进程不允许访问在这个时刻

那我访问ticket对象

我要做什么事情呢

大家看里面的代码

if(t.available=true&&

i小于等于t.number

也就是说有票可卖

而且我想要卖的这个票

比我们那个生产出来的票号

要小于等于它

那我就可以卖票了

那怎么卖呢

那system.out.println

consumer buys ticket+(++i)

也就是打印出来我把票卖掉了

然后if(i)==t.number

然后就意味着我们要卖到

这个最后一张票了

那这个时候我们就是休眠一毫秒

try Thread sleel

然后最后t.available=false

也就是说无票可卖了

然后这些代码执行完以后

它自然而然会释放对象t的锁

那其实提醒大家的是

用synchronized

后面大括号括起来其实是代码

实际上它把它变成一个原子操作

也就是说当我拿到这个

对象t的锁的时候

我这里面的这些代码

是肯定都会被执行的

不会说我执行某一句以后

就被这个打断

然后那个插入别的线程去执行

去访问这个对象t

所以这个是synchronized

它的很重要的作用

那经过这样的一个改造了以后

同学们可以再自己运行一下

我们这个改造后的程序

你就会发现我们的这个存票售票程序

应该不会出现死循环的这个现象了

那这里面最重要的就是我们的

synchronized关键字

实际上我们是通过这个关键字

把存票这个线程和售票线程

对同样一个对象

也就是ticket这个对象的任务访问

把它变成一种互斥操作

也就是说不能让两个线程

都同时去访问这个对象

而必须是说其中一个线程

访问这个ticket这个对象

别的就必须等它访问完

把所有的操作都做完以后

才允许它访问

那当我们的线程执行到

synchronized的时候

它的功能是这样

它要检查传入的实参对象

并申请得到该对象的锁

如果得不到的话

那这个线程就会被放到

与该对象锁所对应的等待池线程当中

直到这个对象的锁被归还

那这个池中的等待线程

才能重新去获得锁

然后才继续执行

那我们除了可以指定

对指定的代码段进行同步控制外

还可以定义某些方法

在同步控制下执行

只要在方法定义前面

加上synchronized关键字就可以了

那我们把上面的例子

再稍微改进一下

我们最重要的是改进

是把这个ticket这个类进行改进

然后给它增加两个方法

而且都设置成为同步的方法

我们看一下这个改进后的类

class tickets

那前面这些它的这个成员变量

和它的购票方法都没有变化

我们看一下新增的两个方法

第一个方法叫

public synchronized void put

同步方法

就是往里面实现存票的功能

我们看一下它的功能是什么

system.out.println

producer puts ticket++number

往里存一张票

所以available=true

那实际在这个方法中

它有两句代码

那由于加上同步synchronized

这个功能以后

那这个方法它里边的代码

就变成原子操作了

这两张代码肯定是要

能被同时执行的

我们再看一下另外一个方法

public synchronized void sell

它也是一个同步方法

它实现售票的功能

那它售票功能的话

它就会判断说if available=true

就是说是不是有票可卖

并且i小于等于number

就要我要卖票和你生产出来票的

那个票号是小于等于它的

可以卖票了

卖票的过程就模拟一下

就打印出consumer buysticket

加上++i

然后最后来判断一下

是不是票都卖没了

如果卖没了

那就available=false

所以我们现在通过这么一个改造的话

就把ticket它增加两个方法

而且是同步方法

这样的话当其他线程

要访问ticket这个类的时候

就可以通过它这两个方法来去访问

那我们看一下这个producer

和consumer又有什么修改

class producer extends Thread

然后我们看一下

主要看它的run方法

run方法的话

比以前有简化了

就是while(t.number

然后t.put

也就是说如果我们存票数

小于要限定的总量的话

我们就不断的去产生票

就把它存进去

通过调用t的put方法去存票

我们再看看consumer怎么改造的

Class consumer extends Thread

然后我们看看run方法

while(t.i

也就是说我们要售票的数量

小于你限定的总量

那我就不停的售票

那怎么售票呢

就是t.sell 用sell的方法

需要注意说我们这个sell方法

也是个同步方法

和刚才那个put方法都是同步方法

那通过这么样一个控制以后

我们这个程序这个线程执行起来

就会避免它出错

那我们再看看就是说这一部分

线程的同步和锁之间

都有什么重要的要点

首先我们只能同步方法

不能同步变量

也就是说我们synchronized的话

你去同步的话

是我们这个 (就是)我们一个方法

另外每个对象只有一个锁

我们当提到同步的时候

应该清楚在什么对象上同步

因为同步的话是因为我们

原因是我们要对某个对象进行访问

就是若干个线程对于某一个对于

进行访问

那这个时候其实锁是和这个对象

在一起的

第三点是我们类可以同时拥有

同步和非同步方法

非同步方法可以被多个线程自由访问

而不受锁的限制

如果两个线程同时使用

相同的实例来调用synchronized方法

那么一次只能有一个线程执行方法

另一个需要等待锁

这个是很自然这个事情

再提醒大家有一点

线程休眠的时候

它所持有的任何锁都不会释放

这一点就是非常有意思的一点

大家不要以为说

我这线程休眠的时候

我会释放锁

就像刚才我们那个例子

我们在这个售票线程里面

每售出来票的时候

它就会休眠一毫秒

但休眠一毫秒的时候

它不会释放出它所占有的

这个ticket对象的锁的

它一直会持有

所以这是一个独特的一个地方

我们线程还可以获得多个锁

比如说在一个对象的同步方法里面

我又调用了另外一个对象的同步方法

然后获取了两个对象的同步锁

这是允许的

另外一个原则是这样

同步它会损害并发性

应该尽可能的缩小同步范围

大家也看到了就是说

比如说两个线程都访问

同样一个对象

如果这个对象上好些方法

都给它加上同步的话

那基本上就是说我一个

一个线程访问这个对象的时候

另外一个线程只能等着

那如果等的情况多的话

那你就会损害这个

这两个线程之间的并发的性能

所以大家在(编程)区要考虑清楚说

我这一个数据对象

到底哪些方法是需要同步方法

哪些不需要

如果你能缩小这个同步方法的话

你就是可以让我们的这个

整个程序的这个并发性得到提高

那我们在使用同步代码块的时候

应该指定是在哪个对象同步

也就是说要说明获取的是

哪个对象的锁

所以我们java线程中的同步和锁

是息息相关的

希望大家能够很好的理解

这部分的内容

在这一节当中

我们通过介绍synchronized

来阐述了如何实现线程同步

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笔记与讨论

也许你还感兴趣的课程:

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