簡單入門,如果要深入的話,期待之後的《多線程進階》《安全多線程》吧。這篇文章将會從源碼的角度比較清晰的展現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多線程,入門也不易啊