JAVA实现网络编程之并发编程
一.并发与并行
个人理解并发就是在时间上运行程序(即不同任务在不同时间片上运行),并行就是在空间上运行程序(即不同任务在不同处理器或计算机上运行)。
二.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();