原文出处:https://www.cnblogs.com/linkworld/p/7819270.html

1. JUC 简介

  • 在 Java 5.0 提供了 java.util.concurrent(简称JUC)包,在此包中增加了在并发编程中很常用的工具类,
    用于定义类似于线程的自定义子系统,包括线程池,异步 IO 和轻量级任务框架;还提供了设计用于多线程上下文中
    的 Collection 实现等;

2. volatile 关键字

  • volatile 关键字: 当多个线程进行操作共享数据时,可以保证内存中的数据是可见的;相较于 synchronized 是一种
    较为轻量级的同步策略;
  • volatile 不具备"互斥性";
  • volatile 不能保证变量的"原子性";
// 使用 volatile 之前
public class TestVolatile{public static void main(String[] args){ThreadDemo td = new ThreadDemo();new Thread(td).start();while(true){if(td.isFlag()){System.out.println("########");break;}}}
}class ThreadDemo implements Runnable{private boolean flag = false;public void run(){try{// 该线程 sleep(200), 导致了程序无法执行成功Thread.sleep(200);}catch(InterruptedException e){e.printStackTrace();}flag = true;Sytem.out.println("flag="+isFlag());}public boolean isFlag(){return flag;}public void setFlag(boolean flag){this.flag = flag;}
}

// 解决问题方式一: 同步锁
//   但是,效率太低
public class TestVolatile{public static void main(String[] args){ThreadDemo td = new ThreadDemo();new Thread(td).start();while(true){// 使用同步锁synchronized(td){if(td.isFlag()){System.out.println("########");break;}}}}
}// 解决方式二: 使用 volatile 关键字
public class TestVolatile{public static void main(String[] args){ThreadDemo td = new ThreadDemo();new Thread(td).start();while(true){if(td.isFlag()){System.out.println("########");break;}}}
}class ThreadDemo implements Runnable{private volatile boolean flag = false;同上(略)
}

3. i++ 的原子性问题

  1. i++的操作实际上分为三个步骤: "读-改-写";
  2. 原子性: 就是"i++"的"读-改-写"是不可分割的三个步骤;
  3. 原子变量: JDK1.5 以后, java.util.concurrent.atomic包下,提供了常用的原子变量;
    • 原子变量中的值,使用 volatile 修饰,保证了内存可见性;
    • CAS(Compare-And-Swap) 算法保证数据的原子性;
int i = 10;
i = i++;  // 此时, i=10执行步骤:
int temp = i;
i = i + 1;
i = temp;// 测试类
public class TestAtomicDemo{public static void main(String[] args){AtomicDemo ad = new AtomicDemo();for(int i=0; i < 10; i++){new Thread(ad).start();}}
}class AtomicDemo implements Runnable{private int serialNumber = 0;public void run(){try{Thread.sleep(200);}catch(InterruptedException e){}System.out.println(Thread.currentThread().getName() + ":" + getSerialNumber());}public int getSerialNumber(){return serialNumber++;}
}

// 改进: 使用原子变量
class AtomicDemo implements Runnable{private AtomicInteger serialNumber = new AtomicInteger();public void run(){try{Thread.sleep(200);}catch(InterruptedException e){}System.out.println(Thread.currentThread().getName()+":"+getSerialNumber());}public int getSerialNumber(){// 自增运算return serialNumber.getAndIncrement();}
}

3.1 CAS 算法

  • CAS(Compare-And-Swap) 算法是硬件对于并发的支持,针对多处理器操作而设计的处理器中的一种特殊指令,用于
    管理对共享数据的并发访问;
  • CAS 是一种无锁的非阻塞算法的实现;
  • CAS 包含了三个操作数:
    • 需要读写的内存值: V
    • 进行比较的预估值: A
    • 拟写入的更新值: B
    • 当且仅当 V == A 时, V = B, 否则,将不做任何操作;
// 模拟CAS 算法
class CompareAndSwap{private int value;// 获取内存值public synchronized int get(){return value;}// 无论更新成功与否,都返回修改之前的内存值public synchronized int compareAndSwap(int expectedValue, int newValue){// 获取旧值int oldValue = value;if(oldValue == expectedValue){this.value = newValue;}// 返回修改之前的值return oldValue;}// 判断是否设置成功public synchronized boolean compareAndSet(int expectedValue, int newValue){return expectedValue == compareAndSwap(expectedValue, newValue);}
}public class TestCompareAndSwap{public static void main(String[] args){final CopareAndSwap cas = new CompareAndSwap();for(int i=0; i<10; i++){// 创建10个线程,模拟多线程环境new Thead(new Runnable(){public void run(){int expectedValue = cas.get();boolean b = cas.compareAndSet(expectedValue, (int)(Math.random()*100));System.out.println(b);}}).start();}}
}

4. 并发容器类

  • Java 5.0 在 java.util.concurrent 包中提供了多种并发容器类来改进同步容器的性能;

4.1 ConcurrentHashMap

  • ConcurrentHashMap 同步容器类是 Java5 增加的一个线程安全的哈希表;介于 HashMap 与 Hashtable 之间;
    内部采用"锁分段"机制替代Hashtable的独占锁,进而提高性能;
  • 此包还提供了设计用于多线程上下文中的Collection实现: ConcurrentHashMap,ConcurrentSkipListMap
    ConcurrentSkipListSetCopyOnWriteArrayList 和 CopyOnWriteArraySet;

    • 当期望许多线程访问一个给定collection时,ConcurrentHashMap通常优于同步的HashMap;
      ConcurrentSkipListMap通常优于同步的TreeMap;
    • 当期望的读数和遍历远远大于列表的更新数时, CopyOnWriteArrayList优于同步的ArrayList;

4.2 CountDownLatch(闭锁)

  • CountDownLatch是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待;
// 测试类: 计算多线程的执行时间
public class TestCountDownLatch{public static void main(String[] args){final CountDownLatch latch = new CountDownLatch(10);LatchDemo ld = new LatchDemo(latch);long start = System.currentTimeMillis();// 创建10个线程for(int i=0; i<10; i++){new Thread(ld).start();}try{latch.await();}catch(InterruptedException e){}long end = System.currentTimeMillis();System.out.println("耗费时间为:"+(end - start));}
}class LatchDemo implements Runnable{private CountDownLatch latch;// 有参构造器public LatchDemo(CountDownLatch latch){this.latch = latch;}public void run(){synchronized(this){try{// 打印50000以内的偶数for(int i=0; i<50000; i++){if(i % 2 == 0){System.out.println(i);}}}finally{// 线程数量递减latch.countDown();}}}
}

5. 创建执行线程的方式三

  • 相较于实现 Runnable 接口的方式,实现 Callable 接口类中的方法可以有返回值,并且可以抛出异常;
// 测试类
public class TestCallable{public static void main(String[] args){ThreadDemo td = new ThreadDemo();// 执行 Callable 方式,需要 FutureTask 实现类的支持// FutureTask 实现类用于接收运算结果, FutureTask 是 Future 接口的实现类FutureTask<Integer> result = new FutureTask<>(td);new Thread(result).start();// 接收线程运算后的结果try{// 只有当 Thread 线程执行完成后,才会打印结果;// 因此, FutureTask 也可用于闭锁Integer sum = result.get();System.out.println(sum);}catch(InterruptedException | ExecutionException e){e.printStackTrace();}}
}class ThreadDemo implements Callable<Integer>{// 需要实现的方法public Integer call() throws Exception{// 计算 0~100 的和int sum = 0;for(int i=0; i<=100; i++){sum += i;}return sum;}
}

6. 同步锁(Lock)

  • 参考 "java 多线程间通信"
// 测试类: 以卖票为例
// 使用 lock 之前
public class TestLock{public static void main(String[] args){Ticket ticket = new Ticket();new Thread(ticket,"1号窗口").start();new Thread(ticket,"2号窗口").start();new Thread(ticket,"3号窗口").start();}
}class Ticket implements Runnable{private int tick = 100;public void run(){while(true){if(tick > 0){try{Thread.sleep(200);}catch(InterruptedException e){}System.out.println(Thread.currentThread().getName()+"完成售票,余票为: "+ --tick);}}}
}// 使用 Lock
class Ticket implements Runnable{private int tick = 100;private Lock lock = new ReentrantLock();public void run(){while(true){// 上锁lock.lock();try{if(tick > 0){try{Thread.sleep(200);}catch(InterruptedException e){}System.out.println(Thread.currentThread().getName()+"完成售票,余票为: "+ --tick);}}finally{// 释放锁lock.unlock();}}}
}// 练习: 程序按序交替
// 编写一个程序,开启3个线程,这三个线程的 ID 分别为 A, B, C, 每个线程将自己的 ID 在屏幕上打印10遍,
// 要求输出的结果必须按顺序显示:
// 如: ABCABCABC... 依次递归public class TestABCAlternate{public static void main(String[] args){AlternateDemo ad = new AlternateDemo();new Thread(new Runnable(){public void run(){for(int i=1; i<20; i++){ad.loopA(i);}}},"A").start();new Thread(new Runnable(){public void run(){for(int i=1; i<20; i++){ad.loopB(i);}}},"B").start();new Thread(new Runnable(){public void run(){for(int i=1; i<20; i++){ad.loopC(i);System.out.println("--------------------");}}},"C").start();}
}class AlternateDemo{private int number = 1; // 当前正在执行线程的标记private Lock lock = new ReentrantLock();private Condition condition1 = lock.newCondition();private Condition condition2 = lock.newCondition();private Condition condition3 = lock.newCondition();// totalLoop 表示循环第几轮// 线程Apublic void loopA(int totalLoop){// 上锁lock.lock();try{// 1. 判断if(number != 1){condition1.await();}// 2. 打印for(int i=1; i <= 5; i++){System.out.println(Thread.currentThread().getName()+"\t"+i+"\t"+totalLoop);}// 3. 唤醒线程Bnumber = 2;condition2.signal();}catch(Exception e){e.printStackTrace();}finally{// 释放锁lock.unlock();}}// 线程Bpublic void loopB(int totalLoop){// 上锁lock.lock();try{// 1. 判断if(number != 2){condition2.await();}// 2. 打印for(int i=1; i <= 15; i++){System.out.println(Thread.currentThread().getName()+"\t"+i+"\t"+totalLoop);}// 3. 唤醒线程Cnumber = 3;condition3.signal();}catch(Exception e){e.printStackTrace();}finally{// 释放锁lock.unlock();}}// 线程Cpublic void loopC(int totalLoop){// 上锁lock.lock();try{// 1. 判断if(number != 3){condition3.await();}// 2. 打印for(int i=1; i <= 20; i++){System.out.println(Thread.currentThread().getName()+"\t"+i+"\t"+totalLoop);}// 3. 唤醒线程Anumber = 1;condition1.signal();}catch(Exception e){e.printStackTrace();}finally{// 释放锁lock.unlock();}}
}

7. ReadWriteLock(读写锁)

// 测试类
public class TestReadWriteLock{public static void main(String[] args){ReadWriteLockDemo rw = new ReadWriteLockDemo();// 一个线程进行写new Thread(new Runnable(){public void run(){rw.set((int)(Math.random()*100));}},"Write:").start();// 100个线程进行读操作for(int i=0; i<100; i++){new Thread(new Runnable(){public void run(){rw.get();}},"Read:").start();}}}class ReadWriteLockDemo{private int number = 0;private ReadWriteLock lock = new ReentrantReadWriteLock();// 读public void get(){lock.readLock().lock(); // 上锁try{System.out.println(Thread.currentThread().getName()+":"+number);}finally{lock.readLock().unlock(); // 释放锁}}// 写public void set(int number){lock.writeLock().lock();try{    System.out.println(Thread.currentThread().getName());this.number = number;}finally{lock.writeLock().unlock();}}
}

8. 线程八锁

// 测试类
public class Test{public static void main(String[] args){Demo demo = new Demo();Demo demo2 = new Demo();new Thread(new Runnable(){public void run(){demo.getOne();}}).start();new Thread(new Runnable(){public void run(){// demo2.getTwo();demo.getTwo();}}).start();}
}class Demo{public synchronized void getOne(){try{Thread.sleep(3000);}catch(InterruptedException e){}System.out.println("one");}public synchronized void getTwo(){System.out.println("two");}}/** 1. 两个普通同步方法,两个线程,标准打印, 打印输出: one  two* 2. 新增 Thread.sleep() 给 getOne(), 打印输出: one  two* 3. 新增普通方法 getThree(), 打印输出: three  one  two* 4. 两个普通同步方法,两个Demo对象, 两个线程,打印输出: two  one* 5. 修改 getOne() 为静态同步方法, 一个Demo对象, 打印输出: two  one* 6. 修改两个方法都为静态同步方法, 一个 Demo 对象, 打印输出: one  two* 7. 修改 getone() 为静态同步方法, 两个 Demo 对象, 打印输出: two  one* 8. 两个均为静态同步方法,两个 Demo 对象,打印输出: one  two*/// 总结://    1. 非静态方法的锁默认为 this, 静态方法的锁为 "对应的Class实例";//    2. 在某一个时刻内,只能有一个线程持有锁,无论几个方法;

9. 线程池

  • 线程池提供了一个线程队列,队列中保存着所有等待状态的线程;
  • 避免了创建与销毁线程的额外开销,提高了响应速度;
  • 线程池的体系结构
    • java.util.concurrent.Executor: 负责线程的使用和调度的根接口;
    • ExecutorService: 子接口,线程池的主要接口;
    • ThreadPoolExecutor: 线程池的实现类;
    • ScheduledExecutorService: 子接口,负责线程的调度;
    • ScheduledThreadPoolExecutor: 继承了线程池的实现类,实现了负责线程调度的子接口;
  • 工具类: Executors
    • ExecutorService newFixedThreadPool(): 创建固定大小的线程池;
    • ExecutorService newCachedThreadPool(): 缓存线程池,线程池中线程的数量不固定,可以根据需求自动更改数量;
    • ExecutorService newSingleThreadExecutor(): 创建单个线程池, 线程池中只有一个线程;
    • ScheduledExecutorService newScheduledThreadPool(): 创建固定大小的线程,可以延时或定时的执行任务;
public class TestThreadPool{public static void main(String[] args){// 1. 创建线程池ExecutorService pool = Executors.newFixedThreadPool(5);ThreadPoolDemo tpd = new ThreadPoolDemo();// 2. 为线程池中线程分配任务//    submit(Callable<T> task)//    submit(Runnable task)for(int i=0; i<10; i++){pool.submit(tpd);}// 3. 关闭线程池pool.shutdown();}
}class ThreadPoolDemo implements Runnable{private int i=0;public void run(){while(i <= 100){System.out.println(Thread.currentThread().getName()+" : "+ i++)}}
}

9.1 线程调度

public class TestScheduledThreadPool{public static void main(String[] args) throws Exception{// 1. 创建线程池ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);// 2. 分配任务//      pool.shedule(Callalbe<T> callable, long delay, TimeUnit unit(时间单位))for(int i=0; i < 10; i++){Future<Integer> result = pool.schedule(new Callable<Integer>(){public Integer call() throws Exception{// 产生100以内的随机数int num = new Random().nextInt(100);System.out.println(Thread.currentThread().getName()+ ":" + num);return num;}}, 3, TimeUnit.SECONDS);System.out.println(result.get());}//3. 关闭线程池pool.shutdown();}
}

10 Fork/Join 框架

public class TestForkJoinPool{public static void main(String[] args){ForkJoinPool pool = new ForkJoinPool();ForkJoinTask<Long> task = new ForkJoinSumCalculate(0L, 100000000L);Long sum = pool.invoke(task);System.out.println(sum);}}class ForkJoinSumCalculate extends RecursiveTask<Long>{private static final long serialVersionUID = 24340990L;private long start;private long end;private static final long THURSHOLD = 10000L;  // 拆分临界值// 有参构造器public ForkJoinSumCalculate(long start, long end){this.start = start;this.end = end;}public Long compute(){long length = end - start;if(length <= THURSHOLD){long  sum = 0L;for(long i = start; i<=end; i++){sum += i;}return sum;}else{long middle = (start + end ) / 2;ForkJoinSumCalculate left = new ForkJoinSumCalculate(start, middle);left.fork(); // 进行拆分,同时压入线程队列ForkJoinSumCalculate right = new ForkJoinSumCalculate(middle + 1, end);right.fork(); // 进行拆分,同时压入线程队列return left.join() + right.join();}}
}

Java 之 JUC相关推荐

  1. 厚积薄发打卡Day26:狂神说Java之JUC并发编程<代码+笔记>(上)

    前言: 学习视频来源:[狂神说Java]JUC并发编程最新版通俗易懂 一个十分优秀且励志的技术大牛+Java讲师,十分推荐他的频道:遇见狂神说

  2. 基于《狂神说Java》JUC并发编程--学习笔记

    前言: 本笔记仅做学习与复习使用,不存在刻意抄袭. -------------------------------------------------------------------------- ...

  3. Java——聊聊JUC中的Future和FutureTask

    1.回顾Future和FutureTask 首先,Future是一个接口(Java5就诞生了),而FutureTask是它的实现类.而Future接口则是定义了操作异步任务执行的一些方法,提供了一种异 ...

  4. Java——聊聊JUC中的CompletableFuture

    文章目录: 1.承接Future和FutureTask 2.CompletableFuture四大静态方法 2.1 runAsync(Runnable runnable) 2.2 runAsync(R ...

  5. java多线程JUC学习笔记

    JUC(java.util.concurrent) 1.1 进程/线程 1.2并发/并行 并发编程:并发.并行 并发(多线程操作同一个资源) CPI一核,模拟出来多条线程,天下武功,唯快不破,快速交替 ...

  6. 【Java】JUC(java.util.concurrent)工具包中的并发相关

    目录 一.AQS详解 AQS原理 AQS 对资源的共享方式 AQS 底层使用了模板方法模式 AQS具体应用举例 ReentrantLock CountDownLatch 二.ConcurrentHas ...

  7. Java之JUC并发编程

    JUC JUC概述 进程与线程 线程的状态 wait / sleep的区别 并发和并行 管程 用户线程与守护线程 用户线程 守护线程 Lock 接口 synchronized Lock锁 lock() ...

  8. 备战面试日记(1.3) - (Java多线程.JUC)

    本人本科毕业,21届毕业生,一年工作经验,简历专业技能如下,现根据简历,并根据所学知识复习准备面试. 记录日期:2021.12.29 大部分知识点只做大致介绍,具体内容根据推荐博文链接进行详细复习. ...

  9. java并发-JUC

    CAS 无锁,乐观锁,自旋锁,轻量级锁 定义 Compare and Swap,是基于硬件级别的指令实现的同步原语,Java并发包java.utile.concurrent许多同步类基于CAS构建 c ...

最新文章

  1. html中<pre>标签
  2. 关于爬虫异步请求心得
  3. MCMC采样和M-H采样
  4. 红帽linux cd命令,redhat linux 文件操作
  5. 大白话5分钟带你走进人工智能-第二十节逻辑回归和Softmax多分类问题(5)
  6. 解决centos6.4 启动dell omsa 失败
  7. foxmail的邮局和端口_Foxmail如何进行POP、SMTP设置(pop)
  8. S5P6818 芯片手册 System Control 章节 理论篇
  9. delete语句详解
  10. 什么是 PaaS?“平台即服务“ 简介
  11. 推荐几个摸鱼时常看的技术网站
  12. Android Jetpack中CameraX保存Bitmap
  13. 介绍了用Meta标签代码让360双核浏览器默认极速模式(google)打开网站不是兼容模式
  14. AccessibilityService的学习,抢红包实现
  15. css实现icon动画效果
  16. Ubuntu 20.04折腾markdown编辑器remarkable血泪史
  17. ora-12505错误
  18. 分享上海seo统计的seo基础知识
  19. 论文阅读《Do Pre-trained Models Benefit Knowledge Graph Completion?A Reliable Evaluation and a Reasonab》
  20. 554 5.7.1详细排错过程

热门文章

  1. 物流仓库管理系统源码
  2. 扫描神器nmap最佳实践使用
  3. 开源b2b2c多用户商城系统具有的优势
  4. 【保研经验】来自一只five的一点经验(最终去向:西电广研院专硕)
  5. Games104笔记---LE6--渲染系统3:游戏中的地形/天空/云渲染
  6. VINKA/永嘉14*4点 LCD液晶段码屏显示驱动控制电路(IC)-VK1S56D SSOP24小体积密脚(0.635脚距),具省电模式,用于美容仪器/小音箱等 小体积段码屏显示驱动
  7. 程序员也可以很浪漫,精选10个圣诞节特效及源码
  8. 使用云服务器搭建黑暗之魂3私服详细教程(ds3os)
  9. adb 下删除锁屏密码
  10. 降权是什么?为什么会被降权?