一.併發與並行
個人理解併發就是在時間上執行程式(即不同任務在不同時間片上執行),並行就是在空間上執行程式(即不同任務在不同處理器或計算機上執行)。
二.Java中的Thread類
1.Thread類通過實現Runnable介面
2.執行緒池(ThreadPool)用來管理執行緒的數量
我們先用一個例子實現:
1.建立並啟動100個執行緒,每個執行緒都往同一個賬戶新增一元。
2.定義一個名為Account類模擬賬戶,一個名為AddAYuanTask的類用來向賬戶裡新增一元。
程式如下
1 import java.util.concurrent.*; 2 3 public class AccountWithoutSync { 4 private static Account account = new Account(); 5 6 public static void main(String[] args) 7 { 8 ExecutorService executor = Executors.newCachedThreadPool(); 9 10 for(int i = 0; i < 100; i++) 11 { 12 executor.execute(new AddOneYuanTask()); 13 } 14 15 executor.shutdown(); 16 17 while(!executor.isTerminated()) 18 { 19 20 } 21 22 System.out.println("What is balance? " + account.getBalance()); 23 } 24 25 //Inner class 26 private static class AddOneYuanTask implements Runnable 27 { 28 public void run() 29 { 30 account.deposit(1); 31 } 32 } 33 34 private static class Account 35 { 36 private int balance = 0; 37 38 public int getBalance() 39 { 40 return balance; 41 } 42 43 public void deposit(int amount) 44 { 45 int newBalance = balance + amount; 46 47 //人為地製造延時 48 try 49 { 50 Thread.sleep(5); 51 } 52 catch(InterruptedException ex) 53 { 54 } 55 56 balance = newBalance; 57 } 58 } 59 }
我們執行一下發現balance為4或5,這是個錯誤的結果,如果一個類的物件在多執行緒程式中導致競爭狀態,則稱這個類為執行緒不安全的。所以這個任務是執行緒不安全的。
三.用同步完善程式
用互斥鎖來實現同步,即在一任務開始執行時加鎖,執行完畢後釋放鎖。在釋放鎖之前其它任務無法執行。同步完全可以避免競爭狀態的產生,但有的時候還需要執行緒之間的相互合作。
然後增加一個向賬戶提款(Withdraw)的任務,當餘額小於取款數時,等待新存入的存款。
Account類新增
private static Lock lock = new ReentrantLock(); //建立一個鎖
private static Condition newDeposit = lock.newCondition(); //實現一個條件
Account類中應用互斥鎖的的方法如下
然後程式相應的修改修改
1 public static void main(String[] args) 2 { 3 System.out.println("Thread 1\t\tThread 2\t\tBalance"); 4 5 ExecutorService executor = Executors.newFixedThreadPool(2); 6 executor.execute(new DepositTask()); 7 executor.execute(new WithdrawTask()); 8 executor.shutdown(); 9 } 10 11 12 public static class DepositTask implements Runnable 13 { 14 public void run() 15 { 16 try 17 { 18 while(true) 19 { 20 account.deposit((int)(Math.random() * 10) + 1); 21 Thread.sleep(1000); 22 } 23 } 24 catch(InterruptedException ex) 25 { 26 ex.printStackTrace(); 27 } 28 } 29 } 30 31 public static class WithdrawTask implements Runnable 32 { 33 public void run() 34 { 35 while(true) 36 { 37 account.withdraw((int)(Math.random() * 10) + 1); 38 } 39 } 40 }
四.客戶端/伺服器的網路應用
Java中對socket的使用十分方便,在建立socket連線後就可以使用輸入輸出流的方法實現資料傳輸了。
在實現基本的GUI後,在伺服器用一個判斷條件永遠為true的迴圈來監聽客戶端的連線請求(Socket socket = serverSocket.accept();
伺服器通過建立一個內部類(HandleAClient),把客服端的socket傳遞過來執行。
1 class HandleAClient implements Runnable 2 { 3 //A connected socket 4 private Socket socket; 5 6 /**Construct a thread */ 7 public HandleAClient(Socket socket) 8 { 9 this.socket = socket; 10 } 11 12 /**Run a thread */ 13 public void run() 14 { 15 try 16 { 17 //Create data input and output streams 18 DataInputStream inputFromClient = new DataInputStream( 19 socket.getInputStream()); 20 DataOutputStream outputToClient = new DataOutputStream( 21 socket.getOutputStream()); 22 int order = 0; 23 double amount; 24 //Continuously serve the client 25 while(order != 4) 26 { 27 //Receive order, amount from the client 28 order = inputFromClient.readInt(); 29 amount = inputFromClient.readDouble(); 30 31 if(order == 1) 32 { 33 outputToClient.writeDouble(account.getBalance()); 34 } 35 else if(order == 2) 36 { 37 account.withdraw(amount); 38 outputToClient.writeDouble(account.getBalance()); 39 } 40 else if(order == 3) 41 { 42 account.deposit(amount); 43 outputToClient.writeDouble(account.getBalance()); 44 } 45 46 jta.append("Order received from client: "+ 47 order + '\n'); 48 jta.append("Balance is " + account.getBalance() + '\n'); 49 } 50 } 51 catch(IOException e) 52 { 53 System.err.println(e); 54 } 55 } 56 }
而客戶端連線上伺服器後,建立兩個IO流
//IO streams
DataOutputStream toServer = new DataOutputStream(socket.getOutputStream());
DataInputStream fromServer = new DataInputStream(socket.getInputStream());
即可傳輸order(選單選項)和amount
//Send the order, amount to the server
toServer.writeInt(order);
toServer.writeDouble(amount);
toServer.flush();
最後再讀取balance
balance = fromServer.readDouble();