JAVA實現網路程式設計之併發程式設計

snake_hand發表於2013-04-07

一.併發與並行

    個人理解併發就是在時間上執行程式(即不同任務在不同時間片上執行),並行就是在空間上執行程式(即不同任務在不同處理器或計算機上執行)。

 

二.Java中的Thread類

    1.Thread類通過實現Runnable介面

    2.執行緒池(ThreadPool)用來管理執行緒的數量

    我們先用一個例子實現:

       1.建立並啟動100個執行緒,每個執行緒都往同一個賬戶新增一元。

       2.定義一個名為Account類模擬賬戶,一個名為AddAYuanTask的類用來向賬戶裡新增一元。

       程式如下

第一版Account
 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類中應用互斥鎖的的方法如下

withdraw和deposit

   然後程式相應的修改修改

第二版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傳遞過來執行。

HandleAClient類
 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();

 

 

相關文章