java使用Thread类表明线程,全部的线程对象都必须是Thread类或其子类的实例。每条线程的做用是完成必定的任务,实际上就是执行一段程序流(一段顺序流的代码)。Java使用run方法来封装这样一段程序。 java
/**继承Thread来建立线程类*/ public class FirstThread extends Thread { private int i; //重写run方法,run方法的方法体就是线程执行体 public void run() { for(;i<10;i++){ System.out.println(this.getName()+":"+i); } } public static void main(String []args){ for(int i=0;i<20;i++){ System.out.println(Thread.currentThread().getName()+" .."+i); if(i==10){ System.out.println("--------------------------------------------"); new FirstThread().start(); new FirstThread().start(); System.out.println("---------------------------------------------"); } } } } 结果:红色部分每次运行都不一致,由于多线程也是并发的 main ..0 main ..1 main ..2 main ..3 main ..4 main ..5 main ..6 main ..7 main ..8 main ..9 main ..10 -------------------------------------------- Thread-0:0 --------------------------------------------- Thread-1:0 Thread-1:1 Thread-1:2 Thread-1:3 Thread-0:1 Thread-1:4 Thread-1:5 main ..11 Thread-1:6 Thread-1:7 Thread-1:8 Thread-1:9 Thread-0:2 Thread-0:3 main ..12 main ..13 ......
总结 :从上面结果能够看出Thread-0和Thread-1两条线程输出的i变量都不连续(注意:i变量是FirestThread的实例属性,而不是局部变量,但由于程序每次建立线程都会建立一个FirstThread对象,因此Thread-0和Thread-1不能共享该实例属性)。 多线程
使用继承Thread类的方法来建立线程类,多条线程之间没法共享线程类的实例变量。 并发
public class SecondThread implements Runnable { private int i; public void run() { for(;i<20;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } } public static void main(String [] args){ for(int i=0;i<20;i++){ System.out.println(Thread.currentThread().getName()+" .."+i); if(i==10){ SecondThread st=new SecondThread(); //经过new Thread( Runable target,String name)来建立新线程 new Thread(st,"线程1").start(); new Thread(st,"线程2").start(); } } } 结果:红色部分每次运行都不一致,由于多线程也是并发的 main ..0 main ..1 main ..2 main ..3 main ..4 main ..5 main ..6 main ..7 main ..8 main ..9 main ..10 -------------------------------------------- 线程1:0 -------------------------------------------- 线程1:1 线程2:1 线程2:3 main ..11 线程2:4 线程2:5 线程2:6 线程1:2 线程2:7 线程2:9 线程2:10 线程2:11 线程2:12 线程2:13 main ..12 线程2:14 线程2:15 线程2:16 线程2:17 线程1:8 线程2:18 main ..13 main ..14 线程1:19 main ..15 main ..16 main ..17 。。。。
总结:根据源代码中Thread类构造方法 Ruanalbe接口对象target只能做为参数传递到Thread构造方法中,因此多个线程能够共用一个Runnable对象,由于都用同一个Runnable对象因此在Runnable实现类的实例变量也能够共享了。 ide
因此Runable很是适合多个相同线程来处理同一份资源的状况。 测试
1.New新建 :当线程被建立时,该线程处于新建状态,此时它和其余java对象同样,仅仅由Java虚拟机为其分配了内存,并初始化了其成员变量的值。(此时的线程没有表现出任何表现出任何线程的动态特征,程序也不会执行线程的线程执行体)new Thread()||new Thread(Runnable target,String name)。 this
2.Runnable就绪:就绪也就是说启动线程,可是启动线程使用start方法,而不是run方法!永远不要调用线程对象的run()方法!调用start方法来启动线程,系统会将该run方法当成线程执行体来处理。若是直接调用线程对象的run方法。则run方法会当即执行,且在这个run方法的执行体未执行结束前其余线程没法并发执行(即系统会将run方法当作一个普通对象的普通方法,而不是线程执行体对待) spa
附1:若是有一个主线程,一个子线程。当根据逻辑代码该调用子线程时不必定会当即调用,为了想在子线程start()后当即调用子线程,能够考虑使用Thread.sleep(1),这样会让当前线程(主线程)睡眠1毫秒,由于cpu在这1毫秒中是不会休息的,这样就会去执行一条处于就绪状态的线程。 线程
附2:不能对已经处于就绪状态的线程,再次使用start() unix
3.Running 运行:当处于就绪状态时,该线程得到cpu,执行体开始运行,就处于运行状态了。
4.Blocked 阻塞:线程不可能一直处于运行状态(线程执行体足够短,瞬间就能够完成的线程排除),线程会在运行过程当中须要被中断,由于是并发,目的是会让其余线程得到执行的机会,线程的调度细节取决于OS采用的策略。(抢占式调度xp win7 linux unix..)。若是是一些特殊的小型设备可能采用 协做式调度(只有线程本身调用它的sleep()或yield()才会放弃所占用的资源)。
5.Dead死亡:根据上图所示。测试测试某条线程是否已经死亡,能够调用线程对象的isAlive()方法,当线程处于就绪,运行,阻塞时,返回true。线程处于新建,死亡时返回false。
不能对已经死亡的线程调用start()方法使它从新启动,死亡就是死亡,是不能再次做为线程执行的。
当主线程结束时候,其余线程不受任何影响,并不会随之结束。一旦子线程启动起来后,它就拥有和主线程相同的地位,它不会受到主线程的影响。
让一个线程等待另外一个线程完成的方法:join()。当在某个程序执行流中调用其余线程的join()方法,那该执行流对应的线程就会阻塞,知道被join()加入的join线程完成为止。join方法一般有使用线程的程序调用,将大问题划分红许多小问题,每一个小问题分配一个线程。当全部的小问题都获得处理后,再调用 主线程来进一步操做(Thread t=new Thread();t.start();t.join简单来讲就是加入到t线程。等t线程执行完成后才会返回出来执行线程。)
Join方法有三种重用形式:
Join():等待被join的线程执行完成
Join(long millis):等待join线程的时间最长为millis毫秒,若是在这个时间内,被join的线程尚未执行结束则再也不等待)
Join(long millis,int nanos)千分之一毫秒(不用)
Code:
public class JoinThread implements Runnable{ @Override public void run() { for(int i=0;i<5;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } } public static void main(String [] args) throws InterruptedException{ //实例化一个Runnable JoinThread jt=new JoinThread(); //建立一个线程 new Thread(jt).start(); for(int i=0;i<10;i++){ if(i==3){ Thread th=new Thread(jt); //启动第二个线程 th.start(); //main的线程中调用了th线程的join方法 //让第二个线程执行完成后再执行main th.join(); } System.out.println(Thread.currentThread().getName()+":"+i); } } } 结果: Thread-0:0 Thread-0:1 Thread-0:2 main:0 main:1 Thread-0:3 main:2 Thread-0:4 Thread-1:0 Thread-1:1 Thread-1:2 Thread-1:3 Thread-1:4 main:3 main:4 main:5 main:6 main:7 main:8 main:9
Code:
public class DaemonThread implements Runnable{ @Override public void run() { for(int i=0;i<100;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } } public static void main(String [] args){ //要将前台线程转换成后台线程,须要在该线程刚新建还未start()以前转换。main线程也是前台线程 //全部前台线程死亡时,后台线程也就随之死亡。 DaemonThread dt=new DaemonThread(); Thread td=new Thread(dt,"线程1"); System.out.println("main方法是不是后台线程"+Thread.currentThread().isDaemon()); System.out.println("td线程最初是不是后台线程"+td.isDaemon()); //指定td为后台线程 td.setDaemon(true); System.out.println("td线程执行setDaemon方法后是不是后台线程"+td.isDaemon()); //就绪启动后台线程 td.start(); for(int i=0;i<5;i++){ System.out.println(Thread.currentThread().getName()+" "+i); } } } 结果:只要前台线程结束,后台线程也会随之结束,并非立刻结束 main方法是不是后台线程false td线程最初是不是后台线程false td线程执行setDaemon方法后是不是后台线程true main 0 main 1 线程1:0 线程1:1 main 2 线程1:2 线程1:3 main 3 线程1:4 线程1:5 main 4 线程1:6 线程1:7 线程1:8 线程1:9 线程1:10 线程1:11 线程1:12 线程1:13
/** * 线程睡眠:sleep有两种重载形式: * static void sleep(long millis) * static void sleep(long millis,int nanos) * */ public class SleepThread { public static void main(String [] args) throws InterruptedException{ for(int i=0;i<5;i++){ System.out.println("线程:"+Thread.currentThread().getName()+"当前时间:"+new Date()); //让当前线程暂停2秒 Thread.sleep(2000); } } } 结果: 线程:main当前时间:Fri Nov 04 18:51:33 CST 2011 线程:main当前时间:Fri Nov 04 18:51:35 CST 2011 线程:main当前时间:Fri Nov 04 18:51:37 CST 2011 线程:main当前时间:Fri Nov 04 18:51:39 CST 2011 线程:main当前时间:Fri Nov 04 18:51:41 CST 2011
/** * yield()方法是一个和sleep方法有点相似的静态方法。yield也可让当前正在执行的线程暂停 * 但它不会阻塞该线程,它只是将该线程转入就绪状态。yield只是让当前线程暂停一下子,让系统的 * 调度器从新调度一次(彻底可能的状况是:当一个线程调用了yield方法暂停以后,线程调度器又立刻 * 将其调度出来从新执行。) * 实际上,当前线程调用了yield方法后,只有优先级和当前线程相同,甚至优先级高于当前线程的处于 * 就绪状态的线程才会得到执行机会。 * */ public class YieldThread implements Runnable{ @Override public void run() { for(int i=0;i<50;i++){ System.out.println(Thread.currentThread().getName()+":"+i); if(i==20){ Thread.yield(); } } } public static void main(String [] args){ //启动第一条子线程 Thread td1=new Thread(new YieldThread(),"线程1"); //最高级 //td1.setPriority(Thread.MAX_PRIORITY); //启动第二条子线程 Thread td2=new Thread(new YieldThread(),"线程2"); //最低级 td2.setPriority(Thread.MIN_PRIORITY); td1.start(); td2.start(); System.out.println(Thread.currentThread().getName()); } }
总结:sleep和yield区别
A.sleep方法暂停当前线程后,会给其余线程执行机会,不会理会其余线程的优先级。而yield只会给优先级>=当前优先级的线程执行机会
B.Sleep方法会将线程转入阻塞状态,知道通过阻塞时间才会转入就绪状态。而yield是不会将线程转入阻塞状态的,它只是强制当前线程进入就绪状态。
C.Sleep会抛出InterruptedException异常。而yield没有声明任何异常
D.Sleep方法比yield方法有更好的移植性。
E.一般不依靠yield来控制并发线程控制