实现方式 : 将他们的状态封装起来,并对每个公有方法都进行同步, 使得每次只有一个线程可以访问.
复合操作 并非线程安全. 比如 迭代, 条件运算等.
在对同步容器类的复合操作加锁时一定要以容器对象为锁对象, 保证复合操作的锁对象和容器使用的锁对象一致.才能实现一个线程安全的复合操作
public static void getLast(Vector<?> list) { // 此处的锁对象必须和 Vector 内部的锁对象一致 synchronized (list) { int lastIndex = list.size()-1; list.remove(lastIndex); } }
在容器的迭代过程中被修改(结构上被改变)时会抛出 ConcurrentModificationException
解决方法 : 在迭代过程中持有容器的锁. 并在所有对共享容器进行迭代的地方加锁
以下操作也会间接的进行容器的迭代操作
toString() , hashCode() , equals() 等很多方法都出触发容器的迭代操作.
BlockingQueue(阻塞队列接口)
LinkedBlockingQueue 类似与 LinkedList
ArrayBlockingQueue 类似与 ArrayList
PriorityBlockingQueue 按优先级排序的队列
SynchronousQueue 每个插入操作必须等待另一个线程的对应移除操作 ,反之亦然。非常适合做交换工作,生产者的线程和消费者的线程同步以传递某些信息、事件或者任务。
生产者和消费者设计也可以使用 Executor 任务执行框架来实现, 其本身也使用 生产者--消费者模式
双端队列
工作密取
处理 InterruptedException
作用 : 用来确保某些活动直到其他活动都完成后才执行.
用法一 : 创建一定数量的线程,多个线程并发的执行任务
package com.pinnet.test; import java.util.concurrent.CountDownLatch; public class CountLatchTest { public void timeTask(int threadNumbers, Runnable task) throws InterruptedException { CountDownLatch start = new CountDownLatch(1); CountDownLatch end = new CountDownLatch(threadNumbers); for (int i = 0; i < threadNumbers; i++) { new Thread() { public void run() { try { // 所有线程在起始门等待 start.await(); // 执行任务 task.run(); // 结束门递减 end.countDown(); } catch (InterruptedException e) { } } }.start(); } // 所有工作线程开始执行 start.countDown(); // 所有工作线程启动后主线程立即登待 end.await(); System.out.println("开始主线程"); } }
用法二: 创建一定数量的线程,多个线程依次的执行任务
public void timeTask2(int threadNumbers, Runnable task) throws InterruptedException { CountDownLatch start = new CountDownLatch(threadNumbers); // 任务一次执行 for (int i = 0; i < threadNumbers; i++) { new Thread() { public void run() { // 任务开始 task.run(); // 递减 start.countDown(); } }.start(); } // 主线程阻塞直到计数器为0 start.await(); System.out.println("开始主线程"); }
一种可生成结果,可异步取消的计算.
public class FutureTaskTest { // 创建任务 private final FutureTask<Integer> future = new FutureTask<>(new Callable<Integer>() { public Integer call() { return 123; } }); // 创建线程 private final Thread thread = new Thread(future); // 对外提供方法启动线程 public void start() { thread.start(); } // 获取计算结果 public Integer get() { try { return future.get(); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); return null; } } }
闭锁和栅栏的区别 :
基本使用 :
用法一 : CyclicBarrier(int number)
await() 调用 number 次后所有调用 await() 的线程继续执行 , 否则线程在await() 阻塞
public void barrier() { int number = 6; // 参数表示屏障拦截的线程数量 // barrier.await() 调用 number 次后所有线程的阻塞状态解除 CyclicBarrier barrier = new CyclicBarrier(number); for (int i = 0; i < number; i++) { new Thread(new Runnable() { @Override public void run() { System.out.println("此线程任务已经完成"); try { // 调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。 barrier.await(); System.out.println("所有线程执行完成"); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } } }).start(); } }
用法二 : CyclicBarrier(int parties, Runnable runnable) 指定数量的线程到达屏障点后 执行 runnable
public void barrier2() { int number = 6; // 参数表示屏障拦截的线程数量 // barrier.await() 调用 number 次后所有线程的阻塞状态解除 CyclicBarrier barrier = new CyclicBarrier(number, new Runnable() { @Override public void run() { // 指定数量的线程到达屏障点后执行 barrier() barrier(); } }); for (int i = 0; i < number; i++) { new Thread(new Runnable() { @Override public void run() { System.out.println("此线程任务已经完成"); try { // 调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。 barrier.await(); System.out.println("所有线程执行完成"); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } } }).start(); } }
双方形式的栅栏 Exchanger
原文:https://www.cnblogs.com/virgosnail/p/9446516.html