背景
之所以要用到java的多线程,是因为最近写的程序需要一个这样的场景:server端监听在一个IO上,但是client端有2个IO(可以是多个,我这里是2个都需要跟server的那个IO通信,但是client两个IO并不是同时向server的那个IO发/收消息。
所以需要用到java的多线程,为client每一个IO的通信过程都建立一个线程。一行一行地读文件中的收发顺序,当IO1发/收消息的时候,IO2就等待,当IO2发/收消息的时候IO1就等待。当通信完成后就关闭该子线程。
Synchronized,wait(), notify()
synchronized是java中用于解决并发情况下数据同步访问的一个很重要的关键字。当我们想要保证一个共享资源在同一时间只会被一个线程访问时,我们就可以在代码中使用synchronized关键字对类或者对象加锁。我这里用到的是给对象加锁。
被synchronized修饰的代码,在开始执行时就会加锁,执行完成后会进行解锁。代码如下:
public class SyncThread extends Thread{
threadTOGO lock = new threadTOGO;
Thread A = new Thread(new Runnable() {
@Override
public void run() {
synchronized(lock){
try {
for(int index = 0; index < messageCount; index++) {
while(lock.IO==2)
lock.wait(); //交出锁的控制权,进入wait状态
*do something here;*
if(start to run thread B) {
lock.IO = 2;
lock.notify(); //唤醒正在wait的线程B
}
}
}catch(Exception ex) {
ex.printStackTrace();
}finally {
lock.IO = 2; //防止死锁
lock.notify();
}
}
}
});
Thread B = new Thread(new Runnable() {
@Override
public void run() {
synchronized(lock){
try {
for(int index = 0; index < messageCount; index++) {
while(lock.IO==1)
lock.wait();
*do something here;*
if(start to run thread A) {
lock.IO = 1;
lock.notify();
}
}
}catch(Exception ex) {
ex.printStackTrace();
}finally {
lock.IO = 1;
lock.notify();
}
}
}
});
}
class threadTOGO{ //锁对象
int IO=1;
}
需要注意的是:想要执行某个对象的notify(), notifyAll(),wait(), wait(long), wait(long, int)方法就必须获取该对象的锁,需要使用synchronized,不然就会抛出IllegalMonitorStateException异常。
在执行这段代码的时候,出现过一种死锁的情况:即当线程A因为某种特殊原因在还没有运行到for循环中的notify()的时候就中止了,这个时候线程B还处于wait状态没有被唤醒,而主线程也在等待着线程A和线程B都运行完了执行下一步操作。所以为了防止这种死锁的情况,我在finally中都加了个notify(),这样即使没有执行到for循环中的notify(),也会执行finally中的notify().