Java多執行緒1:程序與執行緒
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加鎖,下次會詳細講解