1.什么是進程?
進程是操作系統結構的基礎,是一次程序的執行,是系統進行資源分配和調度的一個獨立單位。
這個解釋有點懵了。簡單來講就是一個正在操作系統中的運行的exe程序就是一個進程。
2.什么是線程?
線程可以理解為是在進程中獨立運行的子任務。比如:酷狗音樂.exe運行時,就會有很多子任務在同時運行,包括下載歌詞線程,直播線程等
3.線程的優點
可以在同一時間內運行更多不同種類的任務,比如作為程序員,我們經常邊coding,邊聽鋼琴曲,CPU在不同任務之間飛快切換,導致一種錯覺---它們是在同時運行的。
在單任務中,Task1需要花費100s,而Task2必須要等到Task1執行完畢后才能執行,這對Task2來說肯定很不爽,而在多任務中,Task2不必等到Task1完成后再執行,它們是異步的,因此,效率高了。
4.怎樣使用線程?
實現多線程編程的方式主要有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("結束運行");
}
}
輸出:
結束運行
MyThread
extends Thread的最大弊端在于不支持多繼承,為了實現多繼承,可以使用第二種方式implements Runnable.還有,從結果上來看,run方法執行的時間較晚,也就是說,在使用多線程技術時,代碼執行結果與代碼執行順序是無關的。
這里有一個坑就是extends Thread的線程多次調用start()方法會報
原因在于一個Thread實例只能啟動一個線程.
extends Thread 一般不太建議使用,因為單繼承原因,而更推薦implements Runnable接口。
我們先看一下Thread類的構造函數,
可以看到,可以傳遞一個Runnable接口,這就好辦了,我們可以通過new Thread(Runnable target).start();的方式去啟動一個線程了。還有,Thread(Runnable target)不光可以接收一個Runnable接口對象,還可以接收Thread對象(因為Thread implements Runnbale了,多態嘛),這樣做完全可以將一個Thread對象的run()交給其他線程去調用
再來看一下Runnable接口有什么方法?
可以看到,就有唯一的run(),翻譯過來的意思是:"當我們implements Runnable時去創建 一個線程時,啟動它就會執行run()",而通過源碼得知Thread類其實也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("結束運行");
}
}
這里同樣多次調用start(),但程序是可以正常運行的。這是為什么呢?還是剛剛說的那句話一個Thread實例只能啟動一個線程,這里3次new Thread,當然不是同一個實例啦。
5.實例變量與線程安全
實例變量可以對其他線程有共享和不共享之分。
比如說,我們創建了3個線程,每個線程都有各自的count變量,它們互不影響,這就是變量不共享。
共享數據的情況就是多個線程同時訪問同一變量,這時往往會出現臟讀現象。
代碼實例:
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("當前線程名稱:"+Thread.currentThread().getName()+"用戶名"+LoginServlet.username+"密碼"+LoginServlet.password);
}
}
運行代碼:
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();
}
}
這里出現了臟讀現象,可以通過加鎖機制,關于synchronized加鎖,下次會詳細講解