前言
程序:一組有序的指令集合
進程:執行中的程序
線程:是進程中“單一持續控制流程”
進程跟程序的區別:程序是一組指令的集合,它是靜態的實體,沒有執行的含義。而進程是一個動態的實體,有自己的生命周期。一般說來,一個進程肯定與一個程序相對應,並且只有 一個,但是一個程序可以有多個進程,或者一個進程都沒有。除此之外,進程還有並發性和交往性。簡單地說,進程是程序的一部分,程序運行的時候會產生進程。
進程與線程的區別:進程作為資源分配的單位,線程是調度和執行的單位
線程狀態
新生狀態、就緒狀態、阻塞狀態、運行狀態、死亡狀態
多線程的實現
Thread
PS:無法實現線程之間的數據共享
/** * 通過 extends Thread */ public class MyThread extends Thread{ private final String name; public MyThread(String name){ this.name = name; } @Override public void run(){ for (int i = 0; i < 5; i++) { System.out.println(this.name + ":" + i); } } }
public static void main(String[] args) { MyThread myThread = new MyThread("MyThread-0"); myThread.start(); }
Runnable
/** * 通過 implements Runnable */ public class MyRunnable implements Runnable{ private final String name; public MyRunnable(String name){ this.name = name; } @Override public void run(){ for (int i = 0; i < 5; i++) { System.out.println(this.name + ":" + i); } } }
public static void main(String[] args) { MyRunnable myRunnable = new MyRunnable("MyRunnable-0"); new Thread(myRunnable).start(); }
Callable<V>
/** * 通過 implements Callable<V> */ public class MyCallable implements Callable<String> { private final String name; public MyCallable(String name){ this.name = name; } @Override public String call() throws Exception { for (int i = 0; i < 5; i++) { System.out.println(this.name + ":" + i); } return "執行完成"; } }
public static void main(String[] args) { MyCallable myCallable = new MyCallable("MyCallable-0"); new Thread(new FutureTask<String>(myCallable)).start(); }
線程池
public static void main(String[] args) { MyThread myThread = new MyThread("MyThread-0"); MyRunnable myRunnable = new MyRunnable("MyRunnable-0"); MyCallable myCallable = new MyCallable("MyCallable-0"); //線程池 ExecutorService threadPool = Executors.newFixedThreadPool(10); threadPool.submit(myThread); threadPool.submit(myRunnable); threadPool.submit(myCallable); //關閉線程池 threadPool.shutdown(); }
並發安全
synchronized
使用synchronized關鍵字對線程加鎖,在有線程獲取該內存鎖后,其它線程無法訪問該內存,從而實現JAVA中簡單的同步、互斥操作。
同步代碼塊
synchronized(Obj){//obj叫做同步監聽器,只能是引用類型 }
同步方法
public synchronized void fun(){//同步方法中,默認使用this當前對象作為同步對象 }
Lock
從jdk1.5之后,java提供了另外一種方式來實現同步訪問,那就是Lock。
//可重入鎖 Lock lock = new ReentrantLock(); //加鎖 lock.lock(); try{ //並發處理邏輯... }catch(Exception ex){ }finally{ //釋放鎖 lock.unlock(); }
老公、老婆同時取錢例子:
/** * 銀行賬戶 */ public class Account { //余額 private int balance = 1000; public Account() { } public Account(int balance) { this.balance = balance; } public int getBalance() { return balance; } public void setBalance(int balance) { this.balance = balance; } }
使用synchronized
/** * 通過 implements Runnable */ public class MyRunnable implements Runnable{ private final String name; private final Account account; public MyRunnable(Account account,String name){ this.account = account; this.name = name; } @Override public void run(){ for (int i = 0; i < 5; i++) { //加鎖,鎖的是銀行賬戶對象 synchronized (account) { //余額大於200 if (account.getBalance() >= 200) { //模擬耗時 try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } //取錢成功 account.setBalance(account.getBalance() - 200); System.out.println(this.name + "取走了200元,當前余額為:" + account.getBalance()); } else { System.out.println(this.name + "取錢失敗,余額不足"); } } } } }
使用Lock
/** * 通過 implements Runnable */ public class MyRunnable implements Runnable{ private final String name; private final Account account; //可重入鎖 private static final Lock lock = new ReentrantLock(); public MyRunnable(Account account,String name){ this.account = account; this.name = name; } @Override public void run(){ for (int i = 0; i < 5; i++) { //加鎖 lock.lock(); try{ //余額大於200 if (account.getBalance() >= 200) { //模擬耗時 try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } //取錢成功 account.setBalance(account.getBalance() - 200); System.out.println(this.name + "取走了200元,當前余額為:" + account.getBalance()); } else { System.out.println(this.name + "取錢失敗,余額不足"); } }catch(Exception e){ e.printStackTrace(); }finally{ //釋放鎖 lock.unlock(); } } } }
測試
public static void main(String[] args) { //初始化銀行賬戶 Account account = new Account(); //老公、老婆同時取錢 new Thread(new MyRunnable(account,"老公")).start(); new Thread(new MyRunnable(account,"老婆")).start(); }
如果不加鎖,就會出現並發安全問題
死鎖
死鎖是指兩個或兩個以上的進程在執行過程中,由於競爭資源或者由於彼此通信而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱為死鎖進程
死鎖的發生必須具備以下四個必要條件。
1)互斥條件:指進程對所分配到的資源進行排它性使用,即在一段時間內某資源只由一個進程占用。如果此時還有其它進程請求資源,則請求者只能等待,直至占有資源的進程用畢釋放。
2)請求和保持條件:指進程已經保持至少一個資源,但又提出了新的資源請求,而該資源已被其它進程占有,此時請求進程阻塞,但又對自己已獲得的其它資源保持不放。
3)不剝奪條件:指進程已獲得的資源,在未使用完之前,不能被剝奪,只能在使用完時由自己釋放。
4)環路等待條件:指在發生死鎖時,必然存在一個進程——資源的環形鏈,即進程集合{P0,P1,P2,···,Pn}中的P0正在等待一個P1占用的資源;P1正在等待P2占用的資源,……,Pn正在等待已被P0占用的資源。
后記
Thread.sleep():使線程進入阻塞狀態,會釋放CPU資源但不會釋放對象鎖的控制
Object.wait():當前線程必須擁有此對象監視器。該線程釋放對此監視器的所有權(釋放synchronize鎖)並等待,直到其他線程通過調用 notify 方法,或 notifyAll 方法通知在此對象的監視器上等待的線程醒來。然后該線程將等到重新獲得對監視器的所有權后才能繼續執行。
Thread.sleep()與Object.wait()二者都可以暫停當前線程,釋放CPU控制權,主要的區別在於Object.wait()在釋放CPU同時,釋放了對象鎖的控制