`

Java多线程基础总结九:Mina窥探

阅读更多

   一直以来的多线程的基础总结都是脱离应用的,但是要说多线程的应用就不能不说Mina。Apache Mina作为一个高性能的Java异步并发网 络通讯框架,其内部的多线程的设计和实现可谓是学习多线程的良药。手上的Mina源码是svn剪下来的最新的代码,mvn转化成eclipse项目 后导入mina-core的源码看看多线程的应用吧。
   首先简单的介绍在org.apache.mina.core.service包里的核心接口之一:IoService。这个接口是对于服务器端接收连接和客户端发起连 接这两种服务的顶层抽象,所以就有了IoAcceptor和IoConnector两个子接口的继承与隔离。很拍马屁的说,从这个小细节可以看到mina架 构的精细。这种程度的接口的隔离最重要的就是对接口内抽象行为的准确划分,错误的划分接口的职责将使得后面得实现显得不合理甚至是 错误。只是不知道负责抽象的设计人员是否是一次性的抽象成功,如果是只能说牛x。至于交互会话IoSession和实际的I/O操作处理器 IoProcessor 以及底层处理I/O事件的IoHandle这些接口就不废话了,今天看的是与IoServiceListener有关的多线程应用。 IoServiceListener主要是用来监听IoService相关的事件,而今日主角--IoServiceListenerSupport则是用来把IoService和对应的 IoServiceListener包装在一起进行管理的辅助类。先看看其源码:

public class IoServiceListenerSupport {
   /** The {@link IoService} that this instance manages. */
   private final IoService service;

   /** A list of {@link IoServiceListener}s. */
   private final List<IoServiceListener> listeners = new CopyOnWriteArrayList<IoServiceListener> ();

   /** Tracks managed sessions. */
   private final ConcurrentMap<Long, IoSession> managedSessions = new ConcurrentHashMap<Long,  IoSession>();

   /** Read only version of {@link #managedSessions}. */
   private final Map<Long, IoSession> readOnlyManagedSessions = Collections.unmodifiableMap (managedSessions);

   private final AtomicBoolean activated = new AtomicBoolean();

   /** Time this listenerSupport has been activated */
   private volatile long activationTime;

   /** A counter used to store the maximum sessions we managed since the listenerSupport has been  activated */
   private volatile int largestManagedSessionCount = 0;

   /** A global counter to count the number of sessions managed since the start */
   private volatile long cumulativeManagedSessionCount = 0;

   /**
    * Adds a new listener.
    *
    * @param listener The added listener
    */
   public void add(IoServiceListener listener) {
     if (listener != null) {
       listeners.add(listener);
     }
   }

   /**
    * @return true if the instance is active
    */
   public boolean isActive() {
     return activated.get();
   }

   /**
    * Calls {@link IoServiceListener#serviceActivated(IoService)}
    * for all registered listeners.
    */
   public void fireServiceActivated() {
     if (!activated.compareAndSet(false, true)) {
       // The instance is already active
       return;
     }

     activationTime = System.currentTimeMillis();

     // Activate all the listeners now
     for (IoServiceListener listener : listeners) {
       try {
         listener.serviceActivated(service);
       } catch (Throwable e) {
         ExceptionMonitor.getInstance().exceptionCaught(e);
       }
     }
   }

   /**
    * Calls {@link IoServiceListener#sessionCreated(IoSession)} for all registered listeners.
    *
    * @param session The session which has been created
    */
   public void fireSessionCreated(IoSession session) {
     boolean firstSession = false;

     if (session.getService() instanceof IoConnector) {
       synchronized (managedSessions) {
         firstSession = managedSessions.isEmpty();
       }
     }
   ...

     cumulativeManagedSessionCount ++;
   ...
   }
}


   这里为了说明多线程的应用,我仅仅节选相关的方法和方法片段。从这个类中我们还是能看到丰富的多线程的应用的。下面就开始这盘 菜吧!
   首先从全局变量开始看看:CopyOnWriteArrayList,ConcurrentMap,AtomicBoolean,volatile堪称多线程小动员了。后两者之前都有 介绍暂时省略,后面的方法分析时会看到使用情景。前两者都是java.util.concurrent包下的并发集合框架成员。ConcurrentMap是高效的 并发Map实现,主要是采取分段加锁的机制,默认是16段锁,所以多线程的竞争大大的降低。只有key的hash 分布在同一段位上线程之间存 在竞争。
   而CopyOnWriteArrayList是ArrayList的一个线程安全的变体,变态之处在于对其的所有的可变操作都是对底层的数组的进行的一次新的 复制。看看其实现原来是使用ReentrantLock来保证可变时的线程安全,又多了一个多线程的成员啊。当然使用这个并发集合实现是需要特 殊的情况的(要知道它的每次的可变都是全盘复制,这意味着很大的性能成本):如果遍历集合的操作次数大大的超过了可变操作时,这时 候它的性能优势就体现出来了。因为内部的数组使用的是volatile的,所以遍历查找时都不用同步而能保证可见性,这是锁同步无法比拟的 。要说mina重视性能从这儿可见一斑,毕竟每个全局变量都是为了性能去选择相应的同步机制,而不是synchronized通吃天下。
   IoServiceListenerSupport的 fireServiceActivated(),fireServiceDeactivated(),fireSessionCreated(), fireSessionDestroyed() 方法均有遍历listeners的操作,如果这些方法在线程之间频繁的使用的话,那么无疑使用CopyOnWriteArrayList 是个很好的解决方案。managedSessions主要是管理IoSession的,需要使用并发Map的数据结构,那么ConcurrentMap无疑已被证明是相对出 色的并发Map。activated类似一个开关的设计,看看为什么使用无锁得AtomicBoolean?

fireServiceActivated() {
     if (!activated.compareAndSet(false, true)) {
       // The instance is already active
       return;
     }
     ...
}

  这里可以看到activated的状态改变是要依赖其原来的值得,也就是如果使用volatile的话,要判断之前的是否为false,不满足则置为 true。这样的操作对于volatile只能使用锁同步实现。而AtomicBoolean的“CAS”轻松的使用无锁同步原语解决了这个问题。  compareAndSet(...)不管有多少个线程执行,只有取得activated最新值得线程才能返回true,其余的都会是false。这就看到mina的 committer还是很纠结性能的。
  然后看看那几个volatile的变量吧。首先是activationTime,这个变量除了有自身的get()可以随时取得最新的值之外,就在 fireServiceActivated()内出现了:activationTime = System.currentTimeMillis();可以看到仅仅是一次性的赋值而且不依赖其自身的值 。所以完全的满足线程安全的条件。 largestManagedSessionCount的使用和其类似。
  再来看看cumulativeManagedSessionCount,它是一个全局的计数器,负责记录启动后所管理得会话数量。除了同样有get()方法之外就 是出现在fireSessionCreated(...)方法中:cumulativeManagedSessionCount ++;这引起了我的注意,因为这样的写法是不能保证线程同步 的!因为volatile的变量根本无法完成“++”的原子操作。“++”是需要依赖其自身的值而进行更改的操作。为此我简单的写了个验证的例 子证明了这个铁律。
package thread;

public class VolatileTest1 {

	public static void main(String[] args) {
		final VolatileSample1 sample = new VolatileSample1();

		Runnable runnable = new Runnable() {
			public void run() {
				for (int i = 0; i < 500; i++) {
					System.out.println(sample.incrementAndGet());
				}
			}
		};

		for (int i = 0; i < 50; i++) {
			new Thread(runnable).start();
		}
	}
}

class VolatileSample1 {
	private volatile long counter = 0;

	public long incrementAndGet() {
		counter++;
		return get();
	}

	public long get() {
		return counter;
	}

}

 
  使用50个线程每个线程执行500次对counter进行叠加。正确的答案应该是25000,但是很不幸的是仅仅测试几次就出现了24999和其他的 小于25000的最大值。所以很遗憾的发现mina的这个变量的方案的选择是有问题的!
  这里应该是使用的是AtomicLong,而对应的fireServiceActivated(...)内的代码如果不使用API提供的话应该是:

for (;;) {
      long current = cumulativeManagedSessionCount.get();
      long next = current + 1;
      if (compareAndSet(current, next))
        break;
   }

虽然是for循环+CAS的atomic杀手锏,但是不要怕,一般for循环cpu都是一次搞定,极少情况是多次,性能不会有什么明显影响。这个 bug可能在数量较少的线程的情况下很难显现,或者是mina的开发者考虑使用情景故意的宽松线程机制?从严谨性来看是我个人认为是不对 的。
最后就是看看一个有趣的synchronized的代码片段:
/**
    * Close all the sessions
    * TODO disconnectSessions.
    *
    */
   private void disconnectSessions() {
     Object lock = new Object();
     ...
     try {
       synchronized (lock) {
         while (!managedSessions.isEmpty()) {
           lock.wait(500);
         }
       }
     } catch (InterruptedException ie) {
       // Ignored
     }
   }


   这个方法是关闭所有的会话,但是为了减少线程锁的竞争使用了一个很可爱的方式。这里的lock是个局部变量,所以每个进入这个方法 的线程都会拥有一个lock对象,而      synchronized(lock)是迷惑的重点。这个synchronized不会使得进入方法的线程们产生任何的竞争,因为 每个线程都能获得属于自己的lock。synchronized的作用就在于lock.wait(500)的调用,就因为wait方法必须要 synchronized的配合,所 以就出现了这个可爱的代码。这段代码的主要意图就是不管几个线程去关闭所有会话,每个线程都是间隔500ms去检查 managedSessions是 否为空。这里没有对managedSessions使用synchronized的原因就是为了减少线程锁对 managedSessions的资源独占,而改用while循环的机 制宁愿等待也不为了检查managedSessions而影响其他线程的工作。很精巧的一个实现!
   写到这里,算是这个小菜的结尾了,总感觉意犹未尽。mina对高性能并发的目标还是从代码上得到了一些的体现。当然它的高性能主要 是对NIO的封装使用,不过这一切都是建立在多线程的并发基础上的。至于之前无意发现的bug(个人认为)如果mina的committer也有相同的 认知的话,我想以后会有相应的修改的。


ref:http://www.bianceng.cn/Programming/Java/201206/34157_3.htm
分享到:
评论

相关推荐

    Apache Mina核心jar包:mina-core-2.0.7

    Apache MINA是 Apache 组织一个较新的项目,它为开发高性能和高可用性的网络应用程序提供了非常便利...当前发行的 MINA 版本支持基于 Java NIO 技术的 TCP/UDP 应用程序开发、串口通讯程序(只在最新的预览版中提供)。

    JAVA mina 框架源码

    5. **线程模型**:Mina的线程模型允许用户自定义,如单线程、多线程或者无线程模型,以适应不同的应用场景。 6. **异常处理**:Mina对网络异常进行了封装,通过IoException和IoFuture来处理网络通信中的错误和异常...

    高性能Java网络框架 MINA.7z

    5. **线程模型**:MINA提供了灵活的线程模型,可以根据系统资源和需求选择单线程、多线程或者无线程模型。 6. **强大的异常处理**:MINA提供了丰富的异常处理机制,帮助开发者处理各种可能出现的网络通信错误。 7....

    Mina入门:mina版之HelloWorld

    **Mina入门:Mina版之HelloWorld** Apache Mina是一个开源...在深入学习Mina时,可以进一步研究它的高级特性,如线程模型、缓冲区管理、心跳机制等,这些都将有助于我们在实际项目中更好地利用Mina提供的强大功能。

    mina多线程

    根据给定的信息,我们可以从标题“mina多线程”、描述“mina java多线程开发框架 NIO”以及部分给出的内容中提炼出一系列与Mina框架相关的知识点。 ### Mina框架简介 Mina(Multipurpose Infrastructure Networked...

    Java学习之IO总结及mina和netty

    这篇博客“Java学习之IO总结及mina和netty”探讨了Java IO的基础知识,并深入到两个高级网络通信框架——Mina和Netty。Mina和Netty都是基于NIO(非阻塞IO)的高性能网络应用框架,它们简化了复杂网络编程的实现。 *...

    apache-mina-2.0.4.rar_apache mina_mina

    Apache Mina是一个高性能、异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。这个"apache-mina-2.0.4.rar"压缩包包含的是Apache Mina 2.0.4版本的源代码,是深入理解和定制Mina的...

    高性能Java网络框架 MINA

    这种模型允许MINA在单个线程中处理大量连接,极大地提高了服务器的吞吐量。 2. **平台无关性**:MINA支持多种传输层协议,如TCP、UDP,以及不同类型的传输介质,如本地套接字或文件系统。这使得MINA能够跨平台运行...

    java mina框架全套

    Mina在Java NIO(非阻塞I/O)的基础上构建,支持多种传输层协议,如TCP、UDP、SSL/TLS等,并且能够处理大量的并发连接。 Mina的核心组件包括: 1. **Session**:Mina中的会话接口,代表了网络连接。每个网络连接都...

    基于Java的高性能Java网络框架 MINA.zip

    3. **分布式系统中的通信组件**:MINA可以作为分布式系统中节点间通信的基础框架。 综上所述,Apache MINA是一个强大的网络编程框架,它简化了Java网络应用的开发,提供了高性能、可扩展的解决方案,适合开发对性能...

    MINA1.7包(源码)

    4. **多线程支持**:MINA支持多线程模型,可以灵活配置工作线程数量,以适应不同场景的需求。 5. **缓冲区管理**:MINA提供了高效的数据缓冲区管理机制,能够减少数据拷贝,提高数据处理速度。 6. **编码解码器**...

    java 实现的mina server client完全能用的

    NIO允许单线程处理多个连接,提高了资源利用率。 7. **SocketTestService**:根据压缩包中的文件名,这可能是一个测试服务类,用于验证Mina服务器和客户端的正确性。它可能包含创建连接、发送和接收数据的测试逻辑...

    TestMINA.zip_DEMO_Mina框架_java mina_mina_mina java

    这个“TestMINA.zip_DEMO_Mina框架_java mina_mina_mina java”压缩包包含了使用Java实现的MINA框架的示例代码,旨在帮助开发者快速理解和应用MINA。 MINA的核心概念包括事件驱动和非阻塞I/O模型,这两个特性使得它...

    mina用法及用到的包

    8. **线程模型**:MINA提供了灵活的线程模型,如单线程、多线程,甚至自定义线程模型,以适应不同的应用场景。 9. **mina-core包**:这是MINA的核心包,包含了基本的I/O服务、过滤器、事件模型等核心组件。 10. **...

    mina源代码学习提供下载

    5. **多线程支持**:MINA支持多线程模型,可以在多个线程间分配工作,提高系统性能。 6. **强大的会话管理**:IoSession对象存储了与每个连接相关的所有信息,如会话属性、读写缓冲区、会话状态等。 7. **丰富的...

    Java mina2源码

    源码分析还能帮助我们理解Java的多线程、并发控制和事件驱动编程等高级特性,提升我们的编程技能和解决问题的能力。 此外,对于Java开发者来说,熟悉Mina2源码有助于理解其他类似的网络通信框架,比如Netty,因为...

    websocket+java服务器(mina)

    1. **创建ServerBootstrap**:这是Mina服务器的基础,用于配置服务器的线程模型、处理链等。 2. **设置ChildHandler**:配置一个IoHandler,处理客户端的连接建立、消息接收和断开连接等事件。 3. **实现WebSocket...

    java mina组合包

    Mina的核心概念是基于NIO(非阻塞I/O)模型,它允许在单个线程上处理多个连接,从而极大地提高了系统资源的利用率和整体性能。下面我们将深入探讨Java Mina的关键特性、主要组件以及如何在实际项目中使用它们。 1. ...

Global site tag (gtag.js) - Google Analytics