java并发编程之工具类(代码片段)

029zz010buct 029zz010buct     2023-04-23     484

关键词:

java在线程同步和互斥方面在语言和工具方面都提供了相应的支撑,与此同时,java还提供了一系列的并发容器和原子类,来使得并发编程更容易。

一。并发容器

(一)。同步容器

同步容器指的是容器本身使用synchronized关键字来同步访问,包括我们都知道的HashTable,也包括Vector和Stack。另外,也可以通过工具类Collections.synchronizedList(List<T> list)这个方法将线程不安全的ArrayList转成线程安全的包装类,其他的set,map等等,都有类似的包装类。

通常都任务同步容器的性能较差,但不足以导致问题,会导致问题的是对同步容易的迭代遍历。在迭代遍历的时候,依然需要对同步容器本身进行加锁才能保证线程安全。

1 List list = Collections.synchronizedList(new ArrayList());
2 synchronized (list)    
3  Iterator i = list.iterator();   
4 while (i.hasNext())   
5  foo(i.next());
6 

 

(二)。并发容器

 同步容器的线程安全的保证主要是通过对所有的访问路径添加synchronized保护,往往造成性能不佳,所以在java 1.5之后,提供了更多的性能比较优秀的容器,这里称之为并发容器。

技术图片

 

 根据容器的类型,大致可以分为以上四类。

A。List

CopyOnWriteArrayList 主要体现了读写分离和最终一致的原则。即每次对CopyOnWriteArrayList 对象进行更改的话,都会新创建一个内部对象,而之前已经开始的读,还是基于之前的内部对象,之后的读在基于新的对象,会造成短暂的不一致。

如果要使用CopyOnWriteArrayList,务必保证这个业务场景中读的频率是写的频率的千百倍以上,否则效率比较差,其次就是能够接受短暂的不一致现象。

B。Map

Map 接口的两个实现是 ConcurrentHashMap 和 ConcurrentSkipListMap,它们从应用的角度来看,主要区别在于 ConcurrentHashMap 的 key 是无序的,而 ConcurrentSkipListMap 的 key 是有序的。

使用 ConcurrentHashMap 和 ConcurrentSkipListMap 需要注意的地方是,它们的 key 和 value 都不能为空,否则会抛出NullPointerException这个运行时异常。

技术图片

 

ConcurrentSkipListMap 的插入、删除、查询操作平均的时间复杂度是 O(log n),理论上和并发线程数没有关系,所以在并发度非常高的情况下,若你对 ConcurrentHashMap 的性能不满意,可以尝试一下 ConcurrentSkipListMap。

C。Set

Set 接口的两个实现是 CopyOnWriteArraySet 和 ConcurrentSkipListSet,使用场景可以参考前面讲述的 CopyOnWriteArrayList 和 ConcurrentSkipListMap。

D。Queue

 Queue 并发容器可以从以下两个维度来分类:

* 一个维度是阻塞与非阻塞,所谓阻塞指的是当队列已满时,入队操作阻塞;当队列已空时,出队操作阻塞。Java 并发包里阻塞队列都用 Blocking 关键字标识。

* 一个维度是单端与双端,单端指的是只能队尾入队,队首出队;而双端指的是队首队尾皆可入队出队。单端队列使用 Queue 标识,双端队列使用 Deque 标识。

a.单端阻塞队列,ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue、LinkedTransferQueue、PriorityBlockingQueue 和 DelayQueue。LinkedTransferQueue 融合 LinkedBlockingQueue 和 SynchronousQueue 的功能,性能比 LinkedBlockingQueue 更好;PriorityBlockingQueue 支持按照优先级出队;DelayQueue 支持延时出队。

b.双端阻塞队列,LinkedBlockingDeque。

c.单端非阻塞队列,ConcurrentLinkedQueue。

d.双端非阻塞队列,ConcurrentLinkedDeque。

使用队列时,需要格外注意队列是否支持有界(所谓有界指的是内部的队列是否有容量限制)。实际工作中,一般都不建议使用无界的队列,因为数据量大了之后很容易导致 OOM。上面提到的这些 Queue 中,只有 ArrayBlockingQueue 和 LinkedBlockingQueue 是支持有界的,所以在使用其他无界队列时,一定要充分考虑是否存在导致 OOM 的隐患。

(三)。性能比较

下图对比了都具有线程安全特性的List的三个实现的性能,可以看出CopyOnWriteArrayList在遍历操作的时候性能最好,在读操作上与其他两个相差无几,而写操作非常差。

技术图片

 

二。原子类

