线程并发线程安全介绍及java.util.concurrent包下类介绍

时间:2021-09-11 05:45:32

线程Thread,在Java开发中多线程是必不可少的,但是真正能用好的并不多!

首先开启一个线程三种方式

①new Thread(Runnable).start()

②thread.start();       //thread类必须继承Thread

③Executor pool = Executors.newFixedThreadPool(7);pool.execute(new Runnable() ); //利用线程池

转载:http://blog.csdn.net/king866/article/details/53945400

在多线程并发则一定会带来线程安全的问题,如何解决线程安全

线程的安全控制有三个级别

     •
JVM 级别。大多数现代处理器对并发对 某一硬件级别提供支持,通常以 compare-and-swap (CAS)指令形式。CAS
是一种低级别的、细粒度的技术,它允许多个线程更新一个内存位置,同时能够检测其他线程的冲突并进行恢复。它是许多高性能并发算法的基础。在 JDK
5.0 之前,Java 语言中用于协调线程之间的访问的惟一原语是同步,同步是更重量级和粗粒度的。公开
CAS 可以开发高度可伸缩的并发 Java 类。

• 低级实用程序类 -- 锁定和原子类。使用 CAS 作为并发原语,ReentrantLock 类提供与 synchronized
原语相同的锁定和内存语义,然而这样可以更好地控制锁定(如计时的锁定等待、锁定轮询和可中断的锁定等待)和提供更好的可伸缩性(竞争时的高性能)。大多数开发人员将不再直接使用
ReentrantLock 类,而是使用在 ReentrantLock 类上构建的高级类。

• 高级实用程序类。这些类实现并发构建块,每个计算机科学课本中都会讲述这些类 --
信号、互斥、闩锁、屏障、交换程序、线程池和线程安全集合类等。大部分开发人员都可以在应用程序中用这些类,来替换许多同步、 wait() 和
notify() 的使用,从而提高性能、可读性和正确性。

常见的线程安全操作

①加锁同步 synchronized  Lock等

②wait() notify()线程调度 已实现执行的同步

③ThreadLocal局部变量  每一个线程都有一份数据

 ④Semaphore 信号量

⑤volatile 保证一个变量的线程安全

等 等

接下来讨论集合的多线程安全

原始集合框架包含三个接口:List、Map 和 Set。这三种集合是我们平常使用最多的集合,当集合遇到多线程时,我们必须要考虑多线程的问题,

比如说一个线程1不断读取集合线程2不断往集合放入数据,这时就会出现问题

我们都知道vector,hashtable是在Java1.0就引入的集合,两个都是线程安全的,但是现在已很少使用,原因就是内部实现的线程安全太消耗资源

java.util.concurrent 是什么?

java.util.concurrent
包含许多线程安全、测试良好、高性能的并发构建块。创建 java.util.concurrent 的目的就是要实现 Collection
框架对数据结构所执行的并发操作。通过提供一组可靠的、高性能并发构建块,开发人员可以提高并发类的线程安全、可伸缩性、性能、可读性和可靠性,

java.util.concurrent 中有很多线程安全集合、线程池、信号和同步工具

要成为线程安全的类,在从多个线程访问时,它必须继续正确运行,而不管运行时环境执行那些线程的调度和交叉,且无需对部分调用代码执行任何其他同步。结果是对线程安全对象的操作将用于按固定的整体一致顺序出现所有线程。

JDK 1.2 中引入的 Collection
框架是一种表示对象集合的高度灵活的框架,它使用基本接口 List、Set 和 Map。通过 JDK
提供每个集合的多次实现(HashMap、Hashtable、TreeMap、WeakHashMap、HashSet、TreeSet、
Vector、ArrayList、LinkedList 等等)。

java.util.concurrent 包添加了多个新的线程安全集合类(ConcurrentHashMap、CopyOnWriteArrayList 和CopyOnWriteArraySet)这些类的目的是提供高性能、高度可伸缩性、线程安全的基本集合类型版本

通过同步的封装工厂(Collections.synchronizedMap()、synchronizedList() 和 synchronizedSet()),非线程安全集合均可表现为线程安全的

java.util 中的线程集合仍有一些缺点。例如,在迭代锁定时,通常需要将该锁定保留在集合中,否则,会有抛出 ConcurrentModificationException
的危险。此外,如果从多个线程频繁地访问集合,则常常不能很好地执行这些类。

