欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java Socket一對多通信實現(xiàn)之并發(fā)處理方式

 更新時間:2023年08月28日 14:12:50   作者:從北碼到南  
這篇文章主要介紹了Java Socket一對多通信實現(xiàn)之并發(fā)處理方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

效果圖

場景描述

多臺傳感器連接至服務(wù)端時,保存每臺傳感器最初的登錄順序和 socket 信息,然后根據(jù)登錄順序進行頁面排序,當(dāng)設(shè)備掉線或者主動斷開時移除 socket 連接信息

代碼設(shè)計

1. 創(chuàng)建一個GlobalCommonUtil的工具類

存放全局靜態(tài)集合 

    //存放設(shè)備連接信息 eg: mac 登錄狀態(tài) 初始登錄順序等
    public static List<TcpObject> list = new LinkedList<TcpObject>();
    //存放設(shè)備初始登錄順序(累計排序)
    public static List<FileObject> fileInfo = new LinkedList<FileObject>();
    //存放 socket 連接對象,mac 為key 此處為線程安全的 map 集合
    public static Map<String,Socket> map = new ConcurrentHashMap<String, Socket>();

2.創(chuàng)建線程通信類SocketThread

初始化 ServerSocket 和 Socket 對象

public class SocketThread  extends Thread{
	ServerSocket server;
	Socket client;
    public SocketThread(Socket socket){  
        this.client = socket;  
    }  
    @Override
    public void run() {
	try {
		reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
        // 讀取reader中的信息...
	  } catch (Exception e) {
		//捕獲socket強制斷開異常信息,移除socket
    }	
 }
}

3.創(chuàng)建線程啟動類

public class TcpServer {
    public static void main(String[] args) {
        start();
    }
	 public void start() {
	        try {
	            //記錄鏈接過的客戶端個數(shù)
	            //1、創(chuàng)建一個服務(wù)器端Socket,即ServerSocket,綁定指定的端口,進行監(jiān)聽
	            ServerSocket serverSocket = new ServerSocket(9000);
	            log.info("服務(wù)器即將啟動,等待客戶端連接");
	            //2、循環(huán)監(jiān)聽等待客戶端的連接
	            while(true){
	                //調(diào)用accept方法 等待客戶端的連接
	                Socket socket = serverSocket.accept();
	                if(!GlobalCommonUtil.isStart) {
	                	 //創(chuàng)建一個新的線程
		                SocketThread serverThread = new SocketThread(socket);
		                //啟動線程
		                serverThread.start();
		                GlobalCommonUtil.count++;
		                log.info("連接過的客戶端數(shù)量為:" + GlobalCommonUtil.count);
	            	}
	            }
	        } catch (IOException e) {
	            e.printStackTrace();
	        }
	}
}

測試環(huán)境中,由于真實場景和真實設(shè)備均沒有見過,也無法考慮設(shè)備車間是怎么回事,只能老套路。

用tcp連接工具模擬登錄,登錄,數(shù)據(jù)收發(fā)時均無問題,代碼運行良好,開發(fā)測試都 ok ,然后交付了,再然后>跪了< 。。。

問題一:

全局靜態(tài)資源的并發(fā)操作引起的線程不安全:

 for(TcpObject tcpObject : GlobalCommonUtil.list) {
 if(tcpObject.getMac().equals(mac)) {
	GlobalCommonUtil.list.remove(tcpObject);
	break;
   }
}
GlobalCommonUtil.count--;
log.info("遠程客戶端已關(guān)閉連接!");

但客戶端異常斷開時,GlobalCommonUtil.list 的 remove  其他線程的讀取很容易就會 java.lang.NullPointerException,多線程之間共享變量,從而導(dǎo)致的線程不安全問題,如果我們讓每個線程依次去讀寫這個變量,這樣應(yīng)該可以避免不安全問題了

加 synchronized 鎖

分類具體分類被鎖的對象偽代碼
方法實例方法類的實例對象

// 實例方法 鎖住的是該類的實例對象

public synchronized void method(){

  // action ...

}

靜態(tài)方法類對象

// 靜態(tài)方法,鎖住的是類對象

public static synchronized void method(){

  // action ...

}

代碼塊實例對象類的實例對象

// 同步代碼塊,鎖住的是該類的實例對象

synchronized (this){

   // action ...

}

class對象類對象

// 同步代碼塊,鎖住的是該類的類對象

synchronized (Dermo.class){

   // action ...

}

任意實例對象Object實例對象

// 同步代碼塊,鎖住的是配置的實例對象

//String 對象作為鎖

String lock = “”

synchronized (lock){

   // action ...

}

注:

如果鎖的是類的實例對象的話,每次 new 的操作都是創(chuàng)建一個新的對象,就出現(xiàn) synchronized 鎖不住對象的現(xiàn)象,如果鎖的是類對象的話,無論new多少個實例對象,他們?nèi)匀粫绘i住,即可保證線程之間的同步關(guān)系,synchronized 底層原理是使用了對象持有的監(jiān)視器(monitor),但是同步代碼塊和同步方法的原理存在一點差異:

  • 同步代碼塊使用的 monitorenter 和 monitorexit 指令實現(xiàn)的
  • 同步方法是由方法調(diào)用指令讀取運行時常量池中方法的 ACC_SYNCHRONIZRED 標識隱式實現(xiàn),實際上還是調(diào)用了   monitorenter 和 monitorexit 指令     

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

最新評論