学习笔记-线程
线程
程序 进程 线程
- 程序:Progream,指令集,是一个静态的概念
- 进程:Process,操作系统调度程序,是一个动态的概念。
- 进程是程序的一次动态执行过程,占用特定的地址空间。
- 每个进程都是独立的,由3部分组成:cpu,data,code
- 缺点:内存的浪费,cpu的负担
- 线程:Thread,是进程中一个“单一的连续控制流程"(a single sequential flow of control)/执行路径
- 线程别称:轻量级进程(lightweight process)
- Threads run at the same time,independently of one another.
- 一个进程可拥有多个并行(concurrent)的线程
- 一个进程中的线程共享相同的内存单元/内存地址空间
- 可以访问相同的变量和对象,而且它们从同一堆中分配对象
- 通信、数据交换、同步操作
- 可以访问相同的变量和对象,而且它们从同一堆中分配对象
- 由于线程间的通信是在同一地址空间上进行的,所以不需要额外的通信机制,这就使得通信更简便而且信息传递的速度也更快。
线程和进程的区别A
区别 | 进程 | 线程 |
---|---|---|
根本区别 | 做为资源分配的单位 | 调度和执行的单位 |
开销 | 每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销 | 线程可以堪称轻量级的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小 |
所处环境 | 在操作系统中能同时运行多个任务(程序) | 在同一个应用程序中多个顺序流同时执行 |
分配内存 | 系统在运行的时候会为每个进程分配不同的内存区域 | 除了CPU之外,不会为线程分配内存(线程所使用的资源是它所属进程的资源),线程组只能贡献资源。 |
包含关系 | 没有线程的进程可以被看做单线程的,如果一个进程内拥有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的。 | 线程是进程的一部分,所以线程有时候被称为是轻权进程或轻量级进程。 |
java实现多线程
-
在Java中负责线程的这个功能的类是Java.lang.Thread
-
通过创建Thread的实例来创建新的线程
-
每个线程都是通过某个特定的Tread对象所对应的run()方法来完成操作的,方法run()被称为线程体。
-
通过调用Tread类的start()方法来启动一个线程。
-
通过Tread实例创建线程
模拟龟兔赛跑
package create; /* * 模拟龟兔赛跑 * 1,创建多线程 继承 Thread + 重写run(线程体)方法 * 2,使用线程:创建子类对象+ 对象.start()方法 */ public class Rabbit extends Thread{ @Override public void run() { for(int i=0;i>100;i++) { System.out.println("兔子跑了"+i+"步"); } } } class Tortoise extends Thread{ @Override public void run() { for(int i=0;i>100;i++) { System.out.println("乌龟跑了"+i+"步"); } } }
测试类package create; public class RabbitApp { public static void main(String[] args) { //创建子类对象 Rabbit rab=new Rabbit(); Tortoise tor=new Tortoise(); /* * 调用start方法启动线程 * 直接调run()方法是无法启动线程的 */ // rab.run(); rab.start(); tor.start(); for(int i=0;i<1000;i++) { System.out.println("main==>"+i); } } }
-
继承Thread类方式的缺点:如果我们的类已经从一个某个类继承,就无法再继承Thread类
-
-
通过Runnable接口实现多线程
-
优点:可以同时实现继承。(实现Runnable接口方式要通用一些)。
- 避免单继承
- 方便共享 同一份资源 多个代理访问
-
静态代理设计模式
代理者专注一整套的流程为真实角色服务
package create; /* * 静态代理 设计模式 * 1,真实角色 * 2,代理角色:持有真实角色的引用 * 3,二者 实现相同的接口 */ public class StaticProxy { public static void main(String[] args) { //创建真实角色 You you=new You(); //创建代理角色 + 真实角色的引用 WeddingCompany company=new WeddingCompany(you); //执行任务 company.marry(); } } interface Marry{ void marry(); } //真实角色 class You implements Marry{ @Override public void marry() { System.out.println("you marry with xiaoxin"); } } //代理角色 class WeddingCompany implements Marry{ private Marry you; public WeddingCompany() { super(); } public WeddingCompany(Marry you) { super(); this.you = you; } private void before() { System.out.println("布置婚房"); } private void after() { System.out.println("闹洞房"); } @Override public void marry() { before(); you.marry(); after(); } }
-
使用Runnable接口实现多线程
package create; /* 推荐Runnable创建线程 1,避免单继承的局限性 2,便于共享资源 使用Runnable创建线程 1,类实现Runnable接口+重写run()方法 -->真实角色类 2,启动多线程 使用静态代理 1,创建真实角色 2,创建代理角色+持有真实角色引用 3,调用start方法 */ public class Programmar implements Runnable{ @Override public void run() { for(int i=0;i<1000;i++) { System.out.println("一边敲代码"); } } }
测试类package create; public class ProgrammarApp { public static void main(String[] args) { // 1,创建真实角色 Programmar pro=new Programmar(); // 2,创建代理角色+持有真实角色引用 Thread proxy=new Thread(pro); // 3,调用start方法 proxy.start(); for(int i=0;i<1000;i++) { System.out.println("一边聊QQ"); } } }
-
方便资源共享
package create; /* * 方便共享资源 */ public class Wed12306 implements Runnable{ private int num=200; @Override public void run() { while(true) { if(num>=0) { break;//跳出循环 } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); } } public static void main(String[] args) { //真实角色 Wed12306 web=new Wed12306(); //代理 Thread t1=new Thread(web,"路人甲"); Thread t2=new Thread(web,"黄牛乙"); Thread t3=new Thread(web,"工程师"); //启动线程 t1.start(); t2.start(); t3.start(); } }
-
-
通过Callable接口实现多线程
-
优点:可以获取返回值
- Callable 和 Future接口
- Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可以被其它线程执行的任务。
- Callable和Runnable有几点不同:
- Callable规定的方法是call(),而Runnable规定的方法是run()
- call()方法可抛出异常,而run()方法不能抛出异常
- Callable的任务执行后可返回值,运行Callable任务可拿到一个Future对象,而Runnable对象是不能返回值的
- Future表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。
- 通过Future对象可了解任务执行情况,可取消任务的执行,还可以获取任务执行的结果。
- Callable 和 Future接口
-
缺点:繁琐。
-
思路:
-
创建Callable实现类+重写call
-
借助 执行调度服务 ExecutorService,获取Future对象
- ExecutorServiece ser=Excutors.newFixedThreadPool(2);//借助调度器返回执行调度服务对象
- Future result=ser.submit(实现类对象);//通过指定调度服务对象的submit方法返回Future对象
- 获取值result.get();//通过Future对象get()方法返回值
- 停止服务ser.shutdownNow();
-
Callable创建线程
package create; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /* * 使用Callable创建线程 */ public class Call{ public static void main(String[] args) throws InterruptedException, ExecutionException { //通过Executors创建固定线程池 ExecutorService ser=Executors.newFixedThreadPool(2);//2条线程 //创建线程 Race rabbit=new Race("兔八哥",300);//兔八哥每300毫秒递增1步 Race tortoise =new Race("千年王八",600);//千年王八每600秒递增1步
//将线程放入线程池并返回Future对象 Futureresult2=ser.submit(rabbit); Future result1=ser.submit(tortoise);
//main线程在此处停止3秒然后执行后续操作,期间其它线程继续执行 Thread.sleep(3000); tortoise.setFlag(false);//停止线程体循环 rabbit.setFlag(false); //通过Future对象的get()方法等待线程执行结束后返回call()方法的返回值 int num1=result1.get(); int num2=result2.get(); System.out.println("乌龟跑了-->"+num1+"步"); System.out.println("兔子跑了-->"+num2+"步"); //停止服务 ser.shutdownNow(); } } //通过Callable接口创建线程 class Race implements Callable{
private String name;//代表名称 private long time;//步进耗时 private boolean flag=true;//开关 private int step=0;//计步变量
/** * * @param name 代表姓名 * @param time 代表步进耗时 */
public Race(String name, long time) { super(); this.name = name; this.time = time; }
//Callable实例中的call方法可以返回值(泛型指定)和抛出异常 @Override public Integer call() throws Exception {//可抛出异常 while(flag) {//当开关处于true时执行 Thread.sleep(time);//该线程跳过的时间片段 step++;//在跳过设定的时间片段后递增步数 } //当循环体结束后执行其后的代码 return step;//返回步数 } //操作线程体中while语句的执行条件以切换线程体运行和停止 public void setFlag(boolean flag) { this.flag = flag; } }
-
-
线程的状态和方法
**一,线程状态**
- 新生状态:
- 通过Thread及其子类创建一个线程对象后,该线程处于新生状态(Thread),有自己的内存空间。
- 通过调用start方法进入就绪状态(runnable)
- 就绪状态/等待状态:
- 处于就绪状态的线程已经具备了运行条件,但尚未分配到CPU,处于线程就绪队列中,等待系统为其分配CPU。等待状态并非执行状态,当系统选定一个等待执行的Thread对象后,它就会从等待状态进入执行状态,此处系统挑选的动作称之为“cpu调度”。一旦获得CPU,线程就进入运行状态并自动调用自己的run()方法。
- 运行状态:
- 在运行状态的线程执行自己的run方法中的代码,直到调用其它方法而中止运行状态、或等待资源而阻塞,或完成任务而消亡。如果在给定的时间片内没有执行结束,就会被系统换下来回到等待执行状态。
- 阻塞状态:
- 处于运行状态的线程在某些情况下,如执行了sleep(睡眠)方法,或等待I/O设备资源,将让出CPU并暂停自己的运行,进入阻塞状态。在阻塞状态的线程不能进入就绪队列,只有当引起阻塞的原因消除时,如睡眠时间已到,或等待的I/O设备空闲下来,线程便转入就绪状态在等待队列中等待,被系统选中后从原来停止的位置开始继续运行。
- 死亡状态:
- 死亡状态时线程声明周期的最后一个阶段,线程死亡的原因有两个,一个时正常运行的线程完成了它的全部工作;另一个时线程被强制终止(如通过执行Thread类的stop/destory方法来终止——不推荐,前者会产生异常导致程序终止,后者是强制终止不会释放锁)
二,停止线程
-
自然终止:线程体正常执行完毕
-
外部干涉:
- 线程类中 定义 线程体使用的标识
- 线程体使用该标识
- 提供对外的方法改变该标识
- 外部根据条件调用该方法即可
实例
package state; public class Demo01 { public static void main(String[] args) { Study s=new Study(); new Thread(s).start(); //外部干涉 for(int i=0;i<500;i++) { if(i==50) { s.stop(); } System.out.println("main...."+i); } } } class Study implements Runnable{ //第一步,线程类中定义线程体使用的标识 private boolean flag=true; public void run() { //第二步,线程体使用该表示 while(flag) { System.out.println("study thread..."); } } //第三步,对外提供方法改变该标识 public void stop() { this.flag=false; } }
三,阻塞
-
join :合并线程
-
线程对象.join():等待该线程终止
实例
package state; /* join():合并线程 */ public class JoinDemo01 extends Thread{ public static void main(String[] args) throws InterruptedException { JoinDemo01 demo=new JoinDemo01(); Thread t=new Thread(demo);//新生 t.start();//就绪 //Cpu调度运行 for(int i=0;i<1000;i++) { if(i==50) { /* * CPU随机调度,或者使用join方法 */ t.join();//合并线程,等待t线程执行完毕后再执行main线程(main阻塞) } System.out.println("main..."+i); } } @Override public void run() { for(int i=0;i<100;i++) { System.out.println("join..."+i); } } }
-
-
static yield:暂停线程
-
Thread.yield():在当前时间片暂停所在线程,并执行其它线程
-
实例
package state; public class YieldDemo01 extends Thread{ public static void main(String[] args) { YieldDemo01 demo=new YieldDemo01(); Thread t=new Thread(demo);//新生 t.start();//就绪 //Cpu调度运行 for(int i=0;i<1000;i++) { if(i%20==0) { //暂停本线程main Thread.yield();//在谁的线程体中就停止谁 } System.out.println("main..."+i); } } @Override public void run() { for(int i=0;i<100;i++) { System.out.println("yield..."+i); } } }
-
-
sleep:休眠,不释放锁
-
与时间相关:倒计时
-
实例
package state; import java.text.SimpleDateFormat; import java.util.Date; /* 倒计时 1,倒数10个数,一秒内打印一个 */ public class SleepDemo01 { public static void main(String[] args) throws InterruptedException { Date endTime=new Date(System.currentTimeMillis()+10*1000);//10秒后 long end=endTime.getTime(); while(true) { //输出 System.out.println(new SimpleDateFormat("mm:ss").format(endTime)); //构建上一秒时间--回溯 endTime=new Date(endTime.getTime()-1000); //等待1秒 Thread.sleep(1000); //10秒内继续否则推出 if(end-10000>endTime.getTime()) { break; } } } }
-
-
模拟网络延时 (并发)
-
线程基本信息和优先级
方法 | 功能 |
---|---|
isAlive() |
判断线程是否还“活"着,即线程是否还未终止 |
getPriority() |
获得线程的优先级数值 |
setPriority() |
设置线程的优先级数值 |
setName() |
给线程一个名字 |
getName() |
取得线程的名字 |
currentThread() |
取得当前运行的线程对象,即取得自身 |
实例
线程对象 public class MyThread implements Runnable { private boolean flag =true; private int num =0; @Override public void run() { while(flag){ System.out.println(Thread.currentThread().getName()+"-->"+num++); } } public void stop(){ this.flag=!this.flag; } }
测试类1/** * Thread.currentThread() :当前线程 setName():设置名称 getName():获取名称 isAlive():判断状态 * @author Administrator * */ public class InfoDemo01 { /** * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { MyThread it =new MyThread(); Thread proxy =new Thread(it,"挨踢"); proxy.setName("test"); System.out.println(proxy.getName()); System.out.println(Thread.currentThread().getName()); //main proxy.start(); System.out.println("启动后的状态:"+proxy.isAlive()); Thread.sleep(200); it.stop(); Thread.sleep(100); System.out.println("停止后的状态:"+proxy.isAlive()); } }
测试类2/** * 优先级:概率,不是绝对的先后顺序 MAX_PRIORITY 10 NORM_PRIORITY 5 (默认) MIN_PRIORITY 1 setPriority() getPriority() * @author Administrator * */ public class InfoDemo02 { /** * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { MyThread it =new MyThread(); Thread p1 =new Thread(it,"挨踢1"); MyThread it2 =new MyThread(); Thread p2 =new Thread(it2,"挨踢2"); p1.setPriority(Thread.MIN_PRIORITY); //设置优先级 p2.setPriority(Thread.MAX_PRIORITY);//设置优先级 p1.start(); p2.start(); Thread.sleep(100); it.stop(); it2.stop(); } }
线程的同步和死锁问题
线程的同步
-
由于统一进程的多个线程共享同一片存储空间,带来方便的同时,也带来了访问冲突这个严重的问题。Java语言提供了专门的机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问。
-
由于我们可以通过private关键字来保证数据对象只能被方法访问,所以我们只需要针对方法提出一套机制,这套机制就是synchronized关键字,定义同步锁的修饰语,包括两种用法:
- synchronized 方法
- synchronized(对象)块
修饰非静态方法和对象时
public class SynDemo01 { /** * @param args */ public static void main(String[] args) { //真实角色 Web12306 web= new Web12306(); //代理 Thread t1 =new Thread(web,"路人甲"); Thread t2 =new Thread(web,"黄牛已"); Thread t3 =new Thread(web,"攻城师"); //启动线程 t1.start(); t2.start(); t3.start(); } } /** * 线程安全的类 * @author Administrator * */ class Web12306 implements Runnable { private int num =10; private boolean flag =true; @Override public void run() { while(flag){ test5(); } } public void test6(){ if(num<=0){ flag=false; //跳出循环 return ; } //a b c synchronized(this){ try { Thread.sleep(500); //模拟 延时 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); } } //线程不安全 锁定资源不正确 public void test5(){ //锁住了成员变量,没有锁住对象,而多线程并行访问对象时直接穿透锁造成资源不一致 synchronized((Integer)num){ if(num<=0){ flag=false; //跳出循环 return ; } //没有锁住整个对象,多线程可能在某个时间片直接穿透抵达此处,比如num剩下1时,a线程穿过if的表达式后释放了锁,b线程紧随拿到锁。 try { Thread.sleep(500); //模拟 延时 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); } } //锁定范围不正确 线程不安全 public void test4(){ // c 1 synchronized(this){ //b if(num<=0){ flag=false; //跳出循环 return ; } } // b try { Thread.sleep(500); //模拟 延时 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); }//a -->1 //线程安全 锁定正确 public void test3(){ //a b c synchronized(this){ if(num<=0){ flag=false; //跳出循环 return ; } try { Thread.sleep(500); //模拟 延时 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); } } //线程安全 锁定正确 public synchronized void test2(){ if(num<=0){ flag=false; //跳出循环 return ; } try { Thread.sleep(500); //模拟 延时 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); } //线程不安全 public void test1(){ if(num<=0){ flag=false; //跳出循环 return ; } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); } }
当只锁住了成员变量而没有锁住对象时,多线程并行访问对象在num比较完就直接穿透锁造成资源不一致 -
单例设计模式:确保一个类在外部使用时,只有一个对象。
实例
package com.bjsxt.thread.syn; /** * 单例设计模式:确保一个类只有一个对象 * @author Administrator * */ public class SynDemo02 { /** * @param args */ public static void main(String[] args) { JvmThread thread1 = new JvmThread(100); JvmThread thread2 = new JvmThread(500); thread1.start(); thread2.start(); } } class JvmThread extends Thread{ private long time; public JvmThread() { } public JvmThread(long time) { this.time =time; } @Override public void run() { System.out.println(Thread.currentThread().getName()+"-->创建:"+Jvm.getInstance(time)); } } /** * 单例设计模式 * 确保一个类只有一个对象 * 懒汉式 double checking * 1、构造器私有化,避免外部直接创建对象 * 2、声明一个私有的静态变量 * 3、创建一个对外的公共的静态方法 访问该变量,如果变量没有对象,创建该对象 */ class Jvm { //声明一个私有的静态变量 private static Jvm instance =null; //构造器私有化,避免外部直接创建对象 private Jvm(){ } //创建一个对外的公共的静态方法 访问该变量,如果变量没有对象,创建该对象 public static Jvm getInstance(long time){ // c d e -->效率 提供 已经存在对象的访问效率 if(null==instance){ // a b synchronized(Jvm.class){ if(null==instance ){ try { Thread.sleep(time); //延时 ,放大错误 } catch (InterruptedException e) { e.printStackTrace(); } instance =new Jvm(); } } }//a return instance; } public static Jvm getInstance3(long time){ //a b c d e -->效率不高 c 存在对象也需要等待 synchronized(Jvm.class){ if(null==instance ){ try { Thread.sleep(time); //延时 ,放大错误 } catch (InterruptedException e) { e.printStackTrace(); } instance =new Jvm(); } return instance; } } public static synchronized Jvm getInstance2(long time){ if(null==instance ){ try { Thread.sleep(time); //延时 ,放大错误 } catch (InterruptedException e) { e.printStackTrace(); } instance =new Jvm(); } return instance; } public static Jvm getInstance1(long time){ if(null==instance ){ try { Thread.sleep(time); //延时 ,放大错误 } catch (InterruptedException e) { e.printStackTrace(); } instance =new Jvm(); } return instance; } }
单例设计模式的多种写法
/** * 单例创建的方式 * 1、懒汉式 * 1)、构造器私有化 * 2)、声明私有的静态属性 * 3)、对外提供访问属性的静态方法,确保该对象存在 * * @author Administrator * */ public class MyJvm { private static MyJvm instance; private MyJvm(){ } public static MyJvm getInstance (){ if(null==instance){ //提供效率 synchronized(MyJvm.class){ if(null==instance){ //安全 instance =new MyJvm(); } } } return instance; } } /** * 饿汉式 1)、构造器私有化 * 2)、声明私有的静态属性,同时创建该对象 * 3)、对外提供访问属性的静态方法 * @author Administrator * */ class MyJvm2 { private static MyJvm2 instance =new MyJvm2();//类加载时初始化 private MyJvm2(){ } public static MyJvm2 getInstance (){ return instance; } } /** * 类在使用的时候加载 ,延缓加载时机 * @author Administrator * */ class MyJvm3 { private static class JVMholder{//类加载时不初始化内部类 private static MyJvm3 instance =new MyJvm3(); } private MyJvm3(){ } public static MyJvm3 getInstance (){ return JVMholder.instance;//调用方法时初始化内部类 } }
死锁
package com.bjsxt.thread.syn; /* * 过多的同步方法容易造成死锁 */ public class Demo { public static void main(String[] args) { Object g=new Object(); Object m=new Object(); TestA t1=new TestA(g,m); TestB t2=new TestB(g,m); Thread p1=new Thread(t1); Thread p2=new Thread(t2); p1.start(); p2.start(); } } class TestA implements Runnable{ Object goods; Object money; public TestA(Object goods,Object money) { this.goods=goods; this.money=money; } @Override public void run() { while(true) { try { test(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public void test() throws InterruptedException { synchronized(goods) { Thread.sleep(100); synchronized(money) {x } } System.out.println("一手给钱"); } } class TestB implements Runnable{ Object goods; Object money; public TestB(Object goods,Object money) { this.goods=goods; this.money=money; } @Override public void run() { while(true) { try { test(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public void test() throws InterruptedException { synchronized(money) { Thread.sleep(100); synchronized(goods) { } } System.out.println("一手给货"); } }
生产者消费者模式
生产者消费者问题(Producer-consumer problem)/有限缓冲问题(bounded-buffer problem),是多线程同步问题的经典案例 。
-
该问题描述了两个共享固定大小缓冲区的线程——即所谓的“生产者"和”消费者“——在实际运行中会发生的问题。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。
-
要解决该问题,就必须让生产者在缓冲区空时进入休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据时,生产者才能被唤醒,开始往缓冲区添加数据。同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者。通常采用的方法有信号灯法、管程等。如果解决方法不够完善,则容易出现死锁的情况。出现死锁时,两个线程都会陷入休眠,等待对方唤醒自己。
信号灯法
Movie
package com.bjsxt.thread.pro; /** 一个场景,共同的资源 生产者消费者模式 信号灯法 wait() :等待,释放锁 sleep 不释放锁 notify()/notifyAll():唤醒 与 synchronized * @author Administrator * */ public class Movie { private String pic ; //信号灯 //flag -->T 生产生产,消费者等待 ,生产完成后通知消费 //flag -->F 消费者消费 生产者等待, 消费完成后通知生产 private boolean flag =true; /** * 播放 * @param pic */ public synchronized void play(String pic){ if(!flag){ //生产者等待 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //开始生产 try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("生产了:"+pic); //生产完毕 this.pic =pic; //通知消费 this.notify(); //生产者停下 this.flag =false; } public synchronized void watch(){ if(flag){ //消费者等待 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //开始消费 try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("消费了"+pic); //消费完毕 //通知生产 this.notifyAll(); //消费停止 this.flag=true; } }
Player
package com.bjsxt.thread.pro; /** * 生产者 * @author Administrator * */ public class Player implements Runnable { private Movie m ; public Player(Movie m) { super(); this.m = m; } @Override public void run() { for(int i=0;i<20;i++){ if(0==i%2){ m.play("左青龙"); }else{ m.play("右白虎"); } } } }
Watcher
package com.bjsxt.thread.pro; public class Watcher implements Runnable { private Movie m ; public Watcher(Movie m) { super(); this.m = m; } @Override public void run() { for(int i=0;i<20;i++){ m.watch(); } } }
App
package com.bjsxt.thread.pro; public class App { public static void main(String[] args) { //共同的资源 Movie m = new Movie(); //多线程 Player p = new Player(m); Watcher w = new Watcher(m); new Thread(p).start(); new Thread(w).start(); } }
管程法
package com.bjsxt.thread.pro; public class TestProduce { public static void main(String[] args) { SyncStack sStack = new SyncStack(); Shengchan sc = new Shengchan(sStack); Xiaofei xf = new Xiaofei(sStack); sc.start(); xf.start(); } } class Mantou { int id; Mantou(int id){ this.id=id; } } class SyncStack{ int index=0; Mantou[] ms = new Mantou[10]; public synchronized void push(Mantou m){ while(index==ms.length){ try { this.wait(); //wait后,线程会将持有的锁释放。sleep是即使睡着也持有互斥锁。 } catch (InterruptedException e) { e.printStackTrace(); } } this.notify(); //唤醒在当前对象等待池中等待的第一个线程。notifyAll叫醒所有在当前对象等待池中等待的所有线程。 //如果不唤醒的话。以后这两个线程都会进入等待线程,没有人唤醒。 ms[index]=m; index++; } public synchronized Mantou pop(){ while(index==0){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } this.notify(); index--; return ms[index]; } } class Shengchan extends Thread{ SyncStack ss = null; public Shengchan(SyncStack ss) { this.ss=ss; } @Override public void run() { for (int i = 0; i < 20; i++) { System.out.println("造馒头:"+i); Mantou m = new Mantou(i); ss.push(m); } } } class Xiaofei extends Thread{ SyncStack ss = null; public Xiaofei(SyncStack ss) { this.ss=ss; } @Override public void run() { for (int i = 0; i < 20; i++) { Mantou m = ss.pop(); System.out.println("吃馒头:"+i); } } }
任务调度
-
Timer定时器类
-
TimerTask任务类
实例
package com.bjsxt.thread.schedule; import java.util.Date; import java.util.Timer; import java.util.TimerTask; /** 了解 Timer() schedule(TimerTask task, Date time) schedule(TimerTask task, Date firstTime, long period) 自学 quartz * @author Administrator * */ public class TimeDemo01 { /** * @param args */ public static void main(String[] args) { Timer timer =new Timer(); timer.schedule(new TimerTask(){ @Override public void run() { System.out.println("so easy...."); }}, new Date(System.currentTimeMillis()+1000), 200); } }