天天看點

簡單入門java多線程一:源碼分析Thread和Runnable

簡單入門,如果要深入的話,期待之後的《多線程進階》《安全多線程》吧。這篇文章将會從源碼的角度比較清晰的展現java多線程的應用,避免一團霧水吧。

begin!

一、java實作多線程兩種方式

(1)繼承thread

public class BaseThread extends Thread{
	public BaseThread(String name) {
		super(name);
	}
	public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "  運作中---  " + i);
        }
    }
	public static void main(String[] args) {
		new BaseThread("線程007").start();
	}
}
           

(2)實作Runnable

public class BaseRunnable 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) {
		new Thread(new BaseRunnable(), "線程007").start();
	}
}
           

兩者的運作結果相同。

好,接下來讨論兩個經典的問題。

(a)調用run方法和調用start方法差別

run():本質就是調用成員方法,依然運作在主線程中,不會立即傳回(等run方法執行完畢,才會繼續執行主線程的其他方法)

start():啟動線程,并調用run()方法。(start)方法立即傳回,(run)方法則與其他線程并行運作。

看下源碼(片段):重點在注釋

public class Thread implements Runnable {
    public synchronized void start() {//運作時占用目前Thread對象的同步鎖
        
        if (threadStatus != 0 || this != me)
            throw new IllegalThreadStateException();//重複調用抛異常
        group.add(this);//将目前線程對象添加到線程組
        start0();//此時該線程由VM(虛拟機)調用
        if (stopBeforeStart) {
	    stop0(throwableFromStop);
	}
    }
    //本地方法,通過C實作線程
    private native void start0();

    //target就是Runnable的引用,是以如果繼承Thread則需要重寫run方法,因為此時target為null。
    //而實作Runnable接口的話,此時會調用target所指向的引用中的run方法。
    public void run() {
	if (target != null) {
	    target.run();
	}
    }
}
           

總之,調用run()方法并沒有啟動新線程,沒有任何意義。

有空可以自己去看看源碼,那才是了解java的最好方式。

 (b)Thread和Runnable差別,該選誰?

Thread:各花各的錢

public class MoneyThread extends Thread{
	private int money = 100;//初始錢數
	public MoneyThread(String name) {
		super(name);
	}
	public void run() {
		//每次線程,花兩次錢
		for(int i = 0;i<2;i++){
			money = money-10;
	        System.out.println(Thread.currentThread().getName() + "  花了10元錢,剩餘 " + money);
		}
    }
	public static void main(String[] args) {
		new MoneyThread("張三").start();
		new MoneyThread("李四").start();
	}
}
/*
結果:
張三 花了10元錢,剩餘 90
李四  花了10元錢,剩餘 90
張三  花了10元錢,剩餘 80
李四  花了10元錢,剩餘 80
*/
           

Runnable:有錢大家一起花

public class MoneyRunnable implements Runnable{
	private int money = 100;//初始錢數--共享資源
	@Override
	public void run() {
		//每次線程,花兩次錢
		for(int i = 0;i<2;i++){
			synchronized (this) {//當資源共享時,必須使用同步,來保證資料一緻性
				money = money-10;
				System.out.println(Thread.currentThread().getName() + "  花了10元錢,剩餘 " + money);
			}
		}
	}
	public static void main(String[] args) {
		MoneyRunnable moneyRunnable = new MoneyRunnable();
		new Thread(moneyRunnable,"張三").start();
		new Thread(moneyRunnable,"李四").start();
	}
}
/*
結果:
張三  花了10元錢,剩餘 90
張三  花了10元錢,剩餘 80
李四  花了10元錢,剩餘 70
李四  花了10元錢,剩餘 60
*/
           

當然,Runnable可以一起花,也可以分開花,隻要分别建立MoneyRunnable執行個體就行

總的來說:

1、實作Runnable可以實作資源共享

2、java是單繼承,如果繼承Thread,那就無法再繼承其他類

3、其實看Thread的源碼,它本身就是實作了Runnable接口

是以大部分時候,還是選擇通過實作Runnable接口的方式來實作多線程。

當然Thread之是以存在,必然有其意義。隻是暫時我還沒想到。(阿裡面試有問的喲)

額,突然發現一篇寫不完了,等續集吧,java多線程,入門也不易啊