Java Socket一对多通信实现之并发处理方式

 更新时间:2023年08月28日 14:12:50   作者:从北码到南  
这篇文章主要介绍了Java Socket一对多通信实现之并发处理方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

效果图

场景描述

多台传感器连接至服务端时,保存每台传感器最初的登录顺序和 socket 信息,然后根据登录顺序进行页面排序,当设备掉线或者主动断开时移除 socket 连接信息

代码设计

1. 创建一个GlobalCommonUtil的工具类

存放全局静态集合 

    //存放设备连接信息 eg: mac 登录状态 初始登录顺序等
    public static List<TcpObject> list = new LinkedList<TcpObject>();
    //存放设备初始登录顺序(累计排序)
    public static List<FileObject> fileInfo = new LinkedList<FileObject>();
    //存放 socket 连接对象,mac 为key 此处为线程安全的 map 集合
    public static Map<String,Socket> map = new ConcurrentHashMap<String, Socket>();

2.创建线程通信类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.创建线程启动类

public class TcpServer {
    public static void main(String[] args) {
        start();
    }
	 public void start() {
	        try {
	            //记录链接过的客户端个数
	            //1、创建一个服务器端Socket,即ServerSocket,绑定指定的端口,进行监听
	            ServerSocket serverSocket = new ServerSocket(9000);
	            log.info("服务器即将启动,等待客户端连接");
	            //2、循环监听等待客户端的连接
	            while(true){
	                //调用accept方法 等待客户端的连接
	                Socket socket = serverSocket.accept();
	                if(!GlobalCommonUtil.isStart) {
	                	 //创建一个新的线程
		                SocketThread serverThread = new SocketThread(socket);
		                //启动线程
		                serverThread.start();
		                GlobalCommonUtil.count++;
		                log.info("连接过的客户端数量为:" + GlobalCommonUtil.count);
	            	}
	            }
	        } catch (IOException e) {
	            e.printStackTrace();
	        }
	}
}

测试环境中,由于真实场景和真实设备均没有见过,也无法考虑设备车间是怎么回事,只能老套路。

用tcp连接工具模拟登录,登录,数据收发时均无问题,代码运行良好,开发测试都 ok ,然后交付了,再然后>跪了< 。。。

问题一:

全局静态资源的并发操作引起的线程不安全:

 for(TcpObject tcpObject : GlobalCommonUtil.list) {
 if(tcpObject.getMac().equals(mac)) {
	GlobalCommonUtil.list.remove(tcpObject);
	break;
   }
}
GlobalCommonUtil.count--;
log.info("远程客户端已关闭连接!");

但客户端异常断开时,GlobalCommonUtil.list 的 remove  其他线程的读取很容易就会 java.lang.NullPointerException,多线程之间共享变量,从而导致的线程不安全问题,如果我们让每个线程依次去读写这个变量,这样应该可以避免不安全问题了

加 synchronized 锁

分类具体分类被锁的对象伪代码
方法实例方法类的实例对象

// 实例方法 锁住的是该类的实例对象

public synchronized void method(){

  // action ...

}

静态方法类对象

// 静态方法,锁住的是类对象

public static synchronized void method(){

  // action ...

}

代码块实例对象类的实例对象

// 同步代码块,锁住的是该类的实例对象

synchronized (this){

   // action ...

}

class对象类对象

// 同步代码块,锁住的是该类的类对象

synchronized (Dermo.class){

   // action ...

}

任意实例对象Object实例对象

// 同步代码块,锁住的是配置的实例对象

//String 对象作为锁

String lock = “”

synchronized (lock){

   // action ...

}

注:

