多线程程序设计学习(5)balking模式和timed模式
Balking【返回模式】timed【超时模式】
一:balking pattern的参与者
--->GuardedObject(被警戒的对象)
--->该模式的角色:模拟修改警戒对象的线程,当警戒条件达到执行具体操作的线程,参与者(被警戒的参与者)
二:balking pattern模式什么时候使用
--->不需要刻意去执行什么操作的时候(比如说自动保存)
--->不想等待警戒条件成立时。(不让线程休息)
--->警戒条件只有第一次成立时候。
三:balking pattern思考
--->balking pattern (返回模式)和Guarded suspension pattern(等待唤醒模式)的中间
3.1Guarded suspension当警戒条件不成立时,会等待,直到成立,并被唤醒。
3.2balking 当警戒条件不成立,退出。
3.3两种极端的处理方式之间还有一种折衷的做法。在条件成立为止之前,等待一段时间,看看条件是否成立,如果不成立,则balk。这种方式称之为guarded timed 或简单称之为timeOut
---->线程类中的各个唤醒方法
3.1:当notify方法执行时==>如果wait set里有多条线程,只有一条被唤醒
3.2:当notifyAll方法执行时==>wait set里有多少条线程都被唤醒。
3.3:interrupt方法执行时==>wait set里的线程会(与调用notify,notifyAll一样)重新尝试获取锁定。
==> notify,notifyAll是对实例调用的,而interrupt是对线程调用的。关于中断,后续会提到。
3.4:发生timeout时,由于wait(超时时间),和被notify或notifyAll唤醒重新尝试获取锁定,分不清楚,所以timeout需要程序员自己写。
---->sleep和wait的区别有:
1,这两个方法来自不同的类分别是Thread和Object
2,最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
3,wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在
任何地方使用
synchronized(x){
x.notify()
//或者wait()
}
4,sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常
5.wait被唤醒后,重新获取锁,从阻塞的代码处继续往下执行。和sleep一样。
Balking【返回模式】案例:模拟自动保存文件,当文件没有更改时,每隔一秒的自动保存数据,真正保存操作不执行。如果有修改,则执行真正保存操作。
数据类
1 package com.yeepay.sxf.thread4; 2 3 import java.io.File; 4 import java.io.FileOutputStream; 5 import java.io.IOException; 6 7 /** 8 * 数据类。 9 * @author sxf 10 * 11 */ 12 public class Data { 13 //文件名 14 private String fileName; 15 //文件内容 16 private String content; 17 //表示是否被修改过 18 private boolean flag; 19 //构造器 20 public Data(String fileName, String content) { 21 super(); 22 this.fileName = fileName; 23 this.content = content; 24 this.flag=true; 25 File file =new File(fileName); 26 try { 27 file.createNewFile(); 28 FileOutputStream iFileOutputStream=new FileOutputStream(file); 29 iFileOutputStream.write(content.getBytes()); 30 } catch (IOException e) { 31 // TODO Auto-generated catch block 32 e.printStackTrace(); 33 } 34 } 35 //修改内容 36 public synchronized void channgeContent(String newContent){ 37 this.content=newContent ; 38 flag=true; 39 } 40 41 //把新的数据写入文件 42 private void save() throws IOException{ 43 System.out.println("Data.save()"+Thread.currentThread().getName()+"执行写入操作,写入的内容为:"+content); 44 FileOutputStream fileWriter=new FileOutputStream(new File(fileName)); 45 fileWriter.write(content.getBytes()); 46 } 47 48 //保存内容 49 public synchronized void saveNewContent(){ 50 if(!flag){ 51 System.out.println("Data.saveNewContent(试图新保存,但没有更改)"); 52 return; 53 } 54 try { 55 save(); 56 System.out.println("Data.saveNewContent(新内容保存成功)"); 57 flag=false; 58 } catch (IOException e) { 59 e.printStackTrace(); 60 } 61 } 62 }
模拟修改数据的线程
1 package com.yeepay.sxf.thread4; 2 /** 3 * 模拟自动修改线程 4 * @author sxf 5 * 6 */ 7 public class ChangeThread implements Runnable{ 8 private Data data; 9 10 public ChangeThread(Data data) { 11 super(); 12 this.data = data; 13 } 14 15 @Override 16 public void run() { 17 for(int i=0;i<20;i++){ 18 //修改内容 19 System.out.println("ChangeThread.run()"+Thread.currentThread().getName()+"第"+i+"次修改"); 20 data.channgeContent("新内容"+i); 21 //线程休息10秒 22 try { 23 Thread.sleep(10000L); 24 } catch (InterruptedException e) { 25 // TODO Auto-generated catch block 26 e.printStackTrace(); 27 } 28 } 29 } 30 31 32 }
模拟自动保存的线程
1 package com.yeepay.sxf.thread4; 2 /** 3 * 自动保存的线程 4 * @author sxf 5 * 6 */ 7 public class ZiDongSaveThread implements Runnable{ 8 private Data data; 9 10 11 public ZiDongSaveThread(Data data){ 12 this.data=data; 13 } 14 @Override 15 public void run() { 16 while(true){ 17 //保存数据 18 data.saveNewContent(); 19 //线程休息1秒,意味着每隔一妙钟自动保存一次文件 20 try { 21 Thread.sleep(1000L); 22 } catch (InterruptedException e) { 23 // TODO Auto-generated catch block 24 e.printStackTrace(); 25 } 26 } 27 28 } 29 30 }
测试类
1 package com.yeepay.sxf.thread4; 2 /** 3 * 测试类 4 * @author sxf 5 * 6 */ 7 public class Test { 8 9 10 public static void main(String[] args) { 11 //声明内容 12 Data data=new Data("/usr/war/hsl.txt", "老黄买了新手机"); 13 //声明自动保存线程 14 Thread saveThread=new Thread(new ZiDongSaveThread(data)); 15 saveThread.setName("自动保存线程"); 16 //声明模拟修改线程 17 Thread chaThread=new Thread(new ChangeThread(data)); 18 chaThread.setName("模拟修改线程"); 19 20 //启动线程 21 saveThread.start(); 22 chaThread.start(); 23 24 25 } 26 }
打印结果
Data.save()自动保存线程执行写入操作,写入的内容为:老黄买了新手机
Data.saveNewContent(新内容保存成功)
ChangeThread.run()模拟修改线程第0次修改
Data.save()自动保存线程执行写入操作,写入的内容为:新内容0
Data.saveNewContent(新内容保存成功)
Data.saveNewContent(试图新保存,但没有更改)
Data.saveNewContent(试图新保存,但没有更改)
Data.saveNewContent(试图新保存,但没有更改)
Data.saveNewContent(试图新保存,但没有更改)
Data.saveNewContent(试图新保存,但没有更改)
Data.saveNewContent(试图新保存,但没有更改)
Data.saveNewContent(试图新保存,但没有更改)
Data.saveNewContent(试图新保存,但没有更改)
Data.saveNewContent(试图新保存,但没有更改)
ChangeThread.run()模拟修改线程第1次修改
Data.save()自动保存线程执行写入操作,写入的内容为:新内容1
Data.saveNewContent(新内容保存成功)
Data.saveNewContent(试图新保存,但没有更改)
Data.saveNewContent(试图新保存,但没有更改)
Data.saveNewContent(试图新保存,但没有更改)
Data.saveNewContent(试图新保存,但没有更改)
timed【超时模式】案例:一个线程提供下载数据,另一个线程执行下载,如果有5秒钟以上,提供下载的线程没有提供数据,下载线程因超时异常,停止下载线程运行。
超时异常类
1 package com.yeepay.sxf.thread4; 2 /** 3 * 超时异常类 4 * @author sxf 5 * 6 */ 7 public class TimeOutException extends InterruptedException { 8 9 public TimeOutException(String msg){ 10 super(msg); 11 } 12 }
下载数据的数据类
1 package com.yeepay.sxf.thread4; 2 /** 3 * 下载数据类 4 * @author sxf 5 * 6 */ 7 public class FileData { 8 //提供下载的数据 9 private String data; 10 //有数据可下载 11 private boolean flag; 12 //超时时间 13 private long timeout; 14 15 //构造器 16 public FileData(String data, boolean flag, long timeout) { 17 super(); 18 this.data = data; 19 this.flag = flag; 20 this.timeout = timeout; 21 } 22 23 24 //修改状态,唤醒其他所有线程 25 public synchronized void changeStatus(String data){ 26 this.data=data; 27 flag=true; 28 notify(); 29 } 30 31 //下载操作。如果等timeout/1000秒钟,没有数据供下载,就报超时异常,终止下载 32 public synchronized void execu() throws InterruptedException{ 33 //开始执行的时间 34 long start=System.currentTimeMillis(); 35 int i=0; 36 System.out.println("FileData.execu(开始时间1)"); 37 while (!flag) { 38 //现在的时间 39 long now=System.currentTimeMillis(); 40 long reset=timeout-(now-start); 41 if(reset<=0){ 42 throw new TimeOutException("已经等候"+timeout+"时间了还没有数据可供下载,超时"); 43 } 44 //如果没有超时,就让下载线程进入wait set,设置超时时间。被唤醒,继续从这里开始执行。 45 wait(reset); 46 } 47 //真正的下载操作 48 download(); 49 } 50 51 52 //真正的下载操作 53 private void download(){ 54 System.out.println("顺利下载数据==>:"+data); 55 //下载完之后,将下载状态改为不可下载,等待放入新数据供下载 56 this.flag=false; 57 } 58 59 public String getData() { 60 return data; 61 } 62 public void setData(String data) { 63 this.data = data; 64 } 65 public boolean isFlag() { 66 return flag; 67 } 68 public void setFlag(boolean flag) { 69 this.flag = flag; 70 } 71 72 73 74 public long getTimeout() { 75 return timeout; 76 } 77 public void setTimeout(long timeout) { 78 this.timeout = timeout; 79 } 80 }
提供数据供下载的线程类
1 package com.yeepay.sxf.thread4; 2 /** 3 *制造数据线程 4 * @author sxf 5 * 6 */ 7 public class GiveDataThread implements Runnable { 8 //公共数据 9 private FileData fileData; 10 11 //构造器 12 public GiveDataThread(FileData fileData) { 13 super(); 14 this.fileData = fileData; 15 } 16 17 18 19 @Override 20 public void run() { 21 //制造数据线程,造100个数据 22 for (int i = 0; i <10; i++) { 23 fileData.changeStatus("制造数据"+i); 24 System.out.println("【制造线程制造数据】==》制造数据"+i); 25 try { 26 Thread.sleep(1000); 27 } catch (InterruptedException e) { 28 // TODO Auto-generated catch block 29 e.printStackTrace(); 30 } 31 32 } 33 34 } 35 36 37 }
下载数据的线程类
1 package com.yeepay.sxf.thread4; 2 /** 3 * 下载线程 4 * @author sxf 5 * 6 */ 7 public class DownLoadThread implements Runnable { 8 private FileData fileData; 9 private boolean flag=true; 10 //构造器 11 public DownLoadThread(FileData fileData) { 12 super(); 13 this.fileData = fileData; 14 } 15 16 @Override 17 public void run() { 18 //开始下载线程 19 System.out.println("DownLoadThread.run(下载线程开始"); 20 //根据标识 21 while(flag){ 22 //进行下载 23 try { 24 fileData.execu(); 25 } catch (TimeOutException e) { 26 e.printStackTrace(); 27 flag=false; 28 }catch (InterruptedException e) { 29 // TODO: handle exception 30 System.out.println("DownLoadThread.run(非超时异常)"); 31 } 32 } 33 34 System.out.println("DownLoadThread.run(下载线程因为超时,而执行完毕)"); 35 } 36 37 38 39 40 41 }
测试类
1 package com.yeepay.sxf.thread4; 2 /** 3 * 测试超时模式 4 * @author sxf 5 * 6 */ 7 public class Test2 { 8 9 10 public static void main(String[] args) { 11 //声明数据类 12 FileData data=new FileData("sxf",true,5000); 13 //声明生产数据的线程 14 Thread giveThread=new Thread(new GiveDataThread(data)); 15 //声明下载数据的线程 16 Thread downThread=new Thread(new DownLoadThread(data)); 17 18 //启动线程 19 giveThread.start(); 20 downThread.start(); 21 } 22 }
打印结果
【制造线程制造数据】==》制造数据0
DownLoadThread.run(下载线程开始
FileData.execu(开始时间1)
顺利下载数据==>:制造数据0
FileData.execu(开始时间1)
【制造线程制造数据】==》制造数据1
顺利下载数据==>:制造数据1
FileData.execu(开始时间1)
【制造线程制造数据】==》制造数据2
顺利下载数据==>:制造数据2
FileData.execu(开始时间1)
【制造线程制造数据】==》制造数据3
顺利下载数据==>:制造数据3
FileData.execu(开始时间1)
【制造线程制造数据】==》制造数据4
顺利下载数据==>:制造数据4
FileData.execu(开始时间1)
【制造线程制造数据】==》制造数据5
顺利下载数据==>:制造数据5
FileData.execu(开始时间1)
【制造线程制造数据】==》制造数据6
顺利下载数据==>:制造数据6
FileData.execu(开始时间1)
【制造线程制造数据】==》制造数据7
顺利下载数据==>:制造数据7
FileData.execu(开始时间1)
【制造线程制造数据】==》制造数据8
顺利下载数据==>:制造数据8
FileData.execu(开始时间1)
【制造线程制造数据】==》制造数据9
顺利下载数据==>:制造数据9
FileData.execu(开始时间1)
DownLoadThread.run(下载线程因为超时,而执行完毕)
com.yeepay.sxf.thread4.TimeOutException: 已经等候5000时间了还没有数据可供下载,超时
at com.yeepay.sxf.thread4.FileData.execu(FileData.java:42)
at com.yeepay.sxf.thread4.DownLoadThread.run(DownLoadThread.java:24)
at java.lang.Thread.run(Thread.java:662)