Java线程通俗讲解
Java線程介紹
通常情況下,在一個服務(wù)器上面運行的程序是很多的,可能同一時間會有多個客戶
端的程序訪問服務(wù)器,服務(wù)器都要對這些客戶端做出響應(yīng)。如果我們處理器有限,
只有一個處理器的時候,如何能夠讓這些任務(wù)看起來是并行的同時執(zhí)行呢?這里就需要用到線程的知識。
當有超過一個以上執(zhí)行空間時,看起來會像是有好幾件事同時發(fā)生。但實際上,只有真正的多處理器系統(tǒng)才可以同時執(zhí)行好幾件事。使用java多線程可以讓它看起來好像同時都在執(zhí)行,也就是說,執(zhí)行的動作可以在執(zhí)行空間中快速切換,因此感覺上好像是每一個任務(wù)都在同時執(zhí)行。在Java中,我們用Thread這個類來實現(xiàn)這一點。
下面我們通過代碼一起來看一下如何新建一個線程(ThreadTest)。首先,我們需要新建一個Runnable對象。稍后我們會再定義Runnable類。而這個類會定義線程會執(zhí)行什么樣的任務(wù)。之后,我們會定義Thread對象,用它來去執(zhí)行Runnable定義好的任務(wù)。然后,啟動Thread,將Runnable對象的方法擺到新的執(zhí)行空間中。
package web_server;public class ThreadTest {public static void main(String[] args) {// 新建Runnable對象Runnable threadJob = new MyRunnable();// 將Runnable的實例傳給Thread的構(gòu)造函數(shù)Thread myThread = new Thread(threadJob);// 調(diào)用start()才會讓線程開始執(zhí)行,在此之前// 它只是Thread一個實例,并不是真的線程myThread.start();System.out.println("back in main");}}接下來我們再看如何實現(xiàn)Runnable。Runnable是一個接口,該接口只有一個方法,就是public void run()。在run中定義要執(zhí)行的方法。因為Runnable是一個接口,線程的任務(wù)是可以被定義在任何實現(xiàn)Runnable的類上。線程只在乎傳入給Thread的構(gòu)造函數(shù)的參數(shù)是否為實現(xiàn)Runnable的類。當你把Runnable傳給Thread的構(gòu)造函數(shù)時,實際上就是在給Thread取得run的辦法,這就等于你給Thread一項任務(wù)。
package web_server;public class MyRunnable implements Runnable{// Runnable接口只有一個方法,就是public void run()public void run() {go();}public void go() {doMore();}public void doMore() {System.out.println("top o' the stack");} }線程的原子性
每一個線程變成可執(zhí)行狀態(tài)之后,它就會在可執(zhí)行和不可執(zhí)行兩種狀態(tài)中來來回回的切換,有的時候也會出現(xiàn)第三種狀態(tài),就是暫時不可執(zhí)行狀態(tài)。現(xiàn)在假設(shè)你有多個線程在排隊等待執(zhí)行,這時候哪個線程先執(zhí)行,具體執(zhí)行多長時間呢?這些工作都由調(diào)度器來實現(xiàn)。線程調(diào)度器會去決定哪個線程跑起來,而哪個線程會暫時不去執(zhí)行。
但是這樣又會有一個大問題,特別是對于多線程而言,可能會發(fā)生a線程執(zhí)行一段時間,然后b線程再執(zhí)行一段時間。如果a線程和b線程的程序方法互不影響還好,如果a線程和b線程使用的是同樣的程序方法,可能會發(fā)生結(jié)果互相影響的問題。對于這樣的問題,我們可以通過Synchronized來解決。Synchronized關(guān)鍵字代表線程需要一把鑰匙來存取被同步化過程的線程。也就是說,通過synchronized同步化修飾過后的方法,會將方法中的內(nèi)容執(zhí)行完之后,再交給別的線程去執(zhí)行。如果想要保護重要的數(shù)據(jù),就把作用在數(shù)據(jù)上的方法給同步化。這部分程序,不可分割,應(yīng)該被連續(xù)的執(zhí)行。在古典物理學(xué)中,我們認為原子是不可分割的最小物理單元。因此我們說synchronized修飾過的方法具有原子性。我們下面通過一個例子一起來看一下。
package web_server;public class TestSync implements Runnable{private int balance = 0;public void run() {for(int i = 0; i < 50; i++) {increment();System.out.println("balance is " + balance);}}public void increment() { // public synchronized void increment() {int i = balance;balance = i + 1;}} package web_server;public class TestSyncTest {public static void main(String[] args) {// TODO Auto-generated method stubTestSync job = new TestSync();Thread a = new Thread(job);Thread b = new Thread(job);a.start();b.start();}}可以看到,使用synchronized之后執(zhí)行的效果看起來很正常,而不加之后有一些怪異的值。我們這里新建了兩個線程,這兩個線程執(zhí)行的都是同樣的方法。之所以不適用synchronized會出現(xiàn)數(shù)字亂的問題在于,比如說執(zhí)行了i=balance之后,a線程就切換成b線程去執(zhí)行了。還沒有來得及累加。B線程執(zhí)行一段時間之后再切換成a線程,然后a線程再繼續(xù)累加,就會出現(xiàn)數(shù)字錯亂的問題。而使用synchronized會讓其強制執(zhí)行完累加之后再去切換線程,所以不會出現(xiàn)錯亂。
最后附上 本文內(nèi)容對應(yīng)的視頻講解
參考資料
《Head First Java》
總結(jié)
以上是生活随笔為你收集整理的Java线程通俗讲解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java使用内部类的意义
- 下一篇: IDEA配置Tomcat出现404,Th