如果锁的是类的实例对象的话,每次 new 的操作都是创建一个新的对象,就出现 synchronized 锁不住对象的现象,如果锁的是类对象的话,无论new多少个实例对象,他们仍然会被锁住,即可保证线程之间的同步关系,synchronized 底层原理是使用了对象持有的监视器(monitor),但是同步代码块和同步方法的原理存在一点差异:

  • 同步代码块使用的 monitorenter 和 monitorexit 指令实现的
  • 同步方法是由方法调用指令读取运行时常量池中方法的 ACC_SYNCHRONIZRED 标识隐式实现,实际上还是调用了   monitorenter 和 monitorexit 指令     

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java实现将HTML文件与字符串转换为图片

    Java实现将HTML文件与字符串转换为图片

    在 Java 开发中,我们经常会遇到将 HTML 内容转换为图片的需求,本文小编就来和大家详细讲讲如何使用 Free Spire.Doc for Java 库来实现这一功能吧
    2025-08-08
  • Java工作流实现与实践步骤(最新)

    Java工作流实现与实践步骤(最新)

    本文详细探讨了如何使用Java实现工作流,包括基本要素、流行框架(如jbpm、Activiti、jWorkFlow)和实现步骤,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2025-09-09
  • Spring Security重写AuthenticationManager实现账号密码登录或者手机号码登录

    Spring Security重写AuthenticationManager实现账号密码登录或者手机号码登录

    本文主要介绍了Spring Security重写AuthenticationManager实现账号密码登录或者手机号码登录,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧
    2025-08-08
  • SpringBoot环境下服务端向客户端主动推送数据的几种常见方式

    SpringBoot环境下服务端向客户端主动推送数据的几种常见方式

    在传统的 HTTP 请求-响应模型中,客户端需要主动发起请求,服务端才能返回数据,然而,在某些场景下,我们希望服务端能够主动向客户端推送数据,本文将详细介绍在 Spring Boot 环境下实现服务端向客户端主动推送数据的几种常见方式,并比较它们的优缺点和适用场景
    2025-07-07
  • SpringBoot自定义maven-plugin插件整合asm代码插桩

    SpringBoot自定义maven-plugin插件整合asm代码插桩

    本文主要介绍了SpringBoot自定义maven-plugin插件整合asm代码插桩,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02
  • 使用springboot防止反编译proguard+xjar

    使用springboot防止反编译proguard+xjar

    介绍了三种代码混淆和加密工具的使用方法:ProGuard、Xjar和ClassFinal,ProGuard用于混淆Java字节码,Xjar提供对JAR包内资源的加密和动态解密,而ClassFinal则支持直接加密JAR包或WAR包,通过预研和实际操作
    2024-11-11
  • SpringBoot整合Jackson超详细用法(附Jackson工具类)

    SpringBoot整合Jackson超详细用法(附Jackson工具类)

    这篇文章主要介绍了SpringBoot整合Jackson超详细教程,本篇讲的是Jackson的详细用法,Jackson工具类在文章最后,直接复制粘贴即可使用,需要的朋友可以参考下
    2023-03-03
  • 一篇文章带你搞定JAVA泛型

    一篇文章带你搞定JAVA泛型

    泛型是Java中的高级概念,也是构建框架必备技能,比如各种集合类都是泛型实现的,今天详细聊聊Java中的泛型概念,希望有所收获
    2021-07-07
  • Java动态字节码注入技术的实现

    Java动态字节码注入技术的实现

    Java动态字节码注入技术是一种在运行时修改Java字节码的技术,本文主要介绍了Java动态字节码注入技术的实现,具有一定的参考价值,感兴趣的可以了解一下
    2023-08-08
  • Spring Boot 集成 Kafka的详细步骤

    Spring Boot 集成 Kafka的详细步骤

    Spring Boot与Kafka的集成使得消息队列的使用变得更加简单和高效,可以配置 Kafka、实现生产者和消费者,并利用 Spring Boot 提供的功能处理消息流,以下是 Spring Boot 集成 Kafka 的详细步骤,包括配置、生产者和消费者的实现以及一些高级特性,感兴趣的朋友一起看看吧
    2024-07-07

最新评论