java Socket简单聊天实现 —— 使用线程池及同步锁

现有的聊天室功能虽然已经完成,但是由于客户端的频繁连接与断开,会使得服务端频繁的创建及销毁线程。随着客户端的增加,服务端的线程也在增加,这无疑会对服务端的资源造成浪费,并且由于过多的线程导致的过度切换也会为服务端带来崩溃的风险。与此同时,多个线程会共享服务端的集合属性allOut,这里还存在着多线程并发的安全问题。

为此,需要重构聊天室案例,使用线程池技术来解决服务端多线程问题,并解决多线程并发的安全问题。

服务器端:需要在服务器端定义一个线程池类型的属性,用于管理服务端的线程创建及管理。修改Server的start方法,将原来创建并启动线程的代码替换为使用线程池管理的方式。然后在Server中添加三个方法,用于操作属性allOut,并使用同步锁,使三个方法变为同步的。

package TCPUDP;  
  
import java.io.BufferedReader;  
import java.io.InputStream;  
import java.io.InputStreamReader;  
import java.io.OutputStream;  
import java.io.OutputStreamWriter;  
import java.io.PrintWriter;  
import java.net.ServerSocket;  
import java.net.Socket;  
import java.util.ArrayList;  
import java.util.List;  
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
  
public class Server {  
  
    private ServerSocket serverSocket;  
    //所有客户端输出流  
    private List<PrintWriter> allOut;
    //线程池
    private ExecutorService threadPool;
      
    public Server(){  
        try{  
            serverSocket = new ServerSocket(8088);  
            allOut = new ArrayList<PrintWriter>();
            threadPool = Executors.newFixedThreadPool(40); 
        }catch(Exception e){  
            e.printStackTrace();  
        }  
    }
    
    /*
     * 将输出流存入共享集合,与下面两个方法互斥,保证同步安全
     */
    private synchronized void addOut(PrintWriter out){
    	allOut.add(out);
    }
    private synchronized void removeOut(PrintWriter out){
    	allOut.remove(out);
    }
    private synchronized void sendMessage(String message){
    	for(PrintWriter o:allOut){
    		o.println(message);
    	}
    }
    
    /* 
     * 线程体:用于并发处理不同客户端的交互 
     *  
     */  
    class ClientHandler implements Runnable{  
  
        private Socket socket;  
          
        //构造函数设置为public  
        public ClientHandler(Socket socket){  
            this.socket = socket;  
        }  
          
        @Override  
        public void run() {  
            PrintWriter pw = null;  
            try {  
                OutputStream os = socket.getOutputStream();  
                OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");  
                pw = new PrintWriter(osw, true);  
                //存入共享集合  
                //allOut.add(pw);               
                addOut(pw);
                
                InputStream is = socket.getInputStream();  
                InputStreamReader isr = new InputStreamReader(is, "UTF-8");  
                BufferedReader br = new BufferedReader(isr);  
                String message = null;
                while((message = br.readLine()) != null){  
                    //for(PrintWriter o: allOut){  
                    //    o.println(message);  
                	sendMessage(message); 
                }                
            }catch (Exception e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }finally{  
                //当客户端断线时,要将输出流从集合中删除  
                //allOut.remove(pw);
            	removeOut(pw);
                if(socket != null){  
                    try{  
                        socket.close();  
                    }catch(Exception e){  
                        e.printStackTrace();  
                    }  
                }  
            }             
        }         
    }  
      
      
    public void start(){  
        try{  
            //循环监听客户端的连接  
            while(true){  
                  
                System.out.println("等待客户端连接。。。");  
                Socket socket = serverSocket.accept();  
                System.out.println("客户端已连接!");  
                  
                ClientHandler handler = new ClientHandler(socket);  
                //启动一个线程来完成针对该客户端的交互  
                threadPool.execute(handler);  
            }  
        }catch(Exception e){  
            e.printStackTrace();  
        }  
    }  
      
    public static void main(String[] args) {  
        // TODO Auto-generated method stub  
        Server server = new Server();  
        server.start();  
    }  
  
}  

客户端

package TCPUDP;  
  
import java.io.BufferedReader;  
import java.io.InputStream;  
import java.io.InputStreamReader;  
import java.io.OutputStream;  
import java.io.OutputStreamWriter;  
import java.io.PrintWriter;  
import java.net.Socket;  
import java.util.Scanner;  
  
public class Client {  
  
    private Socket socket;  
      
    public Client(){          
            try {  
                socket = new Socket("localhost", 8088);           
            } catch (Exception e) {           
                e.printStackTrace();  
            }     
    }  
      
    private class ServerHandler implements Runnable{  
  
        @Override  
        public void run() {  
            // TODO Auto-generated method stub  
            try{  
                InputStream is = socket.getInputStream();  
                InputStreamReader isr = new InputStreamReader(is, "UTF-8");  
                BufferedReader br = new BufferedReader(isr);  
                while(true){  
                    System.out.println(br.readLine());  
                }  
            }catch(Exception e){  
                e.printStackTrace();  
            }         
        }     
    }  
      
    public void start(){  
          
        try{  
            ServerHandler handler = new ServerHandler();  
            Thread t = new Thread(handler);  
            t.setDaemon(true);  
            t.start();  
              
            OutputStream out = socket.getOutputStream();              
            OutputStreamWriter osw = new OutputStreamWriter(out, "UTF-8");  
            PrintWriter pw = new PrintWriter(osw, true);              
            //创建Scanner读取用户输入内容  
            Scanner scanner = new Scanner(System.in);  
            while(true){  
                pw.println(scanner.nextLine());  
            }  
        }catch(Exception e){  
            e.printStackTrace();  
        }finally{  
            if(socket != null){  
                try{  
                    socket.close();  
                }catch(Exception e){  
                    e.printStackTrace();  
                }  
            }  
        }                 
    }  
      
    public static void main(String[] args) {  
        // TODO Auto-generated method stub  
        Client client = new Client();  
        client.start();  
    }  
  
}  

    原文作者:java锁
    原文地址: https://blog.csdn.net/qiuyoungster/article/details/59123162
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