多執行緒的三種建立方式
繼承Thread類建立執行緒類
public class Thread extends Object implements Runnable
- 定義Thread類的子類,並重寫其run()方法
- 建立Thread子類的例項,即建立了執行緒物件
- 呼叫執行緒物件的start()方法啟動執行緒
public class FirstThread extends Thread {
public void run(){
for(int i=0;i<100;i++){
/*
* Thread類已經繼承了Object
* Object類建立了name選項 並且有其getName(),setName()方法
* 在繼承Thread的類裡面使用時只需要用this引用
*/
System.out.println(this.getName()+" "+i);
}
}
public static void main(String[] args) {
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
if(i==20){
new FirstThread().start();
new FirstThread().start();
}
}
}
}
Thread類已經繼承了Object
Object類建立了name選項 並且有其getName(),setName()方法
在繼承Thread的類裡面使用時只需要用this引用上面兩個副執行緒和主執行緒隨機切換,又因為使用的是繼承Thread的類所以兩個副執行緒不能共享資源
start()方法呼叫後並不是立即執行多執行緒程式碼,而是使得該執行緒程式設計可執行狀態,什麼時候執行是由作業系統決定的
實現Runnable介面建立執行緒類
public Thread()
public Thread(Runnable target)
public Thread(Runnable target,String name)
- 定義Runnable介面的實現類,並重寫該介面的run()方法
- 建立Runnable實現類的例項,並以此作為Thread的target來建立Thread物件,該Thread物件才是真正的執行緒物件。
public class SecondThread implements Runnable {
public void run(){
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
public static void main(String[] args) {
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
if(i==20){
SecondThread st=new SecondThread();
//通過new Thread(target,name)建立執行緒
new Thread(st,"新執行緒1").start();
new Thread(st,"新執行緒2").start();
}
}
}
}
上面的結果是兩個副執行緒和主執行緒隨機切換,但是並沒有共享資源,因為他們根本沒有能用來共享的資源。
start()方法呼叫後並不是立即執行多執行緒程式碼,而是使得該執行緒程式設計可執行狀態,什麼時候執行是由作業系統決定的
繼承Thread類和建立Runnable介面的共享資源詳解
在只有可以用來共享的資源時候,也就是同用一個例項化物件。兩個建立方式在共享資源時才會有所區別,否則它們都不會共享資源共享資源通常用private static 修飾符來修飾。
class Thread1 extends Thread{
private int count=5;
private String name;
public Thread1(String name) {
this.name=name;
}
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(name + "執行 count= " + count--);
try {
sleep((int) Math.random() * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Main {
public static void main(String[] args) {
Thread1 mTh1=new Thread1("A");
Thread1 mTh2=new Thread1("B");
mTh1.start();
mTh2.start();
}
}
B執行 count= 5
A執行 count= 5
B執行 count= 4
B執行 count= 3
B執行 count= 2
B執行 count= 1
A執行 count= 4
A執行 count= 3
A執行 count= 2
A執行 count= 1
正是因為有了private int count=5;一句才有了共享資源,但這是繼承Thread類的子類,並不能共享資源
class Thread2 implements Runnable{
private int count=15;
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "執行 count= " + count--);
try {
Thread.sleep((int) Math.random() * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Main {
public static void main(String[] args) {
Thread2 my = new Thread2();
new Thread(my, "C").start();//同一個mt,但是在Thread中就不可以,如果用同一個例項化物件mt,就會出現異常
new Thread(my, "D").start();
new Thread(my, "E").start();
}
}
C執行 count= 15
D執行 count= 14
E執行 count= 13
D執行 count= 12
D執行 count= 10
D執行 count= 9
D執行 count= 8
C執行 count= 11
E執行 count= 12
C執行 count= 7
E執行 count= 6
C執行 count= 5
E執行 count= 4
C執行 count= 3
E執行 count= 2
同樣的正是因為有了private int count=15這個共同的例項化物件,實現Runnable的類才可以共享資源
那麼為什麼繼承Thread類的子類實現Runable介面的類在共享資源時有區別呢?
因為Java中只能支援單繼承,單繼承特點意味著只能有一個子類去繼承
而Runnabl介面後可以跟好多類,便可以進行多個執行緒共享一個資源的操作
使用Callable和Future建立執行緒
Callable怎麼看起來都像Runnable介面的增強版,Callable有一個call()方法相當於Runnable的run()方法,但是功能卻更加強大:
call()方法可以有返回值
call()方法可以宣告丟擲異常Callable介面有泛型限制,Callable接口裡的泛型形參型別與call()方法的返回值型別相同。
而且Callable介面是函式式介面,因此可使用Lambda表示式建立Callable物件
Runnable介面也是函式式介面,因此也可以使用Lambda表示式建立Runnable物件
- 建立Callable介面的實現類,並實現call()方法,該call()方法將作為執行緒執行體,再建立Callable實現類的例項
- 使用FutureTask類來包裝Callable物件,該FutureTask物件封裝了該Callable物件的call()方法
- 使用FutureTask類物件作為Thread物件的target建立並啟動新執行緒
- 呼叫FutureTask物件的get()方法來獲得子執行緒結束後的返回值
public class ThirdThread implements Callable<Integer> {
public Integer call(){
int i=0;
for(;i<100;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
}
return i;
}
public static void main(String[] args){
ThirdThread tt=new ThirdThread();
FutureTask<Integer> task=new FutureTask<>(tt);
Thread t=new Thread(task,"有返回值的執行緒");
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
if(i==20){
t.start();
}
}
try{
System.out.println("返回值是:"+task.get());
}catch(Exception e){
e.printStackTrace();
}
}
}
使用Lambda表示式的Callable和Future建立的執行緒
public class ThirdThread{
public static void main(String[] args){
ThirdThread tt=new ThirdThread();
//先使用Lambda表示式建立Callable<Integer>物件
//使用FutureTask封裝Callable物件
FutureTask<Integer> task=new FutureTask<Integer>((Callable<Integer>)()->{
int i=0;
for(;i<100;i++){
System.out.println(Thread.currentThread().getName()+"的迴圈變數i的值:"+i);
}
return i;
});
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+"的迴圈變數i的值:"+i);
if(i==20){
new Thread(task,"有返回值的執行緒").start();
}
}
try{
System.out.println("子執行緒的返回值"+task.get());
}catch(Exception e){
e.printStackTrace();
}
}
}