標籤:one dex lin block spl runnable clear 繼承 機制
1. 多線程編程
2. Thread和Runnable
java中實現多線程的方式有兩種,繼承Thread類、實現Runnable介面
2.1 Thread
開發人員可以編寫一個類繼承Thread,並重寫run方法,在run方法裡面編寫線程將要執行的代碼。
建立線程對象後,只需要調用start()方法即可讓線程進入就緒隊列,等待作業系統調度。
需要特別注意的是調度具有隨機性和隨時性。也就是說無法確定下一次調度哪個線程,也無法確定什麼時刻進行調度
public class MyThreadTest { public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start();//把myThread加入到就緒隊列裡面 }}class MyThread extends Thread { @Override public void run() { System.out.println("MyThread執行了"); }}2.2 Runnable
除了繼承Thread重寫run方法外,在簡單的情況下,還可通過實現Runnable介面的方式編寫線程執行的代碼
Thread thread = new Thread(new Runnable() { @Override public void run() { System.out.println("Runnable介面方式實現多線程"); }});3.安全執行緒問題
一個資料,如一個對象或對象中的某個欄位,如果有多個線程可以同時訪問它,就可能會出現安全執行緒問題:資料錯亂、程式出錯或其他無法預知的問題
比如線程1要遍曆一個list,線程2要把這個list清空,如果這兩個線程同時執行就可能會出現安全執行緒問題
public class ThreadQuestionTest { public static void main(String[] args) { List<Integer> list = new ArrayList<Integer>(); for (int i = 0; i < 100000; i++) { list.add(i); } Thread thread1 = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); } } }); Thread thread2 = new Thread(new Runnable() { @Override public void run() { list.clear(); } }); thread1.start(); thread2.start(); }}
輸出:
0nullnullnullnull...
4. 線程同步
線程同步控制,即使用某種方式使得一個線程在操作完某個資料前,別的線程無法操作這個資料,從而避免多個線程同時操作一個資料,進而避免安全執行緒問題
線程同步控制的方式有同步鎖機制、等待/通知機制、訊號量機制等,它們應用在不同複雜度的情境下
4.1同步代碼塊
synchronized同步鎖機制
Java中每個對象都有一把鎖,同一時刻只能有一個線程持有這把鎖。線程可以使用synchronized關鍵字向系統申請某個對象的鎖,得到鎖之後,別的線程再申請該鎖時,就只能等待。持有鎖的線程在這次操作完成後,可以釋放鎖,以便其他線程可以獲得鎖
synchronized有兩種形式,synchronized代碼塊和synchronized方法
synchronized代碼塊,又稱同步代碼塊:
public class SynchronizedBlockTest { public static void main(String[] args) { List<Integer> list = new ArrayList<Integer>(); for (int i = 0; i < 100000; i++) { list.add(i); } Thread thread1 = new Thread(new Runnable() { @Override public void run() { synchronized (list) { for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); } } } }); Thread thread2 = new Thread(new Runnable() { @Override public void run() { synchronized (list) { list.clear(); } } }); thread1.start(); thread2.start(); }}View Code4.2 同步方法
public class SynchronizedMethodTest { public static void main(String[] args) { Data data = new Data(); Thread thread1 = new Thread(new Runnable() { @Override public void run() { data.bianliList(); } }); Thread thread2 = new Thread(new Runnable() { @Override public void run() { data.clearList(); } }); thread1.start(); thread2.start(); }}class Data { private List<Integer> list; public Data() { list = new ArrayList<Integer>(); for (int i = 0; i < 100000; i++) { list.add(i); } } public synchronized void bianliList() { for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); } } public synchronized void clearList() { list.clear(); }}View Code
非靜態同步方法申請的鎖是類的當前對象的鎖,靜態同步方法申請的鎖是類的Class對象的鎖。同步方法執行完後即向系統歸還鎖
synchronized代碼塊和synchronized方法的效果一樣,可根據具體情境靈活選用
對於簡單的需要線程同步控制的應用情境,synchronized基本夠用
但需要注意,所有需要同步的線程必須都申請同一個對象的鎖,當申請不同的鎖或者有的線程沒有使用synchronized時,同步鎖機制就會失效
5. wait/notify 等待/通知機制
對於稍複雜的情況,比如多個線程需要相互合作有規律的訪問共用資料,就可以使用wait/notify機制,即等待/通知機制,也稱等待/喚醒機制
等待/通知機制建立在synchronized同步鎖機制的基礎上,即在同步代碼塊(或同步方法)內,如果當前線程執行了lockObject.wait()(lockObject表示提供鎖的對象),則當前線程立即暫停執行,並被放入阻塞隊列,並向系統歸還所持有的鎖,並在lockObject上等待,直到別的線程調用lockObject.notify()
如果有多個線程在同一個對象上等待,notify()方法只會隨機通知一個等待的線程,也可以使用notifyAll()方法通知所有等待的線程。被通知的線程獲得鎖後會進入就緒隊列
public class WaitNotifyTest { public static void main(String[] args) { Object lockObject = new Object(); Thread thread1 = new Thread(new Runnable() { @Override public void run() { synchronized (lockObject) { try { System.out.println("線程1即將開始在lockObject上等待"); lockObject.wait(); System.out.println("線程1收到通知並獲得鎖,開始繼續執行"); } catch (InterruptedException e) { e.printStackTrace(); } } } }); Thread thread2 = new Thread(new Runnable() { @Override public void run() { synchronized (lockObject) { System.out.println("線程2將隨機通知在lockObject上等待的線程"); lockObject.notify(); } } }); thread2.start(); thread1.start(); }}View Code5.1 wait/notify-生產者消費者執行個體
一個很典型的生產者消費者例子:現有一個生產者、一個消費者、10個盤子(緩衝區),生產者把生產的產品放入空盤子中,當沒有空盤子時就停止生產;消費者消費盤子中的產品,當所有的盤子都是空盤子時就停止消費
public class ProducerConsumerTest{ public static void main(String[] args) { List<Integer> buffer = new LinkedList<Integer>(); int maxSize = 10; Producer producer = new Producer(buffer, maxSize); Consumer consumer = new Consumer(buffer); producer.start(); consumer.start(); }}//類比生產者class Producer extends Thread { private List<Integer> buffer; //緩衝區,表示多個盤子 private int maxSize; //表示盤子個數 public Producer(List<Integer> buffer, int maxSize) { this.buffer = buffer; this.maxSize = maxSize; } @Override public void run() { int id = 0; while (true) { synchronized (buffer) { if (buffer.size() < maxSize) {//有空盤子則繼續生產 id++;//表示生產了一個產品 buffer.add(id); //表示把產品放入一個空盤子中 System.out.println("生產產品" + id + "並通知消費者可以消費了"); buffer.notify(); //通知消費者有產品可以消費了 } else { //如果沒有空盤子則等待 System.out.println("沒有空盤子了,生產者停止生產產品"); try { buffer.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }}//類比消費者class Consumer extends Thread { private List<Integer> buffer; //緩衝區,表示多個盤子 public Consumer(List<Integer> buffer) { this.buffer = buffer; } @Override public void run() { while (true) { synchronized (buffer) { if (buffer.size() > 0) { //有不空的盤子 int id = buffer.remove(0); //表示消費了一個產品 System.out.println("消費產品" + id + "並通知生產者有空盤了"); buffer.notify(); } else { //全部都是空盤子則等待 System.out.println("全部都是空盤子,消費者停止消費產品"); try { buffer.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }}View Code
java多線程編程