多線程使得程序中的多個任務可以同時執行
在一個程序中允許同時運行多個任務。在許多程序設計語言中,多線程都是通過調用依賴系統的過程或函數來實現的





為什么需要多線程?多個線程如何在單處理器系統中同時運行?
多線程可以使您的程序更具響應性和交互性,並提高性能。在許多情況下需要多線程,例如動畫和客戶端/服務器計算。因為大多數時候CPU處於空閑狀態 - 例如,CPU在用戶輸入數據時什么都不做 - 多個線程在單處理器系統中共享CPU時間是切實可行的。
1、創建任務和線程
一個任務類必須實現Runnable接口。任務必須從線程運行。

一旦定義了一個TaskClass,就可以用它的構造方法創建一個任務。
例子:
TaskClass task = new TaskClass();
任務必須在線程中執行。使用下面的語句創建任務的線程
Thread thread = new Thread(task);
然后調用start()方法告訴Java虛擬機該線程准備運行
thread.start()
例子:

public class TaskThreadDemo { public static void main(String [] args) { Runnable printA = new PrintChar('a', 100); Runnable printB = new PrintChar('b', 100); Runnable printNum = new PrintNum(100); Thread thread1 = new Thread(printA); Thread thread2 = new Thread(printB); Thread thread3 = new Thread(printNum); thread1.start(); thread2.start(); thread3.start(); } } class PrintChar implements Runnable{ private char charToPrint; private int times; public PrintChar(char c,int t) { charToPrint = c; times = t; } @Override public void run() { for(int i=0; i<times; i++) { System.out.print(charToPrint); } } } class PrintNum implements Runnable{ private int lastNumber; public PrintNum(int n) { lastNumber = n; } @Override public void run() { for(int i=1; i<=lastNumber; i++) { System.out.print(" " + i); } } }
2、Thread類









3、線程池
之前運用實現Runnable接口來定義一個任務類,以及如何創建一個線程來運行一個任務

該方法對大量的任務而言是不夠高效的,為每個任務開始一個新線程可能會限制吞吐量並且造成性能降低。
線程池是管理並發執行任務個數的理想方法。
Java提供Executor接口來執行線程池中的任務,提供ExecutorService接口來管理和控制任務。
Executorservice是Executor的子接口

為了創建一個Executor對象,可以使用Executor類中的靜態方法


