線程問題

線程的入門

在瞭解線程以前,首先明白什麼是進程。java

什麼是進程:

進程是指運行中的應用程序,每一個進程都會有本身獨立的地址空間(內存空間)好比:瀏覽器,編譯器,系統任務管理器等等。操做系統會給該進程分配獨立的地址空間,當用戶再次點擊瀏覽器時,就又啓動一個進程。用戶每啓動一個進程,操做系統就會給該進程分配一個獨立的內存空間。web

什麼是線程:

線程能夠理解成進程的寄生,一個進程能夠由多個線程。線程本身不擁有系統資源,只擁有一點在運行中必不可少的資源,但他可與同屬一個進程的其餘線程共享所擁有的所有資源,一個線程能夠建立和撤銷另外一個線程,線程能夠併發執行。瀏覽器

線程有三種基本狀態:就緒,阻塞,運行。緩存

線程的理解:

一、線程是輕量級進程。安全

二、線程沒有獨立的地址空間服務器

三、線程由進程建立(寄生)網絡

四、一個進程能夠擁有多個線程------>多線程多線程

五、線程的生命週期:併發

  新建狀態,就緒狀態,運行狀態,阻塞狀態,死亡狀態。框架

線程的應用場景:

一、咱們熟悉的Tomcat服務器內部就是使用多線程:

上百個客戶訪問同一個web應用,Tomcat接入後都是把後續的處理扔給一個新的線程來處理,這個新的線程就是servlet程序,好比doGetdoPost

若是不採用多線程,上百個用戶同時訪問一個web應用的時候,只能使用串行處理,那樣就不實際。

二、異步處理:

異步處理也使用多線程,好比task a和task b要並行處理。可是CPU是多核的話,就可讓一個CPU執行有一個線程,若是隻有一個CPU的話,底層是按照分時複用的原則,各個線程按照時間獲取CPU資源。

三、javaweb應用中不多用到多線程,由於在開發過程當中,多線程被框架實現了,須要本身實現的不多。

四、用的比較多的時網絡應用程序。

線程的安全問題:

爲何又線程安全問題?

 

當多個線程同時共享,同一個全局變量或靜態變量,作寫的操做時,可能會發生數據衝突問題,也就是線程安全問題。可是作讀操做是不會發生數據衝突問題。

經典案例就是搶火車票的案例啦:

class ThreadTrain1 implements Runnable {

private int count = 100;

private static Object oj = new Object();

 

@Override

public void run() {

while (count > 0) {

try {

Thread.sleep(50);

} catch (Exception e) {

// TODO: handle exception

}

sale();

}

}

 

public void sale() {

// 前提 多線程進行使用、多個線程只能拿到一把鎖。

// 保證只能讓一個線程 在執行 缺點效率下降

// synchronized (oj) {

// if (count > 0) {

System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - count + 1) + "");

count--;

// }

// }

}

}

 

public class ThreadDemo {

public static void main(String[] args) {

ThreadTrain1 threadTrain1 = new ThreadTrain1();

Thread t1 = new Thread(threadTrain1, "①號窗口");

Thread t2 = new Thread(threadTrain1, "②號窗口");

t1.start();

t2.start();

}

}

運行結果:

能夠看到一號跟二號出現搶到同一張票,這顯然是不可用的

線程安全的解決辦法:

使用同步synchronized或者使用鎖(lock)

爲何使用多線程同步或者鎖就能解決線程安全問題?

答:由於多線程可能會發生數據衝突問題(線程不安全問題),只能讓當前一個線程執行,同步/鎖內的代碼執行完以後,才能讓其餘線程進來執行,一個一個執行,這樣就能夠解決線程不安全問題。多個線程共享一個資源,不會收到其餘線程的干擾。

多線程同步代碼塊案例:

private static Object oj = new Object();   

public void sale() {

// 前提 多線程進行使用、多個線程只能拿到一把鎖。

// 保證只能讓一個線程 在執行 缺點效率下降

 synchronized (oj) {

if (count > 0) {

System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - count + 1) + "票");

count--;

}

 }

}

也能夠這樣寫:

public synchronized void sale() {

if (trainCount > 0) { 

try {

Thread.sleep(40);

} catch (Exception e) {

}

System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - trainCount + 1) + "張票.");

trainCount--;

}

}

 注意:使用同步代碼塊的時候通常都是使用當前類。靜態方法時使用鎖時當前類的字節碼文件。

多線程死鎖:

 簡單理解:同步中嵌套同步,致使鎖沒法釋放。

class ThreadTrain6 implements Runnable {

// 這是貨票總票數,多個線程會同時共享資源

private int trainCount = 100;

public boolean flag = true;

private Object mutex = new Object();

 

@Override

public void run() {

if (flag) {

while (true) {

synchronized (mutex) {

// 鎖(同步代碼塊)在何時釋放? 代碼執行完, 自動釋放鎖.

// 若是flag爲true 先拿到 obj,在拿到this 鎖、 才能執行。

// 若是flag爲false先拿到this,在拿到obj鎖,才能執行。

// 死鎖解決辦法:不要在同步中嵌套同步。

sale();

}

}

} else {

while (true) {

sale();

}

}

}

public synchronized void sale() {

synchronized (mutex) {

if (trainCount > 0) {

try {

Thread.sleep(40);

} catch (Exception e) {

 

}

System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - trainCount + 1) + "張票.");

trainCount--;

}

}

}

}

 

public class DeadlockThread {

 

public static void main(String[] args) throws InterruptedException {

 

ThreadTrain6 threadTrain = new ThreadTrain6(); // 定義 一個實例

Thread thread1 = new Thread(threadTrain, "一號窗口");

Thread thread2 = new Thread(threadTrain, "二號窗口");

thread1.start();

Thread.sleep(40);

threadTrain.flag = false;

thread2.start();

}

 

}

多線程的三大特性:

一、原子性

原子性是指不可分割,必須先後一致,好比去銀行存取錢,A帳戶往B帳戶裏轉1000塊錢,A帳戶裏減去1000,B帳戶增長1000,先後不能夠有分割。

二、可見性

多個線程訪問同一個變量時,一個線程修改了該變量的值,其餘線程可以當即看到修改的值

三、有序性

 程序執行的順序按照代碼的前後順序執行。

Java內存模型:

共享內存模型指的就是Java內存模型(簡稱JMM)JMM決定一個線程對共享變量的寫入時,對另外一個線程可見。從抽象的角度來看,JMM定義了線程和主內存之間的抽象關係:線程之間的共享變量存儲在主內存(main memory)中,每一個線程都有一個私有的本地內存(local memory),本地內存中存儲了該線程以讀/寫共享變量的副本。本地內存是JMM的一個抽象概念,並不真實存在。它涵蓋了緩存,寫緩衝區,寄存器以及其餘的硬件和編譯器優化。

相關文章
相關標籤/搜索