对于简单的原子性问题,还有一种无锁方案。Java SDK 并发包将这种无锁方案封装提炼之后,实现了一系列的原子类。无锁方案相对互斥锁方案,最大的好处就是性能。互斥锁方案为了保证互斥性,需要执行加锁、解锁操作,而加锁、解锁操作本身就消耗性能;同时拿不到锁的线程还会进入阻塞状态,进而触发线程切换,线程切换对性能的消耗也很大。 相比之下,无锁方案则完全没有加锁、解锁的性能消耗,同时还能保证互斥性,既解决了问题,又没有带来新的问题,可谓绝佳方案。

原子类性能高的秘密很简单,硬件支持而已。CPU 为了解决并发问题,提供了 CAS 指令(CAS,全称是 Compare And Swap,即“比较并交换”)。CAS 指令包含 3 个参数:共享变量的内存地址 A、用于比较的值 B 和共享变量的新值 C;并且只有当内存中地址 A 处的值等于 B 时,才能将内存中地址 A 处的值更新为新值 C。作为一条 CPU 指令,CAS 指令本身是能够保证原子性的。

技术图片

 

 Java SDK 并发包里的原子类很丰富,可以将它们分为五个类别:原子化的基本数据类型、原子化的对象引用类型、原子化数组、原子化对象属性更新器和原子化的累加器。

(一)原子化的基本数据类型,相关实现有 AtomicBoolean、AtomicInteger 和 AtomicLong。

(二)原子化的对象引用类型,相关实现有 AtomicReference、AtomicStampedReference 和 AtomicMarkableReference,利用它们可以实现对象引用的原子化更新。AtomicStampedReference 和 AtomicMarkableReference 这两个原子类可以解决 ABA 问题。

(三)原子化数组,相关实现有 AtomicIntegerArray、AtomicLongArray 和 AtomicReferenceArray,利用这些原子类,可以原子化地更新数组里面的每一个元素。

(四)原子化对象属性更新器,相关实现有 AtomicIntegerFieldUpdater、AtomicLongFieldUpdater 和 AtomicReferenceFieldUpdater,利用它们可以原子化地更新对象的属性,这三个方法都是利用反射机制实现的。

(五)原子化的累加器,DoubleAccumulator、DoubleAdder、LongAccumulator 和 LongAdder,这四个类仅仅用来执行累加操作,相比原子化的基本数据类型,速度更快,但是不支持 compareAndSet() 方法。

无锁方案相对于互斥锁方案,优点非常多,首先性能好,其次是基本不会出现死锁问题(但可能出现饥饿和活锁问题,因为自旋会反复重试)。所有原子类的方法都是针对一个共享变量的,如果你需要解决多个变量的原子性问题,建议还是使用互斥锁方案。

day846.并发工具类一些问题-java并发编程实战(代码片段)

并发工具类一些问题Hi,我是阿昌,今天学习记录的是关于并发工具类一些问题的内容。关于JavaSDK提供的并发工具类(JUC),这些工具类都是久经考验的,所以学好用好它们对于解决并发问题非常重要。在... 查看详情

『死磕java并发编程系列』并发编程工具类之countdownlatch(代码片段)

《死磕Java并发编程》系列连载中,大家可以关注一波:👍🏻『死磕Java并发编程系列』01十张图告诉你多线程那些破事『死磕Java并发编程系列』02面试官:说说什么是Java内存模型?『死磕Java并发编程系列... 查看详情

『死磕java并发编程系列』并发编程工具类之countdownlatch(代码片段)

《死磕Java并发编程》系列连载中,大家可以关注一波:👍🏻『死磕Java并发编程系列』01十张图告诉你多线程那些破事『死磕Java并发编程系列』02面试官:说说什么是Java内存模型?『死磕Java并发编程系列... 查看详情

『死磕java并发编程系列』并发编程工具类之countdownlatch(代码片段)

《死磕Java并发编程》系列连载中,大家可以关注一波:👍🏻『死磕Java并发编程系列』01十张图告诉你多线程那些破事『死磕Java并发编程系列』02面试官:说说什么是Java内存模型?『死磕Java并发编程系列... 查看详情

juc并发编程共享模式之工具线程池jdk提供的线程池工具类--executors类创建线程池(创建threadpoolexecutor对象)(代码片段)

