【JavaEE】NetFrameWork——多线程的应用

本文对比分析了CsFrameWork与NetFrameWork的主要区别,深入探讨了CsFrameWork在多线程环境下存在的问题,如线程安全性低、效率低下及潜在的系统崩溃风险。通过具体代码示例,详细解释了如何通过加锁机制优化多线程通信,提高系统的稳定性和效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

NetFrameWork是CsFrameWork的进阶,两者之间最大的区别是原来的csFrameWork比较low,用的是长连接,运行的效率比较低,整体线程安全性低等。
其中,安全性低是因为CsFrameWork编写时没有考虑过多线程的情况,当出现多个客户端同时连接等高并发现象时,就会崩溃!
先来看一下之前的会话过程。

public abstract class Communication implements Runnable{
	private Socket socket;
	private DataInputStream dis;
	private DataOutputStream dos;
	private volatile boolean goon;

	public Communication(Socket socket) {
		try {
			this.socket = socket;
			dis = new DataInputStream(socket.getInputStream());
			dos = new DataOutputStream(socket.getOutputStream());

			goon = true;
			new Thread(this, "会话层").start();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	abstract void dealMessage(NetMessage mess);
	@Override
	public void run() {
		String mess = "";
		
		while (goon) {
			try {
				mess = dis.readUTF();
				dealMessage(new NetMessage(mess));
			} catch (IOException e) {
				if (goon == true) {
					// 对端异常掉线
					peerAbnormalDrop();
				}
				goon = false;
			}
		}
		close();
	}
}

上述的代码,当多线程同时运行的时候,会存在很大的问题。因为我们的Communication类只关心会话,所以,当客户端连接好服务器之后,会初始化Communication类,初始化的时候,会调用构造方法,但是,由于存在线程的并发运行,我们不能保证new Thread(this, “会话层”).start();这条语句执行完之后,run();方法会立刻执行,相反,有可能start之后,时间片段到了,但是dis/dos通信信道已经建立,将开始TCP/IP传输,客户端就可以通过dos.writeUTF();方法向服务器端请求一系列数据,然而,服务器端dis.readUTF();之后,就会立马进行dos.writeUTF();方法,这时,由于客户端的run()方法时间片段还未到,传递过来的消息暂时还无法到达客户端,所以资源很浪费,效率很低,严重的还可能出现系统崩溃!
针对上述的问题,我们要对多线程进行处理。

public abstract class Communication implements Runnable {
	private Socket socket;
	private DataInputStream dis;
	private DataOutputStream dos;
	private volatile boolean goon;
	private Object lock;
	
	Communication(Socket socket) {
		try {
			this.lock = new Object();
			// 这个lock对于Communication的不同实例化对象是不同的;
			this.socket = socket;
			this.dis = new DataInputStream(socket.getInputStream());
			this.dos = new DataOutputStream(socket.getOutputStream());
			this.goon = true;
			// 同步锁对象是lock,那么意味着:不同的Communication对象
			// 的lock锁是不同的,不能进行“互锁”。
			// 因此,服务器端连接的多个客户端所实例化的Communication对象
			// 及其线程,不会因为自己的lock而被迫串行下面的代码!
			// 这个“门锁”只对能识别lock的线程起作用!
			synchronized (lock) {
				new Thread(this, "会话层").start();
				try {
					lock.wait();
				} catch (InterruptedException e) {
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public abstract void peerAbnormalDrop();
	protected abstract void dealNetMessage(NetMessage message);
	
	@Override
	public void run() {
		String message = null;
		synchronized (lock) {
			lock.notify();
		}
		// 这个run()(即,线程),因为与产生它的线程,即其父线程共同持有lock
		// 对象锁,因此,上面的synchronized(lock)会对这个线程起作用!
		
		while (goon) {
			try {
				message = dis.readUTF();
				dealNetMessage(new NetMessage(message));
			} catch (IOException e) {
				if (goon == true) {
					goon = false;
					peerAbnormalDrop();
				}
			}
		}
		close();
	}
}

上述代码,通过加锁,把执行顺序进行了控制,当我们刚要开始执行new Thread(this, “会话层”).start();时,假如时间片段到了,轮到别的线程执行时,会先检查锁的状态,发现锁是锁着的,会立刻阻塞自身,进而继续执行这个锁里面的代码,只有当这个锁完全被打开时,才会唤醒刚才进行阻塞自身的线程让他们继续多线程运行。现在假如我们已经执行完了new Thread(this, “会话层”).start();并且时间片段到了,因而,run()方法也应该被执行,但是执行到synchronized时,检查锁的状态是关闭的,只能继续将自身阻塞,等待刚才的锁里面的内容继续执行。直到执行到wait()的时候,会将自身阻塞,打开lock锁,同时会唤醒run()方法这个线程到就绪态,一旦到了运行态,会立刻检查到锁被打开,然后立马给自身上锁,然后直到执行完notify方法,这时,wait()被唤醒,与run方法都进入了就绪态,这时,就开始继续多线程并发执行了。
因为,客户端只有在Communication类的构造方法完全执行完之后,才会去调用其他的与服务器端相关的操作。这样的加锁,保证了在Communication构造方法全部执行完之前,run()方法可以被执行起来。因此,这样就不会出现,服务器的消息回来了以后,dis.readUTF();方法还没法执行这一局面。这也是多线程编程的一个很好的应用!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值