第七章 多线程处理 线程的互斥 线程的同步 死锁问题.

Slides:



Advertisements
Similar presentations
作者 : 陳鍾誠 單位 : 金門技術學院資管系 URL : 日期 : 2016/7/21 行程的同步 註:本章學術性較重,但考試常考。
Advertisements

Java 程序设计 第 8 章 多线程. 第 8 章 Java 的多线程 8.1 线程及其创建 Java 中的线程 Java 程序通过流控制来执行程序流,程序中单个顺序的流 控制称为线程, 多线程则指的是在单个程序中可以同时运行 多个不同的线程执行不同的任务。 单个程序内部也可以在同一时刻进行多种运算。
面向侧面的程序设计 方林博士 本文下载地址:
阻塞操作. 在 linux 里,一个等待队列由一个 wait_queue_head_t 类型的结构来描述 等待队列的初始化: static wait_queue_head_t testqueue; init_waitqueue_head(&testqueue);
3.2 Java的类 Java 类库的概念 语言规则——程序的书写规范 Java语言 类库——已有的有特定功能的Java程序模块
JAVA 编 程 技 术 主编 贾振华 2010年1月.
项目6 通用堆栈.
讓你的程式具有多工(Multitasking) 及多重處理(Multiprocessing)的能力
Java Programming Spring, 2010
第7章 多线程 北京大学计算机系 代亚非.
四資二甲 第三週作業 物件導向程式設計.
面向对象的程序设计(一).
设计模式可以帮助我们改善系统的设计,增强 系统的健壮性、可扩展性,为以后铺平道路。
Oracle数据库 Oracle 子程序.
线程与线程实现 主讲:赖国荣 QQ:
第二章 JAVA语言基础.
在PHP和MYSQL中实现完美的中文显示
Ch08 巢狀類別 物件導向程式設計(II).
2.1 基本資料型別 2.2 變數 2.3 運算式與運算子 2.4 輸出與輸入資料 2.5 資料型別轉換 2.6 實例
Kvm异步缺页中断 浙江大学计算机体系结构实验室 徐浩.
物件導向程式設計 (Object-Oriented rogramming)
Java语言程序设计 第七部分 多线程.
Java基础 JavaSE异常.
程序與函數的類別方法 目的:模組化程式設計 方法:由上而下設計 注意事項:(1)獨立性 (2)結合問題 (3)子問題間的溝通.
西南科技大学网络教育系列课程 高级语程序设计(Java) 第五章 继承、接口与范型.
程式設計實作.
2018/12/3 面向对象与多线程综合实验-网络编程 教师:段鹏飞.
Java语言程序设计 第五部分 Java异常处理.
Java程序设计 第9章 继承和多态.
辅导课程六.
第10章 Java的线程处理 10.1 线程的基本概念 10.2 线程的属性 10.3 线程组 10.4 多线程程序的开发.
2018/12/7 Java语言程序设计-多线程 教师:段鹏飞.
临界区软件互斥软件实现算法.
第一单元 初识C程序与C程序开发平台搭建 ---观其大略
第一次课后作业 1. C/C++/Java 哪些值不是头等程序对象 2. C/C++/Java 哪些机制采用的是动态束定
9.1 程式偵錯 9.2 捕捉例外 9.3 自行拋出例外 9.4 自定例外類別 9.5 多執行緒
第9讲 Java的继承与多态(一) 类的继承 子类的创建 方法覆盖.
临界区软件互斥软件实现算法 主讲教师:夏莹杰
C/C++/Java 哪些值不是头等程序对象
用event class 从input的root文件中,由DmpDataBuffer::ReadObject读取数据的问题
* 單元:電腦與問題解決 主題:Java物件導向程式設計-類別與物件 台南縣國立善化高中 蕭嘉民 老師
4.2通讯服务模块线程之间传递信息 信息工程系 向模军 Tel: QQ:
宁波市高校慕课联盟课程 与 进行交互 Linux 系统管理.
Multithread 多執行緒 以GUI為例了解物件以及Event
JAVA 编 程 技 术 主编 贾振华 2010年1月.
《JAVA程序设计》 语音答疑 辅导老师:高旻.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
$9 泛型基础.
第二章 Java基本语法 讲师:复凡.
信号量(Semaphore).
第二章 Java语法基础.
iSIGHT 基本培训 使用 Excel的栅栏问题
Chapter 18 使用GRASP的对象设计示例.
第二章 Java基本语法 讲师:复凡.
临界区问题的硬件指令解决方案 (Synchronization Hardware)
本节内容 Windows线程切换_时钟中断切换 视频提供:昆山滴水信息技术有限公司 官网地址: 论坛地址: QQ交流 :
Google的云计算 分布式锁服务Chubby.
基于列存储的RDF数据管理 朱敏
C++语言程序设计 C++语言程序设计 第一章 C++语言概述 第十一组 C++语言程序设计.
第2章 Java语言基础.
第8章 创建与使用图块 将一个或多个单一的实体对象整合为一个对象,这个对象就是图块。图块中的各实体可以具有各自的图层、线性、颜色等特征。在应用时,图块作为一个独立的、完整的对象进行操作,可以根据需要按一定比例和角度将图块插入到需要的位置。 2019/6/30.
本节内容 进程 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
判斷(選擇性敘述) if if else else if 條件運算子.
使用Fragment 本讲大纲: 1、创建Fragment 2、在Activity中添加Fragment
第二章 Java基础语法 北京传智播客教育
本节内容 SEMAPHORE 视频提供:昆山滴水信息技术有限公司 官网地址: 论坛地址: QQ交流 :
輸出執行結果到螢幕上 如果要將執行結果的文字和數值都「輸出」到電腦螢幕時,程式要怎麼寫? class 類別名稱 {
第二章 Java基本语法 讲师:复凡.
多个Activity的使用 本讲大纲: 1、使用Bundle在Activity之间交换数据 2、调用另一个Activity并返回结果
Summary
Presentation transcript:

第七章 多线程处理 线程的互斥 线程的同步 死锁问题

1、多线程的互斥 ——线程间的通信 1. 用管道流实现线程间的通信 线程1 线程2 2. 通过一个中间类在线程间传递信息 线程2 线程1 PipedOutputStream PipedInputStream 输出流outStream 输入流inStream 线程2 2. 通过一个中间类在线程间传递信息 线程2 线程1 中间类m s m.write(s) s=m.read() write() read()

1、多线程的互斥 ——线程间的数据共享 例如:银行对同一个帐户的存取款操作。 class Fetch extends Thread{ private Cq a1; private int amount; public Fetch(Cq a1, int amount) { this.a1 = a1; this.amount = amount; } public void run() { int k = a1.howmatch(); try { sleep(1); } catch (InterruptedException e) { System.out.println(e); } System.out.println("现有" + k + ",取走" + a1.get(amount) + ",余额" + a1.howmatch()); } } class Cq { private String name; private int value; void put(int i) { value=value+i;} int get(int i){ if (value>i) value=value-i; else { i=value; value=0; } return i; } int howmatch() { return value; } }

class Save extends Thread{ private Cq a1; private int amount; public Save(Cq a1,int amount) { this.a1=a1; this.amount=amount; } public void run() { int k = a1.howmatch(); try { sleep(1); catch (InterruptedException e) { System.out.println(e); } a1.put(amount); System.out.println("现有" + k + ",存入" + amount + ",余额" + a1.howmatch()); } } public static void main(String args[]) { Cq a1=new Cq(); Cq a2=new Cq(); (new Save(a1,100)).start(); (new Save(a1,200)).start(); (new Fetch(a1,500)).start(); (new Fetch(a2,500)).start();

1、多线程的互斥 ——线程间的资源互斥共享 运行结果: 现有0,存入100,余额100 现有0,存入200,余额300 现有0,取走300,余额0

1、多线程的互斥 ——线程间的资源互斥共享 线程 1 线程 2 线程 10 资源 取过来 加1后送回去 取钱1 取钱2 透支 余额 变量

1、多线程的互斥 ——线程间的资源互斥共享 产生这种问题的原因是对共享资源访问的不完整。为了解决这种问题,需要寻找一种机制来保证对共享数据操作的完整性,这种完整性称为共享数据操作的同步,共享数据叫做条件变量。 在Java语言中,引入了“对象互斥锁”的概念(又称为监视器、管程)来实现不同线程对共享数据操作的同步。 “对象互斥锁”阻止多个线程同时访问同一个条件变量。 在Java语言中,有两种方法可以实现“对象互斥锁”: 用关键字volatile来声明一个共享数据(变量); 用关键字synchronized来声明一个操作共享数据的方法或一段代码。

1、多线程的互斥 ——线程间的资源互斥共享 class Fetch extends Thread{ class Cq { private Cq a1; private int amount; public Fetch(Cq a1, int amount) { this.a1 = a1; this.amount = amount; } public void run() { synchronized(a1){ int k = a1.howmatch(); try { sleep(1); } catch (InterruptedException e) { System.out.println(e); } System.out.println("现有" + k + ",取走" + a1.get(amount) + ",余额" + a1.howmatch()); } } } class Cq { private String name; private int value; void put(int i) { value=value+i;} int get(int i){ if (value>i) value=value-i; else { i=value; value=0; } return i; } int howmatch() { return value; } }

5、多线程的互斥与同步 ——线程间的资源互斥共享 class Save extends Thread{ private Cq a1; private int amount; public Save(Cq a1,int amount) { this.a1=a1; this.amount=amount; } public void run() { synchronized(a1) { int k = a1.howmatch(); try { sleep(1); catch (InterruptedException e) { System.out.println(e); } a1.put(amount); System.out.println("现有" + k + ",存入" + amount + ",余额" + a1.howmatch()); } } public static void main(String args[]) { Cq a1=new Cq(); Cq a2=new Cq(); (new Save(a1,100)).start(); (new Save(a1,200)).start(); (new Fetch(a1,500)).start(); (new Fetch(a2,500)).start();

1、多线程的互斥 ——线程间的资源互斥共享 用synchronized来标识的代码段或方法即为“对象互斥锁”锁住的部分。如果一个程序内有两个或以上的方法使用synchronized标志,则它们在同一个“对象互斥锁”管理之下。 push pop 互 斥 锁 线程1 线程2 一般情况下,都使用synchronized关键字在方法的层次上实现对共享资源操作的同步,很少使用volatile关键字声明共享变量。

synchronized的用法 1、对于同步的方法或者代码块来说,必须获得对象锁才能够进入同步方法或者代码块进行操作; 2、如果采用method级别的同步,则对象锁即为method所在的对象,如果是静态方法,对象锁即指method所在的Class对象(唯一); 3、对于代码块,对象锁即指synchronized(abc)中的abc; 4、因为第一种情况,对象锁即为每一个线程对象,因此有多个,所以同步失效,第二种共用同一个对象锁lock,因此同步生效,第三个因为是static因此对象锁为ThreadTest3的class 对象,因此同步生效。 如上述同步有两种方式:同步块和同步方法。 如果是同步代码块,则对象锁需要编程人员自己指定,一般有些代码为synchronized(this)只有在单例模式才生效;(本类的实例有且只有一个) 如果是同步方法,则分静态和非静态两种。 静态方法则一定会同步,非静态方法需在单例模式才生效,推荐用静态方法(不用担心是否单例)。

ReentrantLock的用法 ReentrantLock是“一个可重入的互斥锁 Lock,它具有与使用 synchronized  方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。ReentrantLock 将由最近成功获得锁,并且还没有释放该锁的线程所拥有。当锁没有被另一个线程所拥有时,调用 lock 的线程将成功获取该锁并返回。如果当前线程已经拥有该锁,此方法将立即返回。可以使用 isHeldByCurrentThread() 和 getHoldCount() 方法来检查此情况是否发生。 ” 简单来说,ReentrantLock有一个与锁相关的获取计数器,如果拥有锁的某个线程再次得到锁,那么获取计数器就加1,然后锁需要被释放两次才能获得真正释放。这模仿了 synchronized 的语义;如果线程进入由线程已经拥有的监控器保护的 synchronized 块,就允许线程继续进行,当线程退出第二个(或者后续) synchronized 块的时候,不释放锁,只有线程退出它进入的监控器保护的第一个 synchronized 块时,才释放锁。 ReentrantLock  类(重入锁)实现了 Lock ,它拥有与 synchronized 相同的并发性和内存语义,但是添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。此外,它还提供了在激烈争用情况下更佳的性能。(换句话说,当许多线程都想访问共享资源时,JVM 可以花更少的时候来调度线程,把更多时间用在执行线程上。)

2、多线程的同步 ——线程间的同步 除了要处理多线程间共享数据操作的同步问题之外,在进行多线程程序设计时,还会遇到另一类问题,这就是如何控制相互交互的线程之间的运行进度,即多线程的同步。 典型的模型:生产者——消费者问题 生产者 消费者 共享对象 put get 若共享对象中只能存放一个数据,可能出现以下问题: 生产者比消费者快时,消费者会漏掉一些数据没有取到; 消费者比生产者快时,消费者取相同的数据。

2、多线程的同步 ——线程间的同步 为了解决所出现的问题,在Java语言中可以用wait () 和notify()/notifyAll()方法(在java.lang.Object类中定义)来协调线程间的运行进度(读取)关系。 wait()方法的作用是让当前线程释放其所持有的“对象互斥锁”,进入wait队列(等待队列);而notify()/notifyAll()方法的作用是唤醒一个或所有正在等待队列中等待的线程,并将它(们)移入等待同一个“对象互斥锁”的队列。 需要指出的是: notify()/notifyAll()方法和wait ()方法都只能在被声明为synchronized的方法或代码段中调用。

public class ProducerConsumerTest { public static void main(String args[]) CubbyHole c = new CubbyHole(); //共享资源 Producer p1 = new Producer(c, 1); //资源生产者 Consumer c1 = new Consumer(c, 1); //资源消耗者 p1.setPriority(Thread.MAX_PRIORITY); c1.setPriority(Thread.MAX_PRIORITY); p1.start(); c1.start(); }

class Producer extends Thread { private CubbyHole cubbyhole; private int number; public Producer(CubbyHole c, int number) cubbyhole = c; this.number = number; } public void run() for (int i = 0; i <10; i++) cubbyhole.put(i); System.out.println("Producer #" + this.number + " put: " + i); try{ sleep((int)(Math.random() * 100)); } catch (InterruptedException e) {

class Consumer extends Thread { private CubbyHole cubbyhole; private int number; public Consumer(CubbyHole c, int number) cubbyhole = c; this.number = number; } public void run() int value = 0; for (int i = 0; i <10; i++) value = cubbyhole.get(); System.out.println("Consumer #" + this.number + " got: " + value);

ProducerConsumerTest.java get()方法在读信息之前先等待,直到信息可读,读完后通知要写的线程。 class CubbyHole { private int seq; private boolean available = false; //信号量 public synchronized int get() while (available == false) try wait(); // waits for notify() call from Producer }catch (InterruptedException e){ } available = false; notify(); return seq; get()方法在读信息之前先等待,直到信息可读,读完后通知要写的线程。 public synchronized void put(int value) { while (available == true) try{ wait(); // waits for notify() call from consumer }catch (InterruptedException e){ } seq = value; available = true; notify(); … put()方法在写信息之前先等待,直到信息被取走,写完后通知要读的进程。 ProducerConsumerTest.java

3、死锁问题 note pen 如果一个线程持有一个锁并试图获取另一个锁时,就有死锁的危险。 线程2 pen 线程1 note 把“pen”给我,我 才能给你“note” 把“note”给我,我 才能给你“pen” 如果一个线程持有一个锁并试图获取另一个锁时,就有死锁的危险。 死锁是资源的无序使用而带来得,解决死锁问题的方法就是给资源施加排序。note编号为1,pen编号为2,线程1和线程2都必须先获得1号资源后方可再获取2号资源。

3、死锁问题 例如:发牌程序

4、线程的优先级及调度 Java提供一个线程调度器来监控程序中启动后进入可运行状态的所有线程。线程调度器按照线程的优先级决定调度哪些线程来执行,具有高优先级的线程会在较低优先级的线程之前得到执行。同时线程的调度是抢先式的,即如果当前线程在执行过程中,一个具有更高优先级的线程进入可执行状态,则该告优先级的线程会被立即调度执行。 多个线程运行时,若线程的优先级相同,由操作系统按时间片轮转方式或独占方式来分配线程的执行时间。

4、线程的优先级及调度 在Java中线程的优先级是用数字来表示的,分为三个级别: 高优先级:Thread.MIN_PRIORITY,数值为1 (2~4) 缺省优先级: Thread. NORM_PRIORITY,数值为5 低优先级:Thread.MAX_PRIORITY,数值为10 (6~9) 具有相同优先级的多个线程,若它们都为高优先级Thread.MAX_PRIORITY,则每个线程都是独占式的,也就是说这些线程将被顺序执行;若该优先级不为高优先级,则这些线程将同时执行,也就是说这些线程的执行是无序的。 线程被创建后,其缺省的优先级是缺省优先级Thread. NORM_PRIORITY。可以用方法 int getPriority()来获得线程的优先级,同时也可以用方法 void setPriority( int p ) 在线程被创建后改变线程的优先级。

5、守护线程 (Daemon) 在客户/服务器模式下,服务器的作用是持续等待用户发来请求,并按请求完成客户的工作。 客户端 服务器端 request daemon 守护线程是为其它线程提供服务的线程,它一般应该是一个独立的线程,它的run()方法是一个无限循环。 可以用方法public boolean isDaemon()确定一个线程是否守护线程,也可以用方法public void setDaemon( boolean )来设定一个线程为守护线程。 守护线程与其它线程的区别是,如果守护线程是唯一运行着的线程,所有用户线程终止后,系统将其强迫终止。

5、守护线程 (Daemon) 一般当最后一个线程结束时,Java程序才退出 守护线程的存在不影响Java程序的退出 setDaemon(true) 使线程成为守护线程(必须在start之前调用) setDaemon(false) 使线程成为一般线程(必须在start之前调用) 守护线程一般不能用于执行关键任务 任务未执行完,线程就可能被强制结束 守护线程一般用来做辅助性工作 提示,帮助等

6、线程组 (ThreadGroup) / 线程局部变量(ThreadLocal) 把一组线程统一管理 构造方法 声明线程的线程组 例如对一组线程同时调用interrupt 构造方法 ThreadGroup(String groupName) ThreadGroup(ThreadGroup tg, String groupName) 线程组可以递归 声明线程的线程组 缺省创建的线程与父线程同组 Thread(ThreadGroup tg, Runnable ro) Thread(ThreadGroup tg, Runnable ro, String threadName)

6、线程组 (ThreadGroup) / 线程局部变量(ThreadLocal) ThreadGroup0 ThreadGroup1

6、线程组 (ThreadGroup) / 线程局部变量(ThreadLocal) int activeCount() 线程组下的所有活动线程数(递归) int enumerate(Thread[] list[, boolean recursive]) int enumerate(ThreadGroup[] list[, boolean recursive]) getMaxProirity / setmaxProirity 获得 / 设置 线程组中线程的最大优先级 getName 获得线程组的名字 getParent / parentOf 获得 / 判断 线程父子关系

6、线程组 (ThreadGroup) / 线程局部变量(ThreadLocal) 不同的线程对相同的线程局部变量的访问相互不影响 Object get() void set(Object o) protected  Object initialValue() InheritableThreadLocal extends ThreadLocal InheritableThreadLocal() protected  Object childValue(Object parentValue) 将线程变量定义为类变量(静态变量),并与相关的线程关联起来。

7、使用线程 线程的使用取决于程序的需求: 1、使用多线程不一定会增加CPU的处理能力; 如果程序对IO或网络连接、数据库连接等相对于CPU来说非常慢的设备操作比较多,应用程序经常性的等待缓慢的资源,在CPU上进行的是一些简单耗时短的操作,使用本地操作可以使CPU在系统进行这些较慢操作时多处理其他线程的请求,从而CPU得到充分利用;如果应用程序是计算密集型的,每个运算占CPU时间较长,即使用线程也未必能提高多少性能。 2、基于Internet的应用有必要使用多线程; 每个线程可以为不同的客户或客户组服务,从而缩短不响应时间。

小结 1. 实现线程有两种方法: 实现Ruannable接口 继承Thread类 2. 当新线程被启动时,Java运行系统调用该线程的run()方法,它是Thread的核心。 3. 线程有四个基本状态:创建、可运行、不可运行、死亡。

小结 4. 两个或多个线程竞争资源时,需要用同步的方法协调资源。 5. 多个线程执行时,要用到同步方法,即使用synchronized的关键字设定同步区。 6. wait和notify起协调作用。