jdk5开始,java对并发编程增加了一些类库支持,主要是java.util.concurrent,而atomic是其包下的子包。
concurrent包
concurrent是在并发编程中很常用的实用工具类。此包包括了几个小的、已标准化的可扩展框架,以及一些提供有用功能的类,没有这些类,这些功能会很难实现或实现起来冗长乏味:
1、接口:Executor是一个简单的标准化接口,用于定义类似于线程的自定义子系统,包括线程池、异步 IO 和轻量级任务框架。根据所使用的具体Executor类的不同,可能在新创建的线程中,现有的任务执行线程中,或者调用execute()的线程中执行任务,并且可能顺序或并发执行;ExecutorService 提供了多个完整的异步任务执行框架,管理任务的排队和安排,并允许受控制的关闭。
2、实现:类ThreadPoolExecutor和 ScheduledThreadPoolExecutor提供可调的、灵活的线程池。Executors类提供大多数Executor的常见类型和配置的工厂方法,以及使用它们的几种实用工具方法。其他基于 Executor 的实用工具包括具体类 FutureTask,它提供 Future 的常见可扩展实现,以及 ExecutorCompletionService,它有助于协调对异步任务组的处理。
3、队列:ConcurrentLinkedQueue 类提供了高效的、可伸缩的、线程安全的非阻塞 FIFO 队列;concurrent 中的五个实现都支持扩展的 BlockingQueue 接口,该接口定义了 put 和 take 的阻塞版本:LinkedBlockingQueue、ArrayBlockingQueue、SynchronousQueue、PriorityBlockingQueue 和 DelayQueue。这些不同的类覆盖了生产者-使用者、消息传递、并行任务执行和相关并发设计的大多数常见使用的上下文。
4、计时:TimeUnit 类为指定和控制基于超时的操作提供了多重粒度(包括纳秒级)。该包中的大多数类除了包含不确定的等待之外,还包含基于超时的操作。在使用超时的所有情况中,超时指定了在表明已超时前该方法应该等待的最少时间。在超时发生后,实现会“尽力”检测超时。但是,在检测超时与超时之后再次实际执行线程之间可能要经过不确定的时间。
5、同步器:四个类可协助实现常见的专用同步语句。Semaphore 是一个经典的并发工具;CountDownLatch 是一个极其简单但又极其常用的实用工具,用于在保持给定数目的信号、事件或条件前阻塞执行;CyclicBarrier 是一个可重置的多路同步点,在某些并行编程风格中很有用;Exchanger 允许两个线程在集合点交换对象,它在多流水线设计中是有用的。
还有同步的集合等等。
atomic包
而atomic,顾名思义,该包下的内容是支持对单个变量进行操作的多线程安全编程。事实上,此包中的类还可以将 volatile值、字段和数组元素的概念扩展到那些也提供原子条件更新操作的类。
在jvm中,一个线程更新了共享变量i,另外一个线程立即去读取共享区中的i时,读到的可能不是刚才另外那个线程更新过的结果,这就类似数据库中的事务隔离级别中的read uncommited,volatile就是解决这个问题的。加了Volatile修饰词的变量确保了该变量在多个线程中保持value的一致性。例如:volatile int i保证了i自增长时的执行的是synchronized(){i++;}。
以AtomicInteger为例,该类有个方法成员incrementAndGet(),表示以原子方式将当前值加1后再返回加完1之后的值,如果只是“将当前值加1后再返回加完1之后的值”,则看上去与“i++”无异,但是加了“以原子方式”,从这句可以推测出,“i++”这个表达式的多线程运行是不安全的。前文《多线程(3)-同步》分析并发风险的各种现象时就已经提出了这一点(看来本人的分析果然没错!噢也也~~~撒花~~~)
原子类不是java.lang.Integer等其他相关类的通用替换方法。它们不定义诸如hashCode和compareTo之类的方法(因为原子变量是可变的,所以对于哈希表键来说,它们不是好的选择);另外,仅为那些通常在预期应用程序中使用的类型提供类。例如,没有表示 byte 的原子类。这种情况不常见,如果要这样做,可以使用AtomicInteger来保持byte值,并进行适当的强制转换。也可以使用
Float.floatToIntBits 和Float.intBitstoFloat转换来保持float值,使用Double.doubleToLongBits和Double.longBitsToDouble转换来保持 double 值。