一、概念
线程:是一个程序里面不同的执行路径,每一个分支都叫线程。到现在为止我们所讲的程序分支只有一个,即main方法,称作主线程。
进程:class文件,exe文件。程序的执行过程:程序放入代码区(进程产生,准备开始执行,进程是静态的概念),平时所说的“进程的执行”是指进程中主线程(main)的执行,实际运行得都是线程。
Windows Linux Unix操作系统是支持多进程、多线程的,Dos是只支持单进程的。一个时间点上一个cpu只有一个线程在执行,CPU很快,看起来像是同时运行。真正的多线程:多个CPU,双核。
二、线程的产生方式
Java的线程是通过java.lang.Thread类实现的。可以通过创建Thread的实例来创建新的线程。通过调用Thread类的start()方法来启动一个线程。VM启动时会有一个由主方法所定义的线程。每个线程都是通过某个特定Thread对象所对应的run()方法来完成其操作的,run()方法称为线程体。
可以有两种方式创建新的线程:
方法一:(建议使用此法)
(1)定义线程类实现Runnable接口
(2)Thread myThread = new Thread(target)//target为Runnable接口类型
(3)Runnable中只有一个方法:public void run();//用以定义线程运行体
(4)使用Runnable接口可以为多个线程提供共享的数据
(5)在实现Runnable接口的类的run方法定义中可以使用Thread的静态方法:public static Thread currentThread()//获得当前线程的引用
方法一举例:要new Thread对象来调用start方法启动线程,之后产生分支,主线程和分支线程并行执行;但是如果直接调用run方法,就是方法调用,还是单线程。
public class TestThread1{
public static void main(String args[]){
Runner1 r = new Runner1();
Thread t = new Thread(r);
t.start();//不可以直接用r.start();或r.run()
for(int i=0; i<100; i++){
System.out.println("Main Thread:---------------------"+i);
}
}
}
class Runner1 implements Runnable{
public void run(){
for(int i=0; i<100; i++){
System.out.println("Runner1:"+i);
}
}
}
方法二:
(1)可以定义一个Thread的子类并重写其run方法。如:class MyThread extends Thread{public void run(){……}}
(2)然后生成该类的对象:MyThread myThread = new MyThread(……)
方法二举例:
public class TestThread1{ public static void main(String args[]){ Runner1 r = new Runner1(); r.start();//可以直接用r.start();但不可以r.run() for(int i=0; i<100; i++){ System.out.println("Main Thread:---------------------"+i); } } } class Runner1 extends Thread{ public void run(){ for(int i=0; i<100; i++){ System.out.println("Runner1:"+i); } } }
三、线程状态转换
start之后进入“就绪”,等待cpu时间片,时间片到达后进入“运行”,阻塞事件发生(如IO),事件解决则进入“就绪”
线程控制的基本方法:
注:就绪,运行,阻塞属于“活着”。优先级高,则分配的时间多。
1、sleep方法:使得当前线程休眠(暂时停止执行millis毫秒)
可以调用Thread的静态方法:public static void sleep(long millis) throws InterruptedException,因为抛异常所以必须要写try catch。
由于是静态方法,所以可以由类名直接调用Thread.sleep(…);
sleep方法举例:在哪个线程调用sleep就是让这个线程睡眠,如第7行的sleep方法是让主线程睡眠。而第10行是主线程睡眠10000ms后,来打断睡眠1000ms中的MyThread线程,导致该子线程抛异常。catch到异常后,该子线程结束。
1 import java.util.*;//因为是用Date() 2 public class TestInterrupt{ 3 public static void main(String args[]){ 4 MyThread thread = new MyThread(); 5 thread.start(); 6 try{ 7 Thread.sleep(10000); 8 }catch(InterruptedException e){ 9 } 10 thread.interrupt();//Interrupt不是让子线程结束的好方法。stop更不好,尽量也不要使用 11 } 12 } 13 class MyThread extends Thread{ 14 public void run(){ 15 while(true){ 16 System.out.println("==="+new Date()+"==="); 17 try{ 18 sleep(1000); 19 }catch(InterruptedException e){ 20 return; 21 } 22 } 23 } 24 }
输出:
===Mon Apr 21 17:16:28 CST 2014===
===Mon Apr 21 17:16:29 CST 2014===
===Mon Apr 21 17:16:30 CST 2014===
===Mon Apr 21 17:16:31 CST 2014===
===Mon Apr 21 17:16:32 CST 2014===
===Mon Apr 21 17:16:33 CST 2014===
===Mon Apr 21 17:16:34 CST 2014===
===Mon Apr 21 17:16:35 CST 2014===
===Mon Apr 21 17:16:36 CST 2014===
===Mon Apr 21 17:16:37 CST 2014===
注意:Interrupt不是让子线程结束的好方法。stop更不好,尽量也不要使用,建议这样:控制flag使run方法结束,run方法结束则线程结束。
import java.util.*;//因为是用Date() public class TestInterrupt{ public static void main(String args[]){ MyThread thread = new MyThread(); thread.start(); try{ Thread.sleep(10000); }catch(InterruptedException e){ } thread.shutDown(); } } class MyThread extends Thread{ private boolean flag = true ; public void run(){ while(flag==true){ System.out.println("==="+new Date()+"==="); try{ sleep(1000); }catch(InterruptedException e){ return; } } } public void shutDown(){ flag = false; } }
注意:重写的方法不能比父类抛出不同的异常,所以不能在run方法抛出异常。
2、join方法:合并某个线程
join方法举例:第7行,join是合并两个线程,相当于方法调用。程序先执行子线程,主线程再执行。
1 import java.util.*;//因为是用Date() 2 public class TestJoin{ 3 public static void main(String args[]){ 4 MyThread t = new MyThread("abcde"); 5 t.start(); 6 try{ 7 t.join(); 8 }catch(InterruptedException e){ 9 } 10 for(int i=1; i<=10; i++){ 11 System.out.println("I am main thread----"+i); 12 } 13 } 14 } 15 16 class MyThread extends Thread{ 17 MyThread(String s){ 18 super(s); 19 } 20 public void run(){ 21 for(int i=1; i<=10; i++){ 22 System.out.println("I am "+getName()+"==="+i); 23 try{ 24 sleep(1000); 25 }catch(InterruptedException e){ 26 return; 27 } 28 } 29 } 30 }
输出:
I am abcde===1
I am abcde===2
I am abcde===3
I am abcde===4
I am abcde===5
I am abcde===6
I am abcde===7
I am abcde===8
I am abcde===9
I am abcde===10
I am main thread----1
I am main thread----2
I am main thread----3
I am main thread----4
I am main thread----5
I am main thread----6
I am main thread----7
I am main thread----8
I am main thread----9
I am main thread----10
3、yield方法:让出CPU,给其他线程执行的机会
yeild方法举例:下面程序有三条路径(main,t1,t2),每次到10的倍数则让出cpu。不经常用。
1 public class TestYeild{ 2 public static void main(String args[]){ 3 MyThread t1 = new MyThread("t1"); 4 MyThread t2 = new MyThread("t2"); 5 t1.start(); 6 t2.start(); 7 } 8 } 9 10 class MyThread extends Thread{ 11 MyThread(String s){ 12 super(s); 13 } 14 public void run(){ 15 for(int i=1; i<=100; i++){ 16 System.out.println("I am "+getName()+"==="+i); 17 if(i%10==0){ 18 yield(); 19 } 20 } 21 } 22 }
四、线程优先级(优先级高得到的cpu执行的时间片多)
Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程。线程调度器按照线程的优先级决定应调度哪个线程来执行。线程的优先级用数字表示,优先级范围1~10,默认是5。
Thread.MIN_PRIORITY = 1
Thread.MAX_PRIORITY = 10
Thread.NORM_PRIORITY = 5
使用下述方法获得或设置线程对象的优先级:
int getPriority();
void setPriority(int newPriority);
举例:t1的优先级高于t2,但并非等到t1执行完毕后t2才执行。
public class TestPriority{ public static void main(String args[]){ Thread t1 = new Thread(new T1()); Thread t2 = new Thread(new T2()); t1.setPriority(Thread.NORM_PRIORITY+3); t1.start(); t2.start(); } } class T1 implements Runnable{ public void run(){ for(int i=1; i<=100; i++){ System.out.println("T1:"+i); } } } class T2 implements Runnable{ public void run(){ for(int i=1; i<=100; i++){ System.out.println("--------T2:"+i); } } }
i<=1000时的部分输出:
四、其他例子
1、同一个线程对象,用来启动两个线程
public class Test{ public static void main(String args[]){ Runner r = new Runner(); Thread t1 = new Thread(r); Thread t2 = new Thread(r); t1.start(); t2.start(); } } class Runner implements Runnable{ public void run(){ for(int i=0; i<30; i++){ System.out.println("No."+i); } } }
2、currentThread是当前线程(this是当前对象)
public class Test{ public static void main(String args[]){ Thread t = new Runner(); t.start(); for(int i=0; i<30; i++){ System.out.println("MainThread:"+i); } } } class Runner extends Thread{ public void run(){ System.out.println(Thread.currentThread().isAlive()); for(int i=0; i<30; i++){ System.out.println("SubThread:"+i); } } }