1、线程状态:
- 新生状态
用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态,处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态(rumnable)
- 就绪状态
处于就绪状态的线程已经具备了运行的条件,但还没有分配到cpu,处于线程就绪队列,等待系统为其分配CPU,等待状态并不是执行状态,当系统选定一个等待执行的Thread对象后,他/她就会从等待执行状态进入执行状态,系统挑选的动作称之为“CPU调度”。一旦获得CPU,线程就进入运行状态并自动调用自己的run方法。
- 运行状态
在运行状态的线程执行自己的run方法中代码,知道调度其他方法而终止、或等待某资源而阻塞或完成任务而死亡。如果在给定的时间片内没有执行结束,就会被系统给换下来回到等待执行状态
- 阻塞状态
处于运行状态的线程在某些情况下,如执行了sheep(睡眠)方法,或等待I/O设备等资源,将让出CPU并暂时停止自己的运行,进入阻塞状态,在阻塞状态的线程不能进入就绪队列,只有当引起阻塞的原因消除时,如睡眠时间已到,或等待的I/O设备空闲下来,线程便转入就绪状态,重新到就绪队列中排队等待,被系统选中后从原来停止的位置开始继续运行。
- 死亡状态
死亡状态是线程生命周期中的最后一个阶段,线程死亡的原因有两个。一个是正常运行的线程完成了它全部工作,另一个是线程被强制性的终止,如通过执行stop或destroy方法来终止一个线程(不推荐使用这两个方法,前者会产生异常,后者是强制终止,不会释放锁)
2、停止线程
1)自然终止:线程体正常执行完毕
2)外部干涉
1、在线程类中定义线程体使用的标识
2、线程体使用该标识
3、提供对外的方法改变该标识
4、外部根据条件调用该方法即可
3、阻塞
1) join:合并线程
2)yield:暂停自己的线程 静态方法
3) sleep:休眠,不释放锁
*与时间相关:倒计时
*模拟网络延时
4、线程的同步(并发)
1)由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题。java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问。
2)由于我们可以通过private关键字来保证数据对象只能被方法访问,所有我们只需针对方法提出一套机制,这套机制就是synchronized关键字,它包括两种用法:synchronized方法和synchronized块。
同步:多个线程访问同一份资源 确保资源安全--->线程安全
- 同步块
synchronized(引用类型|this|类.class){
}
- 同步方法
public synchronized void test1(){
}
线程安全:保证资源安全,相对效率低
锁定范围过大:效率低下
锁定范围过小:没有保证资源安全
例1:
1 public class SynDemo1 { 2 public static void main(String[] args) { 3 JvmThread thread1 = new JvmThread(100); 4 JvmThread thread2 = new JvmThread(500); 5 thread1.start(); 6 thread2.start(); 7 } 8 } 9 10 class JvmThread extends Thread{ 11 private long time; 12 public JvmThread() { 13 } 14 public JvmThread(long time){ 15 this.time = time; 16 } 17 @Override 18 public void run() { 19 System.out.println(Thread.currentThread().getName()+ 20 "-->创建:"+Jvm.getInstance1(time)); 21 } 22 } 23 24 /** 25 * 单例设计模式 26 * 确保一个类只有一个对象 27 * 懒汉式(使用的创建对象): 28 * 1、构造器私有化,避免外部直接创建对象 29 * 2、声明一个私有的静态变量 30 * 3、对外提供一个公共的静态方法访问该变量, 31 * 如果变量没有对象,创建对象,否则直接返回 32 * @author qjc 33 * 34 * 2016-3-13 35 */ 36 class Jvm{ 37 private static Jvm instance = null; 38 private Jvm() { 39 } 40 //线程不安全 41 public static Jvm getInstance1(long time){ 42 if(null==instance){ 43 try { 44 Thread.sleep(time);//延时 放大错误 45 } catch (InterruptedException e) { 46 e.printStackTrace(); 47 } 48 instance = new Jvm(); 49 } 50 return instance; 51 } 52 public static synchronized Jvm getInstance2(long time){ 53 if(null==instance){ //安全 54 try { 55 Thread.sleep(time);//延时 放大错误 56 } catch (InterruptedException e) { 57 e.printStackTrace(); 58 } 59 instance = new Jvm(); 60 } 61 return instance; 62 } 63 public static Jvm getInstance3(long time){ 64 //a b c d e都在此等待 -->效率不高 存在对象也需要等待 65 synchronized (Jvm.class) { //静态代码块里没有this 66 if(null==instance){ //安全 67 try { 68 Thread.sleep(time);//延时 放大错误 69 } catch (InterruptedException e) { 70 e.printStackTrace(); 71 } 72 instance = new Jvm(); 73 } 74 return instance; 75 } 76 } 77 //double checking 双重检查 78 public static Jvm getInstance4(long time){ 79 //b c d e 直接返回对象-->效率 提供已经存在对象的访问效率 80 if(null==instance){ 81 //a 创建对象 82 synchronized (Jvm.class) { //静态代码块里没有this 83 if(null==instance){ //安全 84 try { 85 Thread.sleep(time);//延时 放大错误 86 } catch (InterruptedException e) { 87 e.printStackTrace(); 88 } 89 instance = new Jvm(); 90 } 91 } 92 } 93 return instance; 94 } 95 }
5、死锁
建立在同步之上,过多的同步容易造成死锁
例2:
/** * 过多同步容易造成死锁 * @author qjc * * 2016-3-13 */ public class SynDemo2 { public static void main(String[] args) { Object goods = new Object(); Object money = new Object(); Test t1 = new Test(goods,money); Test t2 = new Test(goods,money); Thread proxy = new Thread(t1); Thread proxy2 = new Thread(t2); proxy.start(); proxy2.start(); } } class Test implements Runnable{ Object goods; Object money; public Test(Object goods, Object money) { super(); this.goods = goods; this.money = money; } @Override public void run() { while (true) { test(); } } public void test() { synchronized (goods) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (money) { } } System.err.println("一手给钱"); } } class Test2 implements Runnable{ Object goods ; Object money ; public Test2(Object goods, Object money) { super(); this.goods = goods; this.money = money; } @Override public void run() { while (true) { test(); } } public void test() { synchronized (money) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (goods) { } } System.err.println("一手给货"); } }
解决死锁方法:生产者消费者模式(不是设计模式)
- 生产者消费者问题,也称有限缓冲问题,是一个多线程同步问题的经典案例。该问题描述了两个共享固定大小缓冲区的线程----即所谓的“生产者”和“消费者” ---在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。在此同时,消费者也在缓冲区消费这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。
- 要解决该问题,就必须谈生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者。通常常用的方法由信号灯法、管程等。如果解决方法不够完善,则容易出现死锁的情况。出现死锁时,两个线程都会陷入休眠,等待对方唤醒自己。
例3:
/** * 一个场景,共同的资源 * 生产者消费者模式:信号灯法 * wait():等待,释放锁 * sleep():不释放锁 * notify()/notifyAll():唤醒 * @author qjc * * 2016-3-13 */ public class Movie { private String pic; /** * 信号灯 * flag = T 生产者生产,消费者等待,生产完成后通知消费 * flag = F 消费者消费,生产者等待,消费完后通知生产 */ private boolean flag = true; 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); //消费完毕 System.out.println("消费了"+pic); //通知生产 this.notifyAll(); //消费停止 this.flag = true; } } /** * 生产者 * @author qjc * * 2016-3-13 */ 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("右白虎"); } } } } /** * 消费者 * @author qjc * * 2016-3-13 */ 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(); } } } /** * 测试 * @author qjc * * 2016-3-13 */ 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(); } }
sleep()与wait()区别
对于sleep()方法,我们首先要知道该方法是属于Thread类中的。而wait()方法,则是属于Object类中的。
sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。
在调用sleep()方法的过程中,线程不会释放对象锁。
而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备
获取对象锁进入运行状态。
6、任务调度
- Timer 定时器类
- TimerTask 任务类
- 通过java timer timertask :(spring的任务调度就是通过他们来实现的)
- 在这种实现方式中,Timer类实现的是类似闹钟的功能,也就是定时或者每个一定时间触发一次线程。其实,Timer类本身实现的就是一个线程,只是这个线程是用来实现调用其他线程的。而TimerTask类似一个抽象类,该类实现了Runnable接口,所以按照前面的介绍,该类具备多线程的能力。
- 在这种实现方式中,通过继承TimerTask是该类获得多线程的能力,将需要多线程执行的代码书写在run方法内部,然后通过Timer类启动线程的执行。
- 在实际使用时,一个Timer可以启动任意多个TimerTask实现线程,但是多个线程之间会存在阻塞。所以如果多个线程之间如果需要完全独立运行的话,最好还是一个Timer启动一个TimerTask实现。
例4:
/** * @author qjc * * 2016-3-13 */ public class TimeDemo { 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) , 500);//参数3:每隔500秒调用一次,没有参数3只调用一次 } }
第三方框架:quartz juc
7、总结
1、创建线程:
- 继承Thread
- 实现Runnable
- 实现Callable
2、线程状态
1)新生-->start -->就绪-->运行-->阻塞-->终止
2)终止线程
3)阻塞:join yield sleep
3、线程的信息
1)Thread.currentThread
2) 获取名称设置名称设置优先级判断状态
4、同步 ----对同一份资源