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