1.Executors类创建线程池ThreadPoolExecutor类构造方法:publicThreadPoolExecutor(intcorePoolSize, intmaximumPoolSize, longkeepAliveTime, TimeUnitunit, BlockingQueue<Runnable>workQueue, ThreadFactorythreadFacto 查看详情

java并发编程学习8-同步工具类(代码片段)

同步工具类同步工具类可以是任意一个对象,只要它根据其自身的状态来协调线程的控制流。阻塞队列可以作为同步工具类,类似地还有信号量(Semaphore)、栅栏(Barrier)以及闭锁(Latch)。当然Ja... 查看详情

java并发编程学习8-同步工具类(代码片段)

同步工具类同步工具类可以是任意一个对象,只要它根据其自身的状态来协调线程的控制流。阻塞队列可以作为同步工具类,类似地还有信号量(Semaphore)、栅栏(Barrier)以及闭锁(Latch)。当然Ja... 查看详情

java并发编程学习6-同步工具类和并发容器(代码片段)

本篇开始将要介绍Java平台类库下的一些最常用的并发基础构建模块,以及使用这些模块来构造并发应用程序时的一些常用模式。同步容器类同步容器类包括Vector和Hashtable,还有由Collections.synchronizedXxx等工厂方法创建的同... 查看详情

juc并发编程共享模式之工具线程池jdk提供的线程池工具类--threadpoolexecutor(关闭线程池:shutdownshutdownnow)(代码片段)

1.关闭线程池1.1shutdown/*线程池状态变为SHUTDOWN-不会接收新任务-但已提交任务会执行完-此方法不会阻塞调用线程的执行*/voidshutdown();测试代码:packagecom.tian;importlombok.extern.slf4j.Slf4j;importjava.util.concurrent.ExecutionException;importjava.util 查看详情

juc并发编程共享模式之工具线程池jdk提供的线程池工具类--threadpoolexecutor(提交任务:submitexecuteinvokallinvokeany)(代码片段)

1.ThreadPoolExecutor提交任务//执行任务voidexecute(Runnablecommand);//提交任务task,用返回值Future获得任务执行结果<T>Future<T>submit(Callable<T>task);//提交tasks中所有任务<T>List<Future<T>>i 查看详情

day623.并发编程工具类库使用错误问题-java业务开发常见错误(代码片段)

并发编程工具类库使用错误问题多线程想必大家都知道,且JDK也为我们提供了很多并发编程的工具类库,接下来就是记录对应在业务开发中,可能会出现的并发编程工具类库使用错误的问题一、线程复用导致信息错乱... 查看详情

《java并发编程的艺术》之atomicx(代码片段)

其中主要是了解下AtomicReference以及AtomicXUpdaterAtomicReference是支持对象引用原子更新的类,仅仅是支持引用,如果要让对象内的字段支持原子更新,就一定要使用到AtomicXUpdater。字段更新类需要特别注意,字段必须是publicvolatile类型... 查看详情

juc并发编程共享模式之工具线程池jdk提供的线程池工具类--threadpoolexecutor(线程池状态构造方法)(代码片段)

1.ThreadPoolExecutor类图:1.1线程池状态ThreadPoolExecutor使用int的高3位来表示线程池状态,低29位表示线程数量从数字上比较,TERMINATED>TIDYING>STOP>SHUTDOWN>RUNNING(因为是高三位,所以第一位代表符号位(0正1负&#... 查看详情

并发编程实践之公平有界阻塞队列实现(代码片段)

简介: JUC工具包是JAVA并发编程的利器。本文讲述在没有JUC工具包帮助下,借助原生的JAVA同步原语,如何实现一个公平有界的阻塞队列。希望你也能在文后体会到并发编程的复杂之处,以及JUC工具包的强。作者|李新然... 查看详情

java并发编程要点速览(java并发容器和框架,原子操作类,并发工具类)(代码片段)

...现原理与使用ConcurrentHashMap是线程安全且高效的HashMap。在并发编程中使用HashMap可能导致程序死循环。而使用线程安全的HashTable效率又非常低下,基于以上两个原因,便有了ConcurrentHashMap的登场机会。在多线程环境下,... 查看详情

并发编程—2并发工具类(代码片段)

目录2.线程的工具类2.1fork/join框架2.2CountDownLatch一般用法2.3CycliBarrier2.4Semaphore2.5Exchange使用举例2.6CallableFutureandFutureTask2.线程的工具类2.1fork/join框架###什么是分而治之简单地说把一个大的问题,拆分成若干个子问题,每个问题相互... 查看详情

java并发编程的艺术--原子操作类和并发工具类(第七八章)(代码片段)

...子更新字段类常用类1.4.2、AtomicIntegerFieldUpdater的使用2、并发工具类2.1、CountDownLatch2.1.1、简介2.1.2、CountDownLatch的使用2.1.3、应用场景2.2、CyclicBarrier2.2.1、简介2.2.2、CyclicBarrier的使用2.2.3、应用场景2.2.4、CyclicBarrier和CountDownLatch的区... 查看详情

并发编程之原子类(代码片段)

...新一个变量的方式。??内部都是采用CAS+volatile实现了无锁并发这节不讲原理,只讲如何使用。整体结构从整体上可以分为5组,分别进行说明↘?基本类型使用原子的方式更新基本类型AtomicInteger:整形原子类AtomicLong:长整型原子类... 查看详情