JDK 5.0 还提供了两个新集合接口 -- Queue 和 BlockingQueue。Queue 接口与 List 类似,但它只允许从后面插入,从前面删除。通过消除 List 的随机

访问要求,可以创建比现有 ArrayList 和 LinkedList 实现性能更好的 Queue 实现。因为 List 的许多应用程序实际上不需要随机访问,所以Queue 通常

可以替代 List,来获得更好的性能。

ConcurrentModificationException这个问题我也遇到过,即便加上同步操作依然不能安全的并发操作,问题的根源还是在于Iterator迭代一致性的

原因

弱一致的迭代器
java.util 包中的集合类都返回 fail-fast 迭代器,这意味着它们假设线程在集合内容中进行迭代时,集合不会更改它的内容。如果 fail-fast 迭代器检测到

在迭代过程中进行了更改操作,那么它会抛出 ConcurrentModificationException,这是不可控异常。在迭代过程中不更改集合的要求通常会对许多并发
应用程序造成不便。相反,比较好的是它允许并发修改并确保迭代器只要进行合理操作,就可以提供集合的一致视图,如 java.util.concurrent 集合类中

的迭代器所做的那样。java.util.concurrent 集合返回的迭代器称为弱一致的(weakly consistent)迭代器。对于这些类,如果元素自从迭代开始已经删
除且尚未由 next() 方法返回,那么它将不返回到调用者。如果元素自迭代开始已经添加,那么它可能返回调用者,也可能不返回。在一次迭代中,无论

如何更改底层集合,元素不会被 返回两次

可以用两种方法创建线程安全支持数据的 List -- Vector 或封装 ArrayList 和 Collections.synchronizedList()。

但是java.util.concurrent 包添加了名称繁琐的 CopyOnWriteArrayList。

为什么我们想要新的线程安全的List类?为什么会出现CopyOnWriteArrayList?
简单的答案是与迭代和并发修改之间的交互有关。使用 Vector 或使用同步的 List 封装器,返回的迭代器是 fail-fast 的,
这意味着如果在迭代过程中任何其他线程修改 List,迭代可能失败。Vector 的非常普遍的应用程序是存储通过组件注册的监听器的列表。当发生适合的事件时,该组件将在监听器的列表中迭代,调用每个监听器。


为了防止 ConcurrentModificationException,迭代线程必须复制列表或锁定列表,以便进行整体迭代,而这两种情况都需要大量的性能成本。CopyOnWriteArrayList 类通过每次添加或删除元素时创建支持数组的新副本,避免了这个问题,但是进行中的迭代保持对创建迭代器时的当前副本进行操作。虽然复制也会有一些成本,但
是在许多情况下,迭代要比修改多得多,
在这些情况下,写入时复制要比其他备用方法具有更好的性能和并发性。


除了CopyOnWriteArrayList,还有CopyOnWriteArraySet、ConcurrentHashMap

ConcurrentLinkedQueue 快速、线程安全的、无阻塞 FIFO 队列

java.util.concurrent除了这些线程安全的集合还有线程相关的类还有:

①Executor 框架 是java.util.concurrent 包中包含灵活的线程池实现,但是更重要的是,它包含用于管理实现 Runnable
的任务的执行的整个框架

②Future 接口允许表示已经完成的任务、正在执行过程中的任务或者尚未开始执行的任务。通过 Future 接口,可以尝试取消尚未完成的任务,

查询任务已经完成还是取消了,以及提取(或等待)任务的结果值。

③Semaphore、CyclicBarrier、CountdownLatch 和 Exchanger 类都是同步工具的例子。每个类都有线程可以调用的方法,

方法是否被阻塞取决于正在使用的特定同步工具的状态和规则。

Executor 线程池的几种模型

1、newFixedThreadPool创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数

则将提交的任务存入到池队列中。
2、newCachedThreadPool创建一个可缓存的线程池。这种类型的线程池特点是: 
3、newSingleThreadExecutor创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,如果这个线程异常结束,会有另一个取代它

保证顺序执行(我觉得这点是它的特色)。单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的 。

4、newScheduleThreadPool创建一个定长的线程池,而且支持定时的以及周期性的任务执行,类似于Timer。