1.什么是進(jìn)程?
進(jìn)程是操作系統(tǒng)結(jié)構(gòu)的基礎(chǔ),是一次程序的執(zhí)行,是系統(tǒng)進(jìn)行資源分配和調(diào)度的一個(gè)獨(dú)立單位友驮。
這個(gè)解釋有點(diǎn)懵了漂羊。簡(jiǎn)單來(lái)講就是一個(gè)正在操作系統(tǒng)中的運(yùn)行的exe程序就是一個(gè)進(jìn)程。
2.什么是線程卸留?
線程可以理解為是在進(jìn)程中獨(dú)立運(yùn)行的子任務(wù)走越。比如:酷狗音樂(lè).exe運(yùn)行時(shí),就會(huì)有很多子任務(wù)在同時(shí)運(yùn)行,包括下載歌詞線程耻瑟,直播線程等
3.線程的優(yōu)點(diǎn)
可以在同一時(shí)間內(nèi)運(yùn)行更多不同種類的任務(wù)旨指,比如作為程序員,我們經(jīng)常邊coding,邊聽(tīng)鋼琴曲匆赃,CPU在不同任務(wù)之間飛快切換淤毛,導(dǎo)致一種錯(cuò)覺(jué)---它們是在同時(shí)運(yùn)行的。
在單任務(wù)中算柳,Task1需要花費(fèi)100s低淡,而Task2必須要等到Task1執(zhí)行完畢后才能執(zhí)行,這對(duì)Task2來(lái)說(shuō)肯定很不爽瞬项,而在多任務(wù)中蔗蹋,Task2不必等到Task1完成后再執(zhí)行,它們是異步的囱淋,因此猪杭,效率高了。
4.怎樣使用線程妥衣?
實(shí)現(xiàn)多線程編程的方式主要有2種皂吮,一種是extends Thread,一種是implements Runnable.
class MyThread extends Thread{
@Override
public void run() {
super.run();
System.out.println("MyThread");
}
}
public class Run {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
System.out.println("結(jié)束運(yùn)行");
}
}
輸出:
結(jié)束運(yùn)行
MyThread
extends Thread的最大弊端在于不支持多繼承税手,為了實(shí)現(xiàn)多繼承蜂筹,可以使用第二種方式implements Runnable.還有,從結(jié)果上來(lái)看芦倒,run方法執(zhí)行的時(shí)間較晚艺挪,也就是說(shuō),在使用多線程技術(shù)時(shí)兵扬,代碼執(zhí)行結(jié)果與代碼執(zhí)行順序是無(wú)關(guān)的麻裳。
這里有一個(gè)坑就是extends Thread的線程多次調(diào)用start()方法會(huì)報(bào)
原因在于一個(gè)Thread實(shí)例只能啟動(dòng)一個(gè)線程.
extends Thread 一般不太建議使用,因?yàn)閱卫^承原因器钟,而更推薦implements Runnable接口津坑。
我們先看一下Thread類的構(gòu)造函數(shù),
可以看到,可以傳遞一個(gè)Runnable接口傲霸,這就好辦了国瓮,我們可以通過(guò)new Thread(Runnable target).start();的方式去啟動(dòng)一個(gè)線程了。還有,Thread(Runnable target)不光可以接收一個(gè)Runnable接口對(duì)象乃摹,還可以接收Thread對(duì)象(因?yàn)門hread implements Runnbale了,多態(tài)嘛)跟衅,這樣做完全可以將一個(gè)Thread對(duì)象的run()交給其他線程去調(diào)用
再來(lái)看一下Runnable接口有什么方法孵睬?
可以看到,就有唯一的run()伶跷,翻譯過(guò)來(lái)的意思是:"當(dāng)我們implements Runnable時(shí)去創(chuàng)建 一個(gè)線程時(shí)掰读,啟動(dòng)它就會(huì)執(zhí)行run()",而通過(guò)源碼得知Thread類其實(shí)也implements Runnable,因此我們使用run().
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("MyRunnable");
}
}
public class Run {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
new Thread(myRunnable).start();
new Thread(myRunnable).start();
new Thread(myRunnable).start();
System.out.println("結(jié)束運(yùn)行");
}
}
這里同樣多次調(diào)用start()叭莫,但程序是可以正常運(yùn)行的蹈集。這是為什么呢?還是剛剛說(shuō)的那句話一個(gè)Thread實(shí)例只能啟動(dòng)一個(gè)線程雇初,這里3次new Thread拢肆,當(dāng)然不是同一個(gè)實(shí)例啦。
5.實(shí)例變量與線程安全
實(shí)例變量可以對(duì)其他線程有共享和不共享之分靖诗。
比如說(shuō)郭怪,我們創(chuàng)建了3個(gè)線程,每個(gè)線程都有各自的count變量刊橘,它們互不影響鄙才,這就是變量不共享。
共享數(shù)據(jù)的情況就是多個(gè)線程同時(shí)訪問(wèn)同一變量促绵,這時(shí)往往會(huì)出現(xiàn)臟讀現(xiàn)象攒庵。
代碼實(shí)例:
LoginServlet代碼:
public class LoginServlet {
private static String username;
private static String password;
public void dopost(String username,String password){
LoginServlet.username = username;
if(username.equals("a")){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
LoginServlet.password=password;
System.out.println("當(dāng)前線程名稱:"+Thread.currentThread().getName()+"用戶名"+LoginServlet.username+"密碼"+LoginServlet.password);
}
}
運(yùn)行代碼:
class MyThread1 extends Thread {
LoginServlet loginServlet;
MyThread1(LoginServlet loginServlet) {
this.loginServlet = loginServlet;
}
@Override
public void run() {
super.run();
loginServlet.dopost("a", "aa");
}
}
class MyThread2 extends Thread {
LoginServlet loginServlet;
MyThread2(LoginServlet loginServlet) {
this.loginServlet = loginServlet;
}
@Override
public void run() {
super.run();
loginServlet.dopost("b", "bb");
}
}
public class Run {
public static void main(String[] args) {
LoginServlet loginServlet = new LoginServlet();
MyThread1 myThread1 = new MyThread1(loginServlet);
MyThread2 myThread2 = new MyThread2(loginServlet);
myThread1.start();
myThread2.start();
}
}
這里出現(xiàn)了臟讀現(xiàn)象,可以通過(guò)加鎖機(jī)制败晴,關(guān)于synchronized加鎖浓冒,下次會(huì)詳細(xì)講解