- 并发映射
- 概述:该映射是JDK1.5提供的用于多并发场景下的映射接口,该映射接的实现类往往是异步式线程安全的
- ConcurrentHashMap-并发哈希映射
- 该映射是一个异步式线程安全的映射
- 底层是依靠数组+链表结构来存储数据
- 默认初始容量是16,默认加载因子是0.75
- 在使用的时候可以指定容量
-
如果指定容量n,因为n介于2x和2x+1之间,所以实际容量经过运算应该是2x+1
-
如果已用桶的数量/桶的总数量>加载影子,则会扩容
-
每次扩容都是在当前的容量的基础上增加一倍
-
底层采用分桶锁机制,即当有线程访问该映射的时候,该映射会将被访问的桶加锁阻止其他线程访问该桶
- 在后续的JDK版本中,为了提高ConcurrentHashMap的效率,在分桶锁的基础上引入了读写锁
- 在JDK1.8中,为了提高效率引入了CAS无锁算法和红黑树机制:即当桶中的元素个数超过8个的时候,会将该桶中的链表扭转成一棵红黑树;当桶中的元素个数不足7个的时候,会将红黑树扭转回链表
- ConcurrentNavigableMap-并发导航映射
- 提供了人截取自映射的方法
- 常用的实现类是ConcurrentSkitMap
- 扩展:CAS算法
- 锁的代价
1. 锁是用来做并发最简单的方式,当然其代价也是最高的 2. 内核态的锁的时候需要操作系统进行一次上下文切换,加锁、释放锁会导致比较多的上下文切换和调度延时,等待锁的线程会被挂起直至锁释放 3. 在上下文切换的时候,cpu之前缓存的指令和数据都将失效,对性能有很大的损失 4. 用户态的锁虽然避免了这些问题,但是其实它们只是在没有真实的竞争时才有效 5. Java在JDK1.5之前都是靠synchronized关键字保证同步的,这种通过使用一致的锁定协议来协调对共享状态的访问,可以确保无论哪个线程持有守护变量的锁,都采用独占的方式来访问这些变量 6. 如果出现多个线程同时访问锁,那未抢到锁的线程将被挂起,当线程恢复执行时,必须等待其它线程执行完他们的时间片以后才能被调度执行 7. 线程在挂起和恢复执行过程中存在着很大的开销 8. 当一个线程正在等待锁时,它不能做任何事 9. 如果一个线程在持有锁的情况下被延迟执行,那么所有需要这个锁的线程都无法执行下 去 10. 如果被阻塞的线程优先级高,而持有锁的线程优先级低,将会导致优先级反转(Priority Inversion)。
- 独占锁
1. 独占锁是一种悲观锁,synchronized就是一种独占锁 2. 它是假设最坏的情况,并且只有在确保其它线程不会造成干扰的情况下执行,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁
- CAS
1. CAS的本意为Compare And Swap,是一种无锁算法 2. CAS需要结合CPU指令实现,现在大多数处理器架构都是支持CAS算法的 3. CAS的语义:我认为V的值应该是A,如果是,那么将V的值更新为B,否则不修改并告诉V的值实际为多少 4. CAS是一种乐观锁技术,即当多个线程尝试使用CAS同时更新同一个变量的时候,只有其中一个线程能更新变量的值,而其他线程失败。失败的线程并不会被挂起,而是会被告知在本次竞争中失败,并且可以再次尝试 5. CAS有3个参数:内存值V,旧的预期值A,要修改的值B 6. 当且仅当预期值A和内存值V相同时,将内存值修改为B,否则什么都不做
- 扩展c语言实现CAS
int compare_and_swap (int* reg, int oldval, int newval) { ATOMIC(); int old_reg_val = *reg; if (old_reg_val == oldval) *reg = newval; END_ATOMIC(); return old_reg_val; }
- 锁的代价
- 执行器服务
- 概述:
1. 本质上是一个线程池 2. 线程池定义的目的是为了减少线程的创建和销毁以减少内存的消耗和资源的占用 3. 线程池中包含了四个部分:核心线程、临时线程、工作队列、拒绝执行助手 4. 线程池在刚创建的时候没有任何的线程,每接收一个新的请求就会创建一个核心线程来处理该请求 5. 需要注意的是,在核心线程创建达到数量之前,即使有核心线程被空出,有新请求过来都会创建一个新的核心线程 6. 当所有线程都被占用之后,再来的请求会被放入工作队列 7. 工作队列本质上是一个阻塞式队列 8. 当工作队列被占用满之后,后来的请求会被交给临时线程来处理 9. 临时线程在处理完请求之后会再存活指定的时间,如果在指定时间内有请求过来则利用临时线程处理;如果没有请求,则时间倒了之后将临时线程销毁 10. 当临时线程也被占满,则再来的请求会被交给拒绝执行助手处理
- 示例
public class ExecutorServiceDemo { @Test public void test() { // 创建线程池 // corePoolSize:核心线程数量 // maximumPoolSize:最大线程数量,实际上是核心线程+临时线程 // keepAliveTime:临时线程存活时间 // unit:时间单位 // workQueue:工作队列 // handler:拒绝执行助手,该参数可以定义也可以不定义 ExecutorService es = new ThreadPoolExecutor(5, 10, 5000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(5), new RejectedExecutionHandler() { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { System.out.println("拒绝处理线程" + new Thread(r).getName()); } }); // 执行提交的任务 // 该方法只能执行Runnable线程 es.execute(new EsDemo()); // 关闭线程池 es.shutdown(); } } class EsDemo implements Runnable { @Override public void run() { System.out.println("hello~~~"); } }
- 预定义线程池
1. Executors.newCachedThreadPool() a. 只有临时线程没有核心线程 b. 临时线程的数量是Integer.MAX_VALUE,所以可以认为临时线程是无限多的 c. 每一个临时线程在用完之后能存活1分钟 d. 工作队列是一个同步队列 e. 大池子小队列 f. 适用于大量的短任务高并发的场景,不适用长任务场景 2. Executors.newCachedThreadPool(int nThreads) a. 只有核心线程没有临时线程 b. 工作队列是一个阻塞式链式队列且该队列没有指定容量,所以容量是Integer.MAX_VALUE,因此认为可以存储无限多个请求 c. 大队列小池子 d. 适用于长任务场景而不适合于短任务场景
- 概述:
-
Callable<T>
-
概述
1. JDK1.5提供的一种新的定义线程的方式 2. 泛型表示返回值的类型 3. 需要重写call方法,将要执行的逻辑放在该方法中
-
示例
public class CallableDemo { public static void main(String[] args) throws InterruptedException, ExecutionException { // 创建线程池 ExecutorService es = Executors.newCachedThreadPool(); // 创建Callable线程 CDemo c = new CDemo(); // 执行Callable线程 // 该方法既可以执行Callable线程也可以执行Runnable线程 // 该方法会将返回值封装成一个Future对象 Future<String> f = es.submit(c); // 关闭线程池 es.shutdown(); // 从Future对象中解析结果 System.out.println(f.get()); } } class CDemo implements Callable<String> { @Override public String call() throws Exception { return "SUCCESS"; } }
-
Runable和Callable的对比
Runnable Callable 返回值 无,返回值类型为void 有,类型用户指定 方法 run call 启动方式 1. 传入Thread中通过Thread启动 只能通过线程池启动 2. 通过线程池启动 容错机制 无,出现异常必须在run方法中使用try-catch进行捕获处理 有,call方法允许抛出异常,所以在出现异常之后㐓利用全局方式进行异常的处理
-
-
ScheduledExecutorService
-
概述
1. 该接口继承了ExecutorService 2. 该接口能够将提交的任务延后执行或者间隔固定的时间执行 3. 利用该接口可以实现定时执行的效果
-
示例一
public class ScheduledExecutorServiceDemo { @Test public void testSchedule() { // 创建定时执行服务对象 ScheduledExecutorService ses = Executors.newScheduledThreadPool(5); // 执行提交的任务 // 该方法可以执行Runnable也可以执行Callable线程 // task:要执行的任务 // delay:延迟的时间 // unit:时间单位 // 表示任务提交之后延迟5s再执行该任务 ses.schedule(new ScheduleThread(), 5, TimeUnit.SECONDS); // 关闭线程池 ses.shutdown(); } } class ScheduleThread implements Runnable { @Override public void run() { System.out.println("hello~"); } }
-
示例二
public class ScheduledExecutorServiceDemo { public static void main(String[] args) { ScheduledExecutorService ses = Executors.newScheduledThreadPool(5); // 执行提交的任务 // 该方法可以执行Runnable也可以执行Callable线程 // task:要执行的任务 // initialDelay:启动延时时间 // period:间隔时间 // unit:时间单位 // 该方法的特点是以上一次线程的启动时间开始计算下一次线程的启动时间 ses.scheduleAtFixedRate(new ScheduleThread(), 0, 5, TimeUnit.SECONDS); // 执行提交的任务 // 该方法可以执行Runnable也可以执行Callable线程 // task:要执行的任务 // initialDelay:启动延时时间 // delay:间隔时间 // unit:时间单位 // 该方法的特点是以上一次线程的结束时间开始计算下一次线程的启动时间 ses.scheduleWithFixedDelay(new ScheduleThread(), 0, 5, TimeUnit.SECONDS); } } class ScheduleThread implements Runnable { @Override public void run() { System.out.println("hello~"); } }
-
-
ForkJoinPool
-
概述
1. 该线程池是一个用于分叉合并的线程池 2. 分叉:将一个大的任务拆分成多个小的任务的过程 3. 合并:将所有的小的任务的结果进行汇总的过程 4. 分叉合并的目的是为了提高CPU的利用率 5. 为了避免出现慢任务而导致效率降低,该线程池采取的是work-stealing(工作窃取)策略:即当当前CPU核在执行完所有的任务之后不会空闲而是会去扫描其他的CPU核,随机的从某一个CPU核上“偷”一个任务回来执行
-
示例
public class ForkJoinPoolDemo { public static void main(String[] args) throws InterruptedException, ExecutionException { // 创建分叉合并线程池 ForkJoinPool pool = new ForkJoinPool(); // 提交任务获取执行结果 Future<Long> f = pool.submit(new Sum(1, 100000000000L)); // 关闭线程池 pool.shutdown(); // 打印结果 System.out.println(f.get()); } } // 如果分叉合并完成之后需要计算结果,则继承RecursiveTask // 如果分叉合并完成之后需要不计算结果,则继承RecursiveAction class Sum extends RecursiveTask<Long> { private static final long serialVersionUID = -2919639359420237069L; private long start; // 起始数字 private long end; // 末尾数字 private static final long THRESHOLD = 1000; // 分叉的阈值 public Sum(long start, long end) { this.start = start; this.end = end; } @Override protected Long compute() { // 判断数字范围是否在阈值范围内 if (end - start <= THRESHOLD) { long sum = 0; for (long i = start; i <= end; i++) sum += i; return sum; } else { long mid = (start + end) / 2; Sum left = new Sum(start, mid); Sum right = new Sum(mid + 1, end); // 分叉 left.fork(); right.fork(); // 合并 return left.join() + right.join(); } } }
-
-
Lock
-
概述:
1. Lock是JDK1.5提供的一个表示锁的新接口 2. 提供了用于加锁的方法lock()以及解锁的方法unlock() 3. 相对传统的synchronized而言,Lock的方式更加灵活和精细
-
公平和非公平策略
1. 公平策略:加锁前检查是否有排队等待的线程,优先排队等待的线程,先来先得 2. 非公平策略:加锁时不考虑排队等待问题,直接尝试获取锁,获取不到自动到队尾等待 3. 相对而言,非公平策略的效率更高 4. synchronized是非公评策略 5. Lock默认是非公平策略,可以手动设置为公平策略
-
读写锁
1. 读写锁分为读锁和写锁 2. 读锁:允许多个线程读取,但是不允许线程写入 写锁:允许一个线程写入,但是不允许线程读取
-
-
其他
-
CountDoenLatch
-
概述
1. 称之为闭锁或者线程递减锁 2. 要求某个任务要等其他一系列任务完成之后才能开始或者继续执行
-
示例
public class CountDownLatchDemo { public static void main(String[] args) throws Exception { // 表示对四个学生进行技计数 CountDownLatch cdl = new CountDownLatch(4); new Thread(new TeacherExam(cdl)).start(); new Thread(new StudentExam(cdl)).start(); new Thread(new StudentExam(cdl)).start(); new Thread(new StudentExam(cdl)).start(); // 使当前线程陷入阻塞 cdl.await(); System.out.println("考试开始~~~"); } } class TeacherExam implements Runnable { private CountDownLatch cdl; public TeacherExam(CountDownLatch cdl) { this.cdl = cdl; } @Override public void run() { System.out.println("监考老师达到考场~~~"); // 达到一个线程,计数减少1个 cdl.countDown(); } } class StudentExam implements Runnable { private CountDownLatch cdl; public StudentExam(CountDownLatch cdl) { this.cdl = cdl; } @Override public void run() { System.out.println("考生达到考场~~~"); cdl.countDown(); } }
-
-
CyclicBarrier
-
概述
1. 称之为栅栏 2. 要求所有线程都到达同一个点之后各个线程再继续往下执行
-
示例
public static void main(String[] args) { CyclicBarrier cb = new CyclicBarrier(4); new Thread(new Runner(cb)).start(); new Thread(new Runner(cb)).start(); new Thread(new Runner(cb)).start(); new Thread(new Runner(cb)).start(); } } class Runner implements Runnable { private CyclicBarrier cb; public Runner(CyclicBarrier cb) { this.cb = cb; } @Override public void run() { try { String name = Thread.currentThread().getName(); System.out.println(name + "到了起跑线~~~"); // 线程阻塞,每阻塞一次,减少一个计数,直到计数减为0,放开阻塞 cb.await(); System.out.println(name + "跑了出去~~~"); } catch (Exception e) { e.printStackTrace(); } } }
-
-
Exchanger<T>
-
概述
1. 用于两个线程进行信息或者数据的交换 2. 泛型表示要交换的信息类型
-
示例
public class ExchangerDemo { public static void main(String[] args) { Exchanger<String> e = new Exchanger<>(); new Thread(new Supplier(e)).start(); new Thread(new Consumer(e)).start(); } } class Supplier implements Runnable { private Exchanger<String> e; public Supplier(Exchanger<String> e) { this.e = e; } @Override public void run() { try { String info = "货物"; String message = e.exchange(info); System.out.println("生产者收到了消费者的" + message); } catch (InterruptedException e) { e.printStackTrace(); } } } class Consumer implements Runnable { private Exchanger<String> e; public Consumer(Exchanger<String> e) { this.e = e; } @Override public void run() { try { String info = "货款"; String message = e.exchange(info); System.out.println("消费者收到了生产者的" + message); } catch (InterruptedException e) { e.printStackTrace(); } } }
-
-
Semaphore
-
概述
1. 用于进行计数,表示允许一段逻辑或者对象同时最多有指定个线程可以进入使用 2. 信号量默认是非公平的
-
示例
public class SemaphoreDemo { public static void main(String[] args) { Semaphore s = new Semaphore(5); for (int i = 0; i < 8; i++) { new Thread(new BusRunner(s)).start(); } } } class BusRunner implements Runnable { private Semaphore s; public BusRunner(Semaphore s) { this.s = s; } @Override public void run() { try { String name = Thread.currentThread().getName(); // 获取了一个信号量,当信号量减为0的时候阻塞 s.acquire(); System.out.println(name + "占了一个座位"); Thread.sleep(3000); System.out.println(name + "起身空出了一个座位"); s.release(); } catch (InterruptedException e) { e.printStackTrace(); } } }
-
-
-
Atomic
-
概述
1. 在操作属性的时候保证属性的原子性,即每次只允许一个线程对该属性的值进行更改 2. 常用的实现类有:AtomicInteger、AtomicLong、AtomicBoolean以及AtomicReference
-
示例
public class AtomicIntegerDemo { static AtomicInteger ai = new AtomicInteger(0); public static void main(String[] args) throws InterruptedException { CountDownLatch cdl = new CountDownLatch(2); new Thread(new Add(cdl)).start(); new Thread(new Add(cdl)).start(); cdl.await(); System.out.println(ai); } } class Add implements Runnable { private CountDownLatch cdl; public Add(CountDownLatch cdl) { this.cdl = cdl; } @Override public void run() { for (int i = 0; i < 10000; i++) { // 表示对该属性的值自增1个单位 AtomicIntegerDemo.ai.getAndIncrement(); } cdl.countDown(); } }
-
NIO/Concurrent包/zookeeper/Avro-Concurrent(二)
最新推荐文章于 2023-04-20 12:04:42 发布