先來講什么是線程:
即:Thread和Runnable兩個類,可以實現線程
class Card extends Thread{
//第一步,重寫父類Thread中的run方法,這樣就可以調度線程,調度線程中啟動的方法,即run方法:
@Override
public void run(){
System.out.println("線程啟動...");
}
}
為何要重寫run方法,因為多線程方法的調用過程就是這么定義的,我們任何線程的
當我們Thread類的引用被創建以后,我們可以調用start方法來啟動該線程,很有趣的是,我們應該要思考,我們start啟動完線程以后,我們的執行順序是怎么樣的?
首先,我們知道,我們的線程體方法是run,如果我們直接調用run方法,例如
public class Test{
Thread1 t1=new Thread1();
Thread2 t2=new Thread2();
t1.run();
t2.run();
}
這種情況下,我們的執行順序,是調用方法的執行順序,即先執行Thread1中的run方法,再是Thread2中的run方法
然而,這樣的調用方式,就會把我們多線程的作用給浪費了,那么該如何是好?
所以,我們才有調用Thread.start()方法,這個方法調用的目的,就是為了將我們的線程,提交至線程棧,或者線程隊列,(待考證)
當然上面的線程體只是做了打印的操作,對於我們的多個線程的創建並運行並沒有什么多大的影響?什么,你告訴我什么是影響?一般都一下幾種影響:
1、線程競爭 顧名思義,就是在多個任務(也叫做創建多個線程(多個線程可能都是相同的線程體,也可能是不同的線程體)並啟動)同時訪問同一個線程體的同時,對執行相同的代碼,並且同時執行,例如以下代碼
static int ii=0;
public Map<Integer,Object> map=new HashMap<Integer,Object>();
@Override
public void run(){
while(ii<100){
ii++;
System.out.println("ii:" + ii + "currentThreadNo:" + Thread.currentThread().getName());
}
}
public static void main(String[] args){
for(int n=0;n<100;n++){
new Thread(new ThreadSample()).start();
}
}
現在我們要啟動100個線程,對相同的線程體進行100次的任務調用,然后,我們截取前10個輸出打印,我們將發現如下:
ii:1currentThreadNo:Thread-0
ii:2currentThreadNo:Thread-2
ii:3currentThreadNo:Thread-2
ii:4currentThreadNo:Thread-2
ii:5currentThreadNo:Thread-2
ii:8currentThreadNo:Thread-2
ii:9currentThreadNo:Thread-2
ii:10currentThreadNo:Thread-3
ii:12currentThreadNo:Thread-3
ii:13currentThreadNo:Thread-3
ii:14currentThreadNo:Thread-3
ii:7currentThreadNo:Thread-0
ii:16currentThreadNo:Thread-0
ii:17currentThreadNo:Thread-0
ii:6currentThreadNo:Thread-4
顯然,我們看到,我們的線程輸出是從1~5之后,直接跳到8,什么意思?就是說在我們線程2調度完畢,輸出為5之后,我們線程2接下去直接調度輸出就變成了8,因為6和7已經被其他線程調度到一般,值發生了改變,於是等到我們線程0都調度輸出為17之后,這個時候,我們先前的線程4,開始輸出6了,大家看,一直等到這一刻,我們的線程4才輸出6啊,這里有個問題,就是我們的線程是不安全的,同時有多個線程一起來競爭這個資源,也或者說叫做同時有多個任務一起競爭這個資源,那么競爭完后必然會造成線程體中,各線程對於資源的爭搶
所以,我們需要對我們的線程體加鎖,怎么加鎖?給方法上,加上同步synchronized,這樣,就能夠保證我們的線程是一個安全的線程,什么是安全線程,就是意味着,我們所有一起參與競爭的任務,或者說線程,不會造成一個任務或線程調用到一半的時候,另外的任務或者線程參與進來開始調度,當我們將run方法改寫為如下后,
public synchronized void run(){...}
於是,,我們在運行一下看下我們的日志輸出是什么樣的情況:
ii:1currentThreadNo:Thread-0
ii:2currentThreadNo:Thread-0
ii:3currentThreadNo:Thread-2
ii:4currentThreadNo:Thread-2
ii:5currentThreadNo:Thread-2
ii:6currentThreadNo:Thread-2
ii:7currentThreadNo:Thread-2
ii:8currentThreadNo:Thread-2
.
.
.
ii:531currentThreadNo:Thread-3
ii:532currentThreadNo:Thread-30
ii:533currentThreadNo:Thread-17
ii:534currentThreadNo:Thread-28
ii:535currentThreadNo:Thread-24
ii:536currentThreadNo:Thread-13
ii:537currentThreadNo:Thread-26
ii:538currentThreadNo:Thread-9
ii:539currentThreadNo:Thread-22
ii:540currentThreadNo:Thread-18
ii:541currentThreadNo:Thread-14
ii:542currentThreadNo:Thread-10
ii:543currentThreadNo:Thread-5
ii:544currentThreadNo:Thread-6
大吃一驚,我們最終輸出的結果居然到了544,而我們同時參與並發的線程任務才只有500個,這又是為啥呢?
原來,我們沒有對run內部調用的所有代碼(我們把所有這些代碼看成一個方法,)進行加鎖操作,所以,我們需要對方法進行如下改進:
@Override
public void run(){
synchronized(this){
while(ii<500){
ii++;
queue.add(ii);
System.out.println("ii:" + ii + "currentThreadNo:" + Thread.currentThread().getName());
}
}
}
但是,當我們運行多個任務調度的時候,發現,在一個線程參與一次迭代的時候,接下去還是會有其他線程參與競爭,這樣的話,問題就來了
為什么我們加了鎖以后,我們的線程還是不安全,還是會有其他線程參與競爭,,,,這個問題很嚴重啊
於是我們將方法修改如下 :
@Override
public void run(){
synchronized(ThreadSample.class){
while(ii<500){
ii++;
queue.add(ii);
System.out.println("ii:" + ii + "currentThreadNo:" + Thread.currentThread().getName());
}
}
}
這個時候,我們高興的發現,我們的輸出,終於是線程安全的了,就是當我們一個線程執行這個線程體的過程中,其他線程是無法參與進來的,輸出如下:
ii:1currentThreadNo:Thread-0 Tue Apr 30 15:18:50 CST 2019
ii:2currentThreadNo:Thread-0 Tue Apr 30 15:18:50 CST 2019
ii:3currentThreadNo:Thread-0 Tue Apr 30 15:18:50 CST 2019
ii:4currentThreadNo:Thread-0 Tue Apr 30 15:18:50 CST 2019
.
.
ii:497currentThreadNo:Thread-0 Tue Apr 30 15:18:50 CST 2019
ii:498currentThreadNo:Thread-0 Tue Apr 30 15:18:50 CST 2019
ii:499currentThreadNo:Thread-0 Tue Apr 30 15:18:50 CST 2019
ii:500currentThreadNo:Thread-0 Tue Apr 30 15:18:50 CST 2019
這樣,我們的線程終於安全了。
鎖:synchronized