學(xué)習(xí)java多線程的一下知識(shí)的總結(jié),寫在這里,防止自己遺忘,也是為了復(fù)習(xí)。
JVM的啟動(dòng)是單線程還是多線程的??
JVM啟動(dòng)的時(shí)候不僅僅啟動(dòng)了主線程,而且啟動(dòng)了垃圾回收線程,所以JVM的啟動(dòng)是多線程的。
如何實(shí)現(xiàn)多線程?
兩種方式:
- 繼承Thread類
- 自定義MyThread類繼承了Thread類
- 重寫MyThread類中的run()方法
- 創(chuàng)建MyThread對(duì)象
- 啟動(dòng)線程對(duì)象
- 實(shí)現(xiàn)Runable接口
- 自定義MyRunnable類實(shí)現(xiàn)了Runnable接口
- 重寫MyRunnable類中的run()方法
- 創(chuàng)建MyRunnable對(duì)象
- 創(chuàng)建Thread對(duì)象,把MyRunnable對(duì)象對(duì)象作為構(gòu)造函數(shù)的入?yún)?/li>
對(duì)于繼承Thread來說,是通過重寫了run
方法來實(shí)現(xiàn)的,因?yàn)橐粋€(gè)類的代碼并不是都需要多線程執(zhí)行的,只會(huì)執(zhí)行run
方法里面的代碼。
為什么有兩張方式來實(shí)現(xiàn)多線程???
- 由于Java只支持單繼承,避免單繼承導(dǎo)致的多線程的局限性
- 適合多個(gè)相同程序的代碼去處理同一個(gè)資源,吧線程相同程序的代碼,數(shù)據(jù)有效分離,體現(xiàn)了Java面向?qū)ο蟮脑O(shè)計(jì)思想。
如果重復(fù)調(diào)用run
會(huì)發(fā)現(xiàn),還是單線程執(zhí)行,原因是run
方法它是單行程的。
可以通過start
方法來執(zhí)行,如果調(diào)用兩次start
方法,并不能進(jìn)行多線程操作,由于已經(jīng)執(zhí)行了start
方法,再次執(zhí)行start
會(huì)報(bào)錯(cuò),,報(bào)錯(cuò)信息如下:
所以可以實(shí)例化兩個(gè)對(duì)象,分別啟動(dòng)對(duì)象的線程,會(huì)發(fā)現(xiàn)他們兩個(gè)確實(shí)是多線程執(zhí)行的。看到兩個(gè)for循環(huán)是同時(shí)執(zhí)行的,并沒有順序執(zhí)行。
代碼示例:
package com.aircjm.thread;
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(i);
}
}
}
package com.aircjm.thread;
public class ThreadTest {
public static void main(String[] args) {
MyThread myThread = new MyThread();
//由于run()方法時(shí)單線程的,所以還是按照單線程執(zhí)行,一個(gè)run()執(zhí)行完了,另一個(gè)run()才會(huì)執(zhí)行
//myThread.run();
//myThread.run();
//可以通過start()方法來啟動(dòng)線程
//兩者之間的區(qū)別,start是使該線程開始執(zhí)行,JVM調(diào)用該線程的run()方法
//myThread.start();
//myThread.start();
MyThread myThread1 = new MyThread();
MyThread myThread2 = new MyThread();
myThread1.start();
myThread2.start();
}
}
如何獲取繼承Thread的線程對(duì)象的名稱
public final String getName() //獲取線程的名詞
線程控制的幾個(gè)方法:
休眠線程:
public static void sleep(long millis) throws InterruptedException
在指定的毫秒數(shù)內(nèi)讓當(dāng)前正在執(zhí)行的線程休眠(暫停執(zhí)行),此操作受到系統(tǒng)計(jì)時(shí)器和調(diào)度程序精度和準(zhǔn)確性的影響。該線程不丟失任何監(jiān)視器的所屬權(quán)。
啟動(dòng)線程:
start
public void start()
使該線程開始執(zhí)行;Java 虛擬機(jī)調(diào)用該線程的 run 方法。
結(jié)果是兩個(gè)線程并發(fā)地運(yùn)行;當(dāng)前線程(從調(diào)用返回給 start 方法)和另一個(gè)線程(執(zhí)行其 run 方法)。
多次啟動(dòng)一個(gè)線程是非法的。特別是當(dāng)線程已經(jīng)結(jié)束執(zhí)行后,不能再重新啟動(dòng)。
拋出:
IllegalThreadStateException - 如果線程已經(jīng)啟動(dòng)。
另請(qǐng)參見:
run(), stop()
run
public void run()
如果該線程是使用獨(dú)立的 Runnable 運(yùn)行對(duì)象構(gòu)造的,則調(diào)用該 Runnable 對(duì)象的 run 方法;否則,該方法不執(zhí)行任何操作并返回。
Thread 的子類應(yīng)該重寫該方法。
加入線程:
禮讓線程:
守護(hù)線程:
中斷線程:
啟動(dòng)線程用的是哪個(gè)方法?
start()
run()和start()方法的區(qū)別?
- run直接調(diào)用,調(diào)用的是普通方法
- start()方法,是啟動(dòng)線程,然后JVM調(diào)用了run()方法
如何讓線程安全??
給需要線程安全的地方進(jìn)行加鎖
public class SellTicket implements Runnable {
private Integer ticketNum = 100;
public void run() {
while (true){
synchronized (ticketNum){
if (ticketNum>0){
System.out.println("now is sell "+ ticketNum-- +" ticket");
}
}
}
}
}
加鎖的方式有多種,可以給代碼塊加鎖(鎖對(duì)象是任意對(duì)象),可以給方法加鎖(鎖對(duì)象是this),也可以給靜態(tài)方法加鎖(鎖對(duì)象是字節(jié)碼文件)
學(xué)習(xí)到的線程安全的對(duì)象有:
StringBuffer stringBuffer = new StringBuffer();
Vector<String> vector = new Vector<String>();
Hashtable<String, Object> hashTable = new Hashtable<String, Object>();
// 即使線程安全但是也不推薦使用
Vector<String> vector = new Vector<String>();
Hashtable<String, Object> hashTable = new Hashtable<String, Object>();
// 如何使用線程安全的List呢??
List<String> list = Collections.synchronizedList(new ArrayList<String>()); // 線程安全
List<String> list1 = new ArrayList<String>(); //線程不安全
通過靜態(tài)方法獲取到線程安全的List集合