Java多线程(全)学习笔记(上)

一.线程的建立和启动

java使用Thread类表明线程,全部的线程对象都必须是Thread类或其子类的实例。每条线程的做用是完成必定的任务,实际上就是执行一段程序流(一段顺序流的代码)。Java使用run方法来封装这样一段程序。 java

1.继承Thread类建立线程类 linux

/**继承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类的方法来建立线程类,多条线程之间没法共享线程类的实例变量。 并发

2.实现Runnable接口建立线程类

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()方法使它从新启动,死亡就是死亡,是不能再次做为线程执行的。

当主线程结束时候,其余线程不受任何影响,并不会随之结束。一旦子线程启动起来后,它就拥有和主线程相同的地位,它不会受到主线程的影响。

三.控制线程

1.join线程:

让一个线程等待另外一个线程完成的方法:join()。当在某个程序执行流中调用其余线程的join()方法,那该执行流对应的线程就会阻塞,知道被join()加入的join线程完成为止。join方法一般有使用线程的程序调用,将大问题划分红许多小问题,每一个小问题分配一个线程。当全部的小问题都获得处理后,再调用 主线程来进一步操做(Thread t=new Thread();t.start();t.join简单来讲就是加入到t线程。等t线程执行完成后才会返回出来执行线程。

     Join方法有三种重用形式:

           Join():等待被join的线程执行完成

           Join(long millis):等待join线程的时间最长为millis毫秒,若是在这个时间内,被join的线程尚未执行结束则再也不等待)

           Joinlong millisint 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



2.后台线程:

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



3.线程睡眠:sleep

/** 
 * 线程睡眠: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



4.线程让步(yield

/** 
 * 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());  
}  
}



总结:sleepyield区别

A.sleep方法暂停当前线程后,会给其余线程执行机会,不会理会其余线程的优先级。而yield只会给优先级>=当前优先级的线程执行机会

B.Sleep方法会将线程转入阻塞状态,知道通过阻塞时间才会转入就绪状态。而yield是不会将线程转入阻塞状态的,它只是强制当前线程进入就绪状态。

C.Sleep会抛出InterruptedException异常。而yield没有声明任何异常

D.Sleep方法比yield方法有更好的移植性。

E.一般不依靠yield来控制并发线程控制

相关文章
相关标签/搜索