例子:
public class TaskThreadDemo { public static void main(String [] args) { ExecutorService executor = Executors.newFixedThreadPool(3); // ExecutorService executor = Executors.newCachedThreadPool(); executor.execute(new PrintChar('a', 100)); executor.execute(new PrintChar('b', 100)); executor.execute(new PrintNum(100)); executor.shutdown(); } }
4、線程同步
例子:
public class AccountWithoutSync { private static Account account = new Account(); public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); for (int i = 0; i < 100; i++) { executorService.execute(new AddAPennyTask()); } executorService.shutdown(); //等待 全部任務 完成 while(!executorService.isTerminated()) { } System.out.println(account.getBalance()); } private static class AddAPennyTask implements Runnable{ @Override public void run() { // TODO Auto-generated method stub account.deposit(1); } } public static class Account { private int balance = 0; public int getBalance() { return balance; } public void deposit(int amount) { int newBalance = balance + amount; try { Thread.sleep(5); }catch (InterruptedException e) { // TODO: handle exception } balance = balance + newBalance; } } }

這個例子中出錯了
原因是:

因為線程不安全,所以數據遭到破壞
synchronized關鍵字
為避免競爭狀態,應該防止多個線程同時進入程序的某一特定部分,程序中的這部分稱為臨界區。
使用關鍵字synchronized來同步方法,以便一次只有一個線程可以訪問這個方法
例子:



調用一個對象上的同步實例方法,需要給該對象加鎖。而調用一個類上的同步靜態方法,需要給該類加鎖。
同步語句
當執行方法中某一個代碼塊時,同步語句不僅可用與this對象加鎖,而且可用於對任何對象加鎖。這個代碼塊稱為同步塊。同步語句的一般形式如下:

利用加鎖同步
之前的用的同步實例方法

實際上 在執行之前都隱式地需要一個加在實例上的鎖



例子:
部分代碼:
public static class Account { private int balance = 0; private Lock lock = new ReentrantLock(); //創建一個鎖 public int getBalance() { return balance; } public void deposit(int amount) { lock.lock();//獲取該鎖 try { int newBalance = balance + amount; Thread.sleep(5); balance = newBalance; }catch (InterruptedException e) { // TODO: handle exception }finally { lock.unlock();//釋放該鎖 } } }
線程間協作


例子:

public class ThreadCooperation { private static Account account = new Account(); public static void main(String [] args) { ExecutorService executorService = Executors.newFixedThreadPool(2); executorService.execute(new DepositTask()); executorService.execute(new withdrawTask()); executorService.shutdown(); System.out.println("Thread 1 \t Thread 2 \t Balance"); System.out.println(); } public static class DepositTask implements Runnable{ @Override public void run() { try { while(true) { account.deposit((int)(Math.random() * 10) + 1); Thread.sleep(1000); } } catch (InterruptedException e) { e.printStackTrace(); } } } public static class withdrawTask implements Runnable{ @Override public void run() { while(true) { account.withdraw((int)(Math.random() * 10) + 1); } } } public static class Account { private static Lock lock = new ReentrantLock(); private static Condition newDeposit = lock.newCondition(); //線程間的協作 private int balance = 0; public int getBalance() { return balance; } public void withdraw(int accountNumber) { lock.lock(); try { while(balance < accountNumber) { System.out.println("\t\tWait for a deposit, wait to withdraw :" + accountNumber); newDeposit.await(); } balance -= accountNumber; System.out.println("\t\tWithdraw: " + accountNumber + "\t\t" + "balance: " + getBalance()); }catch (Exception e) { // TODO: handle exception e.printStackTrace(); }finally { lock.unlock(); } } public void deposit(int accountNumber) { lock.lock(); try { balance += accountNumber; System.out.println("Deposit " + accountNumber + "\t\t\t\t balance: " + getBalance()); newDeposit.signalAll(); } finally { lock.unlock(); } } } }



消費者/生產者


public class ComsumerProducer { private static Buffer buffer = new Buffer(); public static void main(String [] args) { ExecutorService excurtor = Executors.newFixedThreadPool(2); excurtor.execute(new ProducerTask()); excurtor.execute(new ConsumerTask()); excurtor.shutdown(); } private static class ProducerTask implements Runnable{ @Override public void run() { // TODO Auto-generated method stub try { int i = 1; while(true) { System.out.println("Producer writes " + i); buffer.write(i++); Thread.sleep((int)(Math.random() * 1000)); } }catch (InterruptedException e) { // TODO: handle exception e.printStackTrace(); } } } private static class ConsumerTask implements Runnable{ @Override public void run() { // TODO Auto-generated method stub try { while(true) { System.out.println("\t\tConsumer reads " + buffer.read()); Thread.sleep((int)(Math.random() * 1000)); } }catch (InterruptedException e) { // TODO: handle exception e.printStackTrace(); } } } private static class Buffer{ private static final int CAPACITY = 3; private LinkedList<Integer> queue = new LinkedList<>(); private static Lock lock = new ReentrantLock(); private static Condition notFull = lock.newCondition(); private static Condition notEmpty = lock.newCondition(); public void write(int value) { lock.lock(); try{ while(queue.size() == CAPACITY) { System.out.println("Wait for notNull condition"); notFull.await(); } queue.offer(value); notEmpty.signal(); } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); } } public int read() { lock.lock(); int value = 0; try { while(queue.isEmpty()) { System.out.println("\t\tWait for notEmpty condition"); notEmpty.await(); } value = queue.pop(); notFull.signal(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { lock.unlock(); return value; } } } }

阻塞隊列

阻塞隊列在試圖向一個滿隊列添加元素或者從空隊列中刪除元素時會導致線程阻塞。BlockingQueue接口繼承了Queue,並且提供同步的put和take方法向隊列尾部添加元素,以及從隊列頭部刪除元素

java支持三個具體的阻塞隊列ArrayBlockingQueue、LinkedBlockingQueue和PriorityBlockingQueue




例子:
用阻塞隊列做的消費者/生產者
public class ConsumerProducerUsingBlockingQueue { private static ArrayBlockingQueue<Integer> buffer = new ArrayBlockingQueue<Integer>(2); public static void main(String [] args) { ExecutorService excutor = Executors.newFixedThreadPool(2); excutor.execute(new ProducerTask()); excutor.execute(new ConsumerTask()); excutor.shutdown(); } private static class ProducerTask implements Runnable{ @Override public void run() { // TODO Auto-generated method stub try { int i = 1; while(true) { System.out.println("Producer writes " + i); buffer.put(i++); Thread.sleep((int)(Math.random() * 1000)); } }catch (InterruptedException e) { // TODO: handle exception e.printStackTrace(); } } } private static class ConsumerTask implements Runnable{ @Override public void run() { // TODO Auto-generated method stub try { while(true) { System.out.println("\t\tConsumer reads " + buffer.take()); Thread.sleep((int)(Math.random() * 1000)); } }catch (InterruptedException e) { // TODO: handle exception e.printStackTrace(); } } } }

信號量



例子:


同步合集


並行編程

