Java JUC 简介

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

volatile 关键字-内存可见性

内存可见性

内存可见性(Memory Visibility)

是指当某个线程正在使用对象状态
而另一个线程在同时修改该状态,需要确保当一个线程修改了对象
状态后,其他线程能够看到发生的状态变化。

可见性错误是指当读操作与写操作在不同的线程中执行时,我们无
法确保执行读操作的线程能适时地看到其他线程写入的值,有时甚
至是根本不可能的事情。

我们可以通过同步来保证对象被安全地发布。除此之外我们也可以
使用一种更加轻量级的 volatile 变量

volatile 关键字

Java 提供了一种稍弱的同步机制,即 volatile 变
量,用来确保将变量的更新操作通知到其他线程。
可以将 volatile 看做一个轻量级的锁,但是又与
锁有些不同:
对于多线程,不是一种互斥关系
不能保证变量状态的“原子性操作”

  1. /*
  2. * 一、volatile 关键字:当多个线程进行操作共享数据时,可以保证内存中的数据可见。
  3. * 相较于 synchronized 是一种较为轻量级的同步策略。
  4. *
  5. * 注意:
  6. * 1. volatile 不具备“互斥性”
  7. * 2. volatile 不能保证变量的“原子性”
  8. */
  9. public class TestVolatile {
  10.  
  11. public static void main(String[] args) {
  12. ThreadDemo td = new ThreadDemo();
  13. new Thread(td).start();
  14.  
  15. while(true){
  16. if(td.isFlag()){
  17. System.out.println("------------------");
  18. break;
  19. }
  20. }
  21.  
  22. }
  23.  
  24. }
  25.  
  26. class ThreadDemo implements Runnable {
  27.  
  28. private volatile boolean flag = false;
  29.  
  30. @Override
  31. public void run() {
  32.  
  33. try {
  34. Thread.sleep(200);
  35. } catch (InterruptedException e) {
  36. }
  37.  
  38. flag = true;
  39.  
  40. System.out.println("flag=" + isFlag());
  41.  
  42. }
  43.  
  44. public boolean isFlag() {
  45. return flag;
  46. }
  47.  
  48. public void setFlag(boolean flag) {
  49. this.flag = flag;
  50. }
  51.  
  52. }

原子变量-CAS算法

CAS 算法

  1. /*
  2. * 模拟 CAS 算法
  3. */
  4. public class TestCompareAndSwap {
  5.  
  6. public static void main(String[] args) {
  7. final CompareAndSwap cas = new CompareAndSwap();
  8.  
  9. for (int i = 0; i < 10; i++) {
  10. new Thread(new Runnable() {
  11.  
  12. @Override
  13. public void run() {
  14. int expectedValue = cas.get();
  15. boolean b = cas.compareAndSet(expectedValue, (int)(Math.random() * 101));
  16. System.out.println(b);
  17. }
  18. }).start();
  19. }
  20.  
  21. }
  22.  
  23. }
  24.  
  25. class CompareAndSwap{
  26. private int value;
  27.  
  28. //获取内存值
  29. public synchronized int get(){
  30. return value;
  31. }
  32.  
  33. //比较
  34. public synchronized int compareAndSwap(int expectedValue, int newValue){
  35. int oldValue = value;
  36.  
  37. if(oldValue == expectedValue){
  38. this.value = newValue;
  39. }
  40.  
  41. return oldValue;
  42. }
  43.  
  44. //设置
  45. public synchronized boolean compareAndSet(int expectedValue, int newValue){
  46. return expectedValue == compareAndSwap(expectedValue, newValue);
  47. }
  48. }

CAS (Compare-And-Swap) 是一种硬件对并发的支持,针对多处理器
操作而设计的处理器中的一种特殊指令,用于管理对共享数据的并
发访问。
CAS 是一种无锁的非阻塞算法的实现。
CAS 包含了 3 个操作数:
需要读写的内存值 V
进行比较的值 A
拟写入的新值 B
当且仅当 V 的值等于 A 时,CAS 通过原子方式用新值 B 来更新 V 的
值,否则不会执行任何操作。

原子变量

  1. import java.util.concurrent.atomic.AtomicInteger;
  2.  
  3. /*
  4. * 一、i++ 的原子性问题:i++ 的操作实际上分为三个步骤“读-改-写”
  5. * int i = 10;
  6. * i = i++; //10
  7. *
  8. * int temp = i;
  9. * i = i + 1;
  10. * i = temp;
  11. *
  12. * 二、原子变量:在 java.util.concurrent.atomic 包下提供了一些原子变量。
  13. * 1. volatile 保证内存可见性
  14. * 2. CAS(Compare-And-Swap) 算法保证数据变量的原子性
  15. * CAS 算法是硬件对于并发操作的支持
  16. * CAS 包含了三个操作数:
  17. * ①内存值 V
  18. * ②预估值 A
  19. * ③更新值 B
  20. * 当且仅当 V == A 时, V = B; 否则,不会执行任何操作。
  21. */
  22. public class TestAtomicDemo {
  23.  
  24. public static void main(String[] args) {
  25. AtomicDemo ad = new AtomicDemo();
  26.  
  27. for (int i = 0; i < 10; i++) {
  28. new Thread(ad).start();
  29. }
  30. }
  31.  
  32. }
  33.  
  34. class AtomicDemo implements Runnable{
  35.  
  36. // private volatile int serialNumber = 0;
  37.  
  38. private AtomicInteger serialNumber = new AtomicInteger(0);
  39.  
  40. @Override
  41. public void run() {
  42.  
  43. try {
  44. Thread.sleep(200);
  45. } catch (InterruptedException e) {
  46. }
  47.  
  48. System.out.println(getSerialNumber());
  49. }
  50.  
  51. public int getSerialNumber(){
  52. return serialNumber.getAndIncrement();
  53. }
  54.  
  55. }

类的小工具包,支持在单个变量上解除锁的线程安全编程。事实上,此包中的类可
将 volatile 值、字段和数组元素的概念扩展到那些也提供原子条件更新操作的类。
类 AtomicBoolean、AtomicInteger、AtomicLong 和 AtomicReference 的实例各自提供对
相应类型单个变量的访问和更新。每个类也为该类型提供适当的实用工具方法。
AtomicIntegerArray、AtomicLongArray 和 AtomicReferenceArray 类进一步扩展了原子操
作,对这些类型的数组提供了支持。这些类在为其数组元素提供 volatile 访问语义方
面也引人注目,这对于普通数组来说是不受支持的。
核心方法:boolean compareAndSet(expectedValue, updateValue)
java.util.concurrent.atomic 包下提供了一些原子操作的常用类:
AtomicBoolean 、AtomicInteger 、AtomicLong 、 AtomicReference
AtomicIntegerArray 、AtomicLongArray
AtomicMarkableReference
AtomicReferenceArray
AtomicStampedReference

ConcurrentHashMap 锁分段机制

ConcurrentHashMap

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

ConcurrentHashMap 同步容器类是Java 5 增加的一个线程安全的哈希表。对
与多线程的操作,介于 HashMap 与 Hashtable 之间。内部采用“锁分段”
机制替代 Hashtable 的独占锁。进而提高性能。

此包还提供了设计用于多线程上下文中的 Collection 实现:
ConcurrentHashMap、ConcurrentSkipListMap、ConcurrentSkipListSet、
CopyOnWriteArrayList 和 CopyOnWriteArraySet。当期望许多线程访问一个给
定 collection 时,ConcurrentHashMap 通常优于同步的 HashMap,
ConcurrentSkipListMap 通常优于同步的 TreeMap。当期望的读数和遍历远远
大于列表的更新数时,CopyOnWriteArrayList 优于同步的 ArrayList。

  1. import java.util.Iterator;
  2. import java.util.concurrent.CopyOnWriteArrayList;
  3.  
  4. /*
  5. * CopyOnWriteArrayList/CopyOnWriteArraySet : “写入并复制”
  6. * 注意:添加操作多时,效率低,因为每次添加时都会进行复制,开销非常的大。并发迭代操作多时可以选择。
  7. */
  8. public class TestCopyOnWriteArrayList {
  9.  
  10. public static void main(String[] args) {
  11. HelloThread ht = new HelloThread();
  12.  
  13. for (int i = 0; i < 10; i++) {
  14. new Thread(ht).start();
  15. }
  16. }
  17.  
  18. }
  19.  
  20. class HelloThread implements Runnable{
  21.  
  22. // private static List<String> list = Collections.synchronizedList(new ArrayList<String>());
  23.  
  24. private static CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
  25.  
  26. static{
  27. list.add("AA");
  28. list.add("BB");
  29. list.add("CC");
  30. }
  31.  
  32. @Override
  33. public void run() {
  34.  
  35. Iterator<String> it = list.iterator();
  36.  
  37. while(it.hasNext()){
  38. System.out.println(it.next());
  39.  
  40. list.add("AA");
  41. }
  42.  
  43. }
  44.  
  45. }

CountDownLatch 闭锁

CountDownLatch
Java 5.0 在 java.util.concurrent 包中提供了多种并发容器类来改进同步容器
的性能。
CountDownLatch 一个同步辅助类,在完成一组正在其他线程中执行的操作
之前,它允许一个或多个线程一直等待。
闭锁可以延迟线程的进度直到其到达终止状态,闭锁可以用来确保某些活
动直到其他活动都完成才继续执行:
确保某个计算在其需要的所有资源都被初始化之后才继续执行;
确保某个服务在其依赖的所有其他服务都已经启动之后才启动;
等待直到某个操作所有参与者都准备就绪再继续执行。

  1. import java.util.concurrent.CountDownLatch;
  2.  
  3. /*
  4. * CountDownLatch :闭锁,在完成某些运算是,只有其他所有线程的运算全部完成,当前运算才继续执行
  5. */
  6. public class TestCountDownLatch {
  7.  
  8. public static void main(String[] args) {
  9. final CountDownLatch latch = new CountDownLatch(50);
  10. LatchDemo ld = new LatchDemo(latch);
  11.  
  12. long start = System.currentTimeMillis();
  13.  
  14. for (int i = 0; i < 50; i++) {
  15. new Thread(ld).start();
  16. }
  17.  
  18. try {
  19. latch.await();
  20. } catch (InterruptedException e) {
  21. }
  22.  
  23. long end = System.currentTimeMillis();
  24.  
  25. System.out.println("耗费时间为:" + (end - start));
  26. }
  27.  
  28. }
  29.  
  30. class LatchDemo implements Runnable {
  31.  
  32. private CountDownLatch latch;
  33.  
  34. public LatchDemo(CountDownLatch latch) {
  35. this.latch = latch;
  36. }
  37.  
  38. @Override
  39. public void run() {
  40.  
  41. try {
  42. for (int i = 0; i < 50000; i++) {
  43. if (i % 2 == 0) {
  44. System.out.println(i);
  45. }
  46. }
  47. } finally {
  48. latch.countDown();
  49. }
  50.  
  51. }
  52.  
  53. }

实现 Callable 接口

Callable 接口
Java 5.0 在 java.util.concurrent 提供了一个新的创建执行
线程的方式:Callable 接口
Callable 接口类似于 Runnable,两者都是为那些其实例可
能被另一个线程执行的类设计的。但是 Runnable 不会返
回结果,并且无法抛出经过检查的异常。
Callable 需要依赖FutureTask ,FutureTask 也可以用作闭
锁。

  1. import java.util.concurrent.Callable;
  2. import java.util.concurrent.ExecutionException;
  3. import java.util.concurrent.FutureTask;
  4.  
  5. /*
  6. * 一、创建执行线程的方式三:实现 Callable 接口。 相较于实现 Runnable 接口的方式,方法可以有返回值,并且可以抛出异常。
  7. *
  8. * 二、执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。 FutureTask 是 Future 接口的实现类
  9. */
  10. public class TestCallable {
  11.  
  12. public static void main(String[] args) {
  13. ThreadDemo td = new ThreadDemo();
  14.  
  15. //1.执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。
  16. FutureTask<Integer> result = new FutureTask<>(td);
  17.  
  18. new Thread(result).start();
  19.  
  20. //2.接收线程运算后的结果
  21. try {
  22. Integer sum = result.get(); //FutureTask 可用于 闭锁
  23. System.out.println(sum);
  24. System.out.println("------------------------------------");
  25. } catch (InterruptedException | ExecutionException e) {
  26. e.printStackTrace();
  27. }
  28. }
  29.  
  30. }
  31.  
  32. class ThreadDemo implements Callable<Integer>{
  33.  
  34. @Override
  35. public Integer call() throws Exception {
  36. int sum = 0;
  37.  
  38. for (int i = 0; i <= 100000; i++) {
  39. sum += i;
  40. }
  41.  
  42. return sum;
  43. }
  44.  
  45. }
  46.  
  47. /*class ThreadDemo implements Runnable{
  48.  
  49. @Override
  50. public void run() {
  51. }
  52.  
  53. }*/

Lock 同步锁

显示锁 Lock
在 Java 5.0 之前,协调共享对象的访问时可以使用的机
制只有 synchronized 和 volatile 。Java 5.0 后增加了一些
新的机制,但并不是一种替代内置锁的方法,而是当内
置锁不适用时,作为一种可选择的高级功能。
ReentrantLock 实现了 Lock 接口,并提供了与
synchronized 相同的互斥性和内存可见性。但相较于
synchronized 提供了更高的处理锁的灵活性。

  1. import java.util.concurrent.locks.Lock;
  2. import java.util.concurrent.locks.ReentrantLock;
  3.  
  4. /*
  5. * 一、用于解决多线程安全问题的方式:
  6. *
  7. * synchronized:隐式锁
  8. * 1. 同步代码块
  9. *
  10. * 2. 同步方法
  11. *
  12. * jdk 1.5 后:
  13. * 3. 同步锁 Lock
  14. * 注意:是一个显示锁,需要通过 lock() 方法上锁,必须通过 unlock() 方法进行释放锁
  15. */
  16. public class TestLock {
  17.  
  18. public static void main(String[] args) {
  19. Ticket ticket = new Ticket();
  20.  
  21. new Thread(ticket, "1号窗口").start();
  22. new Thread(ticket, "2号窗口").start();
  23. new Thread(ticket, "3号窗口").start();
  24. }
  25.  
  26. }
  27.  
  28. class Ticket implements Runnable{
  29.  
  30. private int tick = 100;
  31.  
  32. private Lock lock = new ReentrantLock();
  33.  
  34. @Override
  35. public void run() {
  36. while(true){
  37.  
  38. lock.lock(); //上锁
  39.  
  40. try{
  41. if(tick > 0){
  42. try {
  43. Thread.sleep(200);
  44. } catch (InterruptedException e) {
  45. }
  46.  
  47. System.out.println(Thread.currentThread().getName() + " 完成售票,余票为:" + --tick);
  48. }
  49. }finally{
  50. lock.unlock(); //释放锁
  51. }
  52. }
  53. }
  54.  
  55. }
  1. /*
  2. * 生产者和消费者案例
  3. */
  4. public class TestProductorAndConsumer {
  5.  
  6. public static void main(String[] args) {
  7. Clerk clerk = new Clerk();
  8.  
  9. Productor pro = new Productor(clerk);
  10. Consumer cus = new Consumer(clerk);
  11.  
  12. new Thread(pro, "生产者 A").start();
  13. new Thread(cus, "消费者 B").start();
  14.  
  15. new Thread(pro, "生产者 C").start();
  16. new Thread(cus, "消费者 D").start();
  17. }
  18.  
  19. }
  20.  
  21. /*//店员
  22. class Clerk{
  23. private int product = 0;
  24.  
  25. //进货
  26. public synchronized void get(){//循环次数:0
  27. while(product >= 1){//为了避免虚假唤醒问题,应该总是使用在循环中
  28. System.out.println("产品已满!");
  29.  
  30. try {
  31. this.wait();
  32. } catch (InterruptedException e) {
  33. }
  34.  
  35. }
  36.  
  37. System.out.println(Thread.currentThread().getName() + " : " + ++product);
  38. this.notifyAll();
  39. }
  40.  
  41. //卖货
  42. public synchronized void sale(){//product = 0; 循环次数:0
  43. while(product <= 0){
  44. System.out.println("缺货!");
  45.  
  46. try {
  47. this.wait();
  48. } catch (InterruptedException e) {
  49. }
  50. }
  51.  
  52. System.out.println(Thread.currentThread().getName() + " : " + --product);
  53. this.notifyAll();
  54. }
  55. }
  56.  
  57. //生产者
  58. class Productor implements Runnable{
  59. private Clerk clerk;
  60.  
  61. public Productor(Clerk clerk) {
  62. this.clerk = clerk;
  63. }
  64.  
  65. @Override
  66. public void run() {
  67. for (int i = 0; i < 20; i++) {
  68. try {
  69. Thread.sleep(200);
  70. } catch (InterruptedException e) {
  71. }
  72.  
  73. clerk.get();
  74. }
  75. }
  76. }
  77.  
  78. //消费者
  79. class Consumer implements Runnable{
  80. private Clerk clerk;
  81.  
  82. public Consumer(Clerk clerk) {
  83. this.clerk = clerk;
  84. }
  85.  
  86. @Override
  87. public void run() {
  88. for (int i = 0; i < 20; i++) {
  89. clerk.sale();
  90. }
  91. }
  92. }*/
  1. import java.util.concurrent.locks.Condition;
  2. import java.util.concurrent.locks.Lock;
  3. import java.util.concurrent.locks.ReentrantLock;
  4.  
  5. /*
  6. * 生产者消费者案例:
  7. */
  8. public class TestProductorAndConsumerForLock {
  9.  
  10. public static void main(String[] args) {
  11. Clerk clerk = new Clerk();
  12.  
  13. Productor pro = new Productor(clerk);
  14. Consumer con = new Consumer(clerk);
  15.  
  16. new Thread(pro, "生产者 A").start();
  17. new Thread(con, "消费者 B").start();
  18.  
  19. // new Thread(pro, "生产者 C").start();
  20. // new Thread(con, "消费者 D").start();
  21. }
  22.  
  23. }
  24.  
  25. class Clerk {
  26. private int product = 0;
  27.  
  28. private Lock lock = new ReentrantLock();
  29. private Condition condition = lock.newCondition();
  30.  
  31. // 进货
  32. public void get() {
  33. lock.lock();
  34.  
  35. try {
  36. if (product >= 1) { // 为了避免虚假唤醒,应该总是使用在循环中。
  37. System.out.println("产品已满!");
  38.  
  39. try {
  40. condition.await();
  41. } catch (InterruptedException e) {
  42. }
  43.  
  44. }
  45. System.out.println(Thread.currentThread().getName() + " : "
  46. + ++product);
  47.  
  48. condition.signalAll();
  49. } finally {
  50. lock.unlock();
  51. }
  52.  
  53. }
  54.  
  55. // 卖货
  56. public void sale() {
  57. lock.lock();
  58.  
  59. try {
  60. if (product <= 0) {
  61. System.out.println("缺货!");
  62.  
  63. try {
  64. condition.await();
  65. } catch (InterruptedException e) {
  66. }
  67. }
  68.  
  69. System.out.println(Thread.currentThread().getName() + " : "
  70. + --product);
  71.  
  72. condition.signalAll();
  73.  
  74. } finally {
  75. lock.unlock();
  76. }
  77. }
  78. }
  79.  
  80. // 生产者
  81. class Productor implements Runnable {
  82.  
  83. private Clerk clerk;
  84.  
  85. public Productor(Clerk clerk) {
  86. this.clerk = clerk;
  87. }
  88.  
  89. @Override
  90. public void run() {
  91. for (int i = 0; i < 20; i++) {
  92. try {
  93. Thread.sleep(200);
  94. } catch (InterruptedException e) {
  95. e.printStackTrace();
  96. }
  97.  
  98. clerk.get();
  99. }
  100. }
  101. }
  102.  
  103. // 消费者
  104. class Consumer implements Runnable {
  105.  
  106. private Clerk clerk;
  107.  
  108. public Consumer(Clerk clerk) {
  109. this.clerk = clerk;
  110. }
  111.  
  112. @Override
  113. public void run() {
  114. for (int i = 0; i < 20; i++) {
  115. clerk.sale();
  116. }
  117. }
  118.  
  119. }

Condition 控制线程通信

Condition
Condition 接口描述了可能会与锁有关联的条件变量。这些变量在用
法上与使用 Object.wait 访问的隐式监视器类似,但提供了更强大的
功能。需要特别指出的是,单个 Lock 可能与多个 Condition 对象关
联。为了避免兼容性问题,Condition 方法的名称与对应的 Object 版
本中的不同。
在 Condition 对象中,与 wait、notify 和 notifyAll 方法对应的分别是
await、signal 和 signalAll。
Condition 实例实质上被绑定到一个锁上。要为特定 Lock 实例获得
Condition 实例,请使用其 newCondition() 方法。

线程按序交替

线程按序交替
编写一个程序,开启 3 个线程,这三个线程的 ID 分别为
A、B、C,每个线程将自己的 ID 在屏幕上打印 10 遍,要
求输出的结果必须按顺序显示。
如:ABCABCABC...... 依次递归

  1. import java.util.concurrent.locks.Condition;
  2. import java.util.concurrent.locks.Lock;
  3. import java.util.concurrent.locks.ReentrantLock;
  4.  
  5. /*
  6. * 编写一个程序,开启 3 个线程,这三个线程的 ID 分别为 A、B、C,每个线程将自己的 ID 在屏幕上打印 10 遍,要求输出的结果必须按顺序显示。
  7. * 如:ABCABCABC…… 依次递归
  8. */
  9. public class TestABCAlternate {
  10.  
  11. public static void main(String[] args) {
  12. AlternateDemo ad = new AlternateDemo();
  13.  
  14. new Thread(new Runnable() {
  15. @Override
  16. public void run() {
  17.  
  18. for (int i = 1; i <= 20; i++) {
  19. ad.loopA(i);
  20. }
  21.  
  22. }
  23. }, "A").start();
  24.  
  25. new Thread(new Runnable() {
  26. @Override
  27. public void run() {
  28.  
  29. for (int i = 1; i <= 20; i++) {
  30. ad.loopB(i);
  31. }
  32.  
  33. }
  34. }, "B").start();
  35.  
  36. new Thread(new Runnable() {
  37. @Override
  38. public void run() {
  39.  
  40. for (int i = 1; i <= 20; i++) {
  41. ad.loopC(i);
  42.  
  43. System.out.println("-----------------------------------");
  44. }
  45.  
  46. }
  47. }, "C").start();
  48. }
  49.  
  50. }
  51.  
  52. class AlternateDemo{
  53.  
  54. private int number = 1; //当前正在执行线程的标记
  55.  
  56. private Lock lock = new ReentrantLock();
  57. private Condition condition1 = lock.newCondition();
  58. private Condition condition2 = lock.newCondition();
  59. private Condition condition3 = lock.newCondition();
  60.  
  61. /**
  62. * @param totalLoop : 循环第几轮
  63. */
  64. public void loopA(int totalLoop){
  65. lock.lock();
  66.  
  67. try {
  68. //1. 判断
  69. if(number != 1){
  70. condition1.await();
  71. }
  72.  
  73. //2. 打印
  74. for (int i = 1; i <= 1; i++) {
  75. System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + totalLoop);
  76. }
  77.  
  78. //3. 唤醒
  79. number = 2;
  80. condition2.signal();
  81. } catch (Exception e) {
  82. e.printStackTrace();
  83. } finally {
  84. lock.unlock();
  85. }
  86. }
  87.  
  88. public void loopB(int totalLoop){
  89. lock.lock();
  90.  
  91. try {
  92. //1. 判断
  93. if(number != 2){
  94. condition2.await();
  95. }
  96.  
  97. //2. 打印
  98. for (int i = 1; i <= 1; i++) {
  99. System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + totalLoop);
  100. }
  101.  
  102. //3. 唤醒
  103. number = 3;
  104. condition3.signal();
  105. } catch (Exception e) {
  106. e.printStackTrace();
  107. } finally {
  108. lock.unlock();
  109. }
  110. }
  111.  
  112. public void loopC(int totalLoop){
  113. lock.lock();
  114.  
  115. try {
  116. //1. 判断
  117. if(number != 3){
  118. condition3.await();
  119. }
  120.  
  121. //2. 打印
  122. for (int i = 1; i <= 1; i++) {
  123. System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + totalLoop);
  124. }
  125.  
  126. //3. 唤醒
  127. number = 1;
  128. condition1.signal();
  129. } catch (Exception e) {
  130. e.printStackTrace();
  131. } finally {
  132. lock.unlock();
  133. }
  134. }
  135.  
  136. }

ReadWriteLock 读写锁

读-写锁 ReadWriteLock
ReadWriteLock 维护了一对相关的锁,一个用于只读操作,
另一个用于写入操作。只要没有 writer,读取锁可以由
多个 reader 线程同时保持。写入锁是独占的。。
ReadWriteLock 读取操作通常不会改变共享资源,但执行
写入操作时,必须独占方式来获取锁。对于读取操作占
多数的数据结构。 ReadWriteLock 能提供比独占锁更高
的并发性。而对于只读的数据结构,其中包含的不变性
可以完全不需要考虑加锁操作

  1. import java.util.concurrent.locks.ReadWriteLock;
  2. import java.util.concurrent.locks.ReentrantReadWriteLock;
  3.  
  4. /*
  5. * 1. ReadWriteLock : 读写锁
  6. *
  7. * 写写/读写 需要“互斥”
  8. * 读读 不需要互斥
  9. *
  10. */
  11. public class TestReadWriteLock {
  12.  
  13. public static void main(String[] args) {
  14. ReadWriteLockDemo rw = new ReadWriteLockDemo();
  15.  
  16. new Thread(new Runnable() {
  17.  
  18. @Override
  19. public void run() {
  20. rw.set((int)(Math.random() * 101));
  21. }
  22. }, "Write:").start();
  23.  
  24. for (int i = 0; i < 100; i++) {
  25. new Thread(new Runnable() {
  26.  
  27. @Override
  28. public void run() {
  29. rw.get();
  30. }
  31. }).start();
  32. }
  33. }
  34.  
  35. }
  36.  
  37. class ReadWriteLockDemo{
  38.  
  39. private int number = 0;
  40.  
  41. private ReadWriteLock lock = new ReentrantReadWriteLock();
  42.  
  43. //读
  44. public void get(){
  45. lock.readLock().lock(); //上锁
  46.  
  47. try{
  48. System.out.println(Thread.currentThread().getName() + " : " + number);
  49. }finally{
  50. lock.readLock().unlock(); //释放锁
  51. }
  52. }
  53.  
  54. //写
  55. public void set(int number){
  56. lock.writeLock().lock();
  57.  
  58. try{
  59. System.out.println(Thread.currentThread().getName());
  60. this.number = number;
  61. }finally{
  62. lock.writeLock().unlock();
  63. }
  64. }
  65. }

线程八锁

一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用
其中的一个synchronized方法了,其它的线程都只能等待,换句话说,某一个时刻
内,只能有唯一一个线程去访问这些synchronized方法
锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的
synchronized方法
加个普通方法后发现和同步锁无关
换成两个对象后,不是同一把锁了,情况立刻变化。
都换成静态同步方法后,情况又变化
所有的非静态同步方法用的都是同一把锁——实例对象本身,也就是说如果一个实
例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获
取锁的方法释放锁后才能获取锁,可是别的实例对象的非静态同步方法因为跟该实
例对象的非静态同步方法用的是不同的锁,所以毋须等待该实例对象已获取锁的非
静态同步方法释放锁就可以获取他们自己的锁。
所有的静态同步方法用的也是同一把锁——类对象本身,这两把锁是两个不同的对
象,所以静态同步方法与非静态同步方法之间是不会有竞态条件的。但是一旦一个
静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取
锁,而不管是同一个实例对象的静态同步方法之间,还是不同的实例对象的静态同
步方法之间,只要它们同一个类的实例对象!11-线程池

  1. /*
  2. * 题目:判断打印的 "one" or "two" ?
  3. *
  4. * 1. 两个普通同步方法,两个线程,标准打印, 打印? //one two
  5. * 2. 新增 Thread.sleep() 给 getOne() ,打印? //one two
  6. * 3. 新增普通方法 getThree() , 打印? //three one two
  7. * 4. 两个普通同步方法,两个 Number 对象,打印? //two one
  8. * 5. 修改 getOne() 为静态同步方法,打印? //two one
  9. * 6. 修改两个方法均为静态同步方法,一个 Number 对象? //one two
  10. * 7. 一个静态同步方法,一个非静态同步方法,两个 Number 对象? //two one
  11. * 8. 两个静态同步方法,两个 Number 对象? //one two
  12. *
  13. * 线程八锁的关键:
  14. * ①非静态方法的锁默认为 this, 静态方法的锁为 对应的 Class 实例
  15. * ②某一个时刻内,只能有一个线程持有锁,无论几个方法。
  16. */
  17. public class TestThread8Monitor {
  18.  
  19. public static void main(String[] args) {
  20. Number number = new Number();
  21. Number number2 = new Number();
  22.  
  23. new Thread(new Runnable() {
  24. @Override
  25. public void run() {
  26. number.getOne();
  27. }
  28. }).start();
  29.  
  30. new Thread(new Runnable() {
  31. @Override
  32. public void run() {
  33. // number.getTwo();
  34. number2.getTwo();
  35. }
  36. }).start();
  37.  
  38. /*new Thread(new Runnable() {
  39. @Override
  40. public void run() {
  41. number.getThree();
  42. }
  43. }).start();*/
  44.  
  45. }
  46.  
  47. }
  48.  
  49. class Number{
  50.  
  51. public static synchronized void getOne(){//Number.class
  52. try {
  53. Thread.sleep(3000);
  54. } catch (InterruptedException e) {
  55. }
  56.  
  57. System.out.println("one");
  58. }
  59.  
  60. public synchronized void getTwo(){//this
  61. System.out.println("two");
  62. }
  63.  
  64. public void getThree(){
  65. System.out.println("three");
  66. }
  67.  
  68. }

线程池

第四种获取线程的方法:线程池,一个 ExecutorService,它使用可能的几个池线程之
一执行每个提交的任务,通常使用 Executors 工厂方法配置。

线程池可以解决两个不同问题:由于减少了每个任务调用的开销,它们通常可以在
执行大量异步任务时提供增强的性能,并且还可以提供绑定和管理资源(包括执行
任务集时使用的线程)的方法。每个 ThreadPoolExecutor 还维护着一些基本的统计数
据,如完成的任务数。
为了便于跨大量上下文使用,此类提供了很多可调整的参数和扩展钩子 (hook)。但
是,强烈建议程序员使用较为方便的 Executors 工厂方法 :

Executors.newCachedThreadPool()(无界线程池,可以进行自动线程回收)
Executors.newFixedThreadPool(int)(固定大小线程池)
Executors.newSingleThreadExecutor()(单个后台线程)
它们均为大多数使用场景预定义了设置。12-线程调度

  1. import java.util.ArrayList;
  2. import java.util.List;
  3. import java.util.concurrent.Callable;
  4. import java.util.concurrent.ExecutorService;
  5. import java.util.concurrent.Executors;
  6. import java.util.concurrent.Future;
  7.  
  8. /*
  9. * 一、线程池:提供了一个线程队列,队列中保存着所有等待状态的线程。避免了创建与销毁额外开销,提高了响应的速度。
  10. *
  11. * 二、线程池的体系结构:
  12. * java.util.concurrent.Executor : 负责线程的使用与调度的根接口
  13. * |--**ExecutorService 子接口: 线程池的主要接口
  14. * |--ThreadPoolExecutor 线程池的实现类
  15. * |--ScheduledExecutorService 子接口:负责线程的调度
  16. * |--ScheduledThreadPoolExecutor :继承 ThreadPoolExecutor, 实现 ScheduledExecutorService
  17. *
  18. * 三、工具类 : Executors
  19. * ExecutorService newFixedThreadPool() : 创建固定大小的线程池
  20. * ExecutorService newCachedThreadPool() : 缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量。
  21. * ExecutorService newSingleThreadExecutor() : 创建单个线程池。线程池中只有一个线程
  22. *
  23. * ScheduledExecutorService newScheduledThreadPool() : 创建固定大小的线程,可以延迟或定时的执行任务。
  24. */
  25. public class TestThreadPool {
  26.  
  27. public static void main(String[] args) throws Exception {
  28. //1. 创建线程池
  29. ExecutorService pool = Executors.newFixedThreadPool();
  30.  
  31. List<Future<Integer>> list = new ArrayList<>();
  32.  
  33. for (int i = ; i < ; i++) {
  34. Future<Integer> future = pool.submit(new Callable<Integer>(){
  35.  
  36. @Override
  37. public Integer call() throws Exception {
  38. int sum = ;
  39.  
  40. for (int i = ; i <= ; i++) {
  41. sum += i;
  42. }
  43.  
  44. return sum;
  45. }
  46.  
  47. });
  48.  
  49. list.add(future);
  50. }
  51.  
  52. pool.shutdown();
  53.  
  54. for (Future<Integer> future : list) {
  55. System.out.println(future.get());
  56. }
  57.  
  58. /*ThreadPoolDemo tpd = new ThreadPoolDemo();
  59.  
  60. //2. 为线程池中的线程分配任务
  61. for (int i = 0; i < 10; i++) {
  62. pool.submit(tpd);
  63. }
  64.  
  65. //3. 关闭线程池
  66. pool.shutdown();*/
  67. }
  68.  
  69. // new Thread(tpd).start();
  70. // new Thread(tpd).start();
  71.  
  72. }
  73.  
  74. class ThreadPoolDemo implements Runnable{
  75.  
  76. private int i = ;
  77.  
  78. @Override
  79. public void run() {
  80. while(i <= ){
  81. System.out.println(Thread.currentThread().getName() + " : " + i++);
  82. }
  83. }
  84.  
  85. }

线程调度

一个 ExecutorService,可安排在给定的延迟后运行或定
期执行的命令。

  1. import java.util.Random;
  2. import java.util.concurrent.Callable;
  3. import java.util.concurrent.Executors;
  4. import java.util.concurrent.Future;
  5. import java.util.concurrent.ScheduledExecutorService;
  6. import java.util.concurrent.TimeUnit;
  7.  
  8. /*
  9. * 一、线程池:提供了一个线程队列,队列中保存着所有等待状态的线程。避免了创建与销毁额外开销,提高了响应的速度。
  10. *
  11. * 二、线程池的体系结构:
  12. * java.util.concurrent.Executor : 负责线程的使用与调度的根接口
  13. * |--**ExecutorService 子接口: 线程池的主要接口
  14. * |--ThreadPoolExecutor 线程池的实现类
  15. * |--ScheduledExecutorService 子接口:负责线程的调度
  16. * |--ScheduledThreadPoolExecutor :继承 ThreadPoolExecutor, 实现 ScheduledExecutorService
  17. *
  18. * 三、工具类 : Executors
  19. * ExecutorService newFixedThreadPool() : 创建固定大小的线程池
  20. * ExecutorService newCachedThreadPool() : 缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量。
  21. * ExecutorService newSingleThreadExecutor() : 创建单个线程池。线程池中只有一个线程
  22. *
  23. * ScheduledExecutorService newScheduledThreadPool() : 创建固定大小的线程,可以延迟或定时的执行任务。
  24. */
  25. public class TestScheduledThreadPool {
  26.  
  27. public static void main(String[] args) throws Exception {
  28. ScheduledExecutorService pool = Executors.newScheduledThreadPool();
  29.  
  30. for (int i = ; i < ; i++) {
  31. Future<Integer> result = pool.schedule(new Callable<Integer>(){
  32.  
  33. @Override
  34. public Integer call() throws Exception {
  35. int num = new Random().nextInt();//生成随机数
  36. System.out.println(Thread.currentThread().getName() + " : " + num);
  37. return num;
  38. }
  39.  
  40. }, , TimeUnit.SECONDS);
  41.  
  42. System.out.println(result.get());
  43. }
  44.  
  45. pool.shutdown();
  46. }
  47.  
  48. }

ForkJoinPool 分支/合并框架 工作窃取

Fork/Join 框架:就是在必要的情况下,将一个大任务,进行拆分(fork)成
若干个小任务(拆到不可再拆时),再将一个个的小任务运算的结果进
行 join 汇总。

Fork/Join 框架与线程池的区别
采用 “工作窃取”模式(work-stealing):
当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加
到线程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队
列中。
相对于一般的线程池实现,fork/join框架的优势体现在对其中包含的任务
的处理方式上.在一般的线程池中,如果一个线程正在执行的任务由于某些
原因无法继续运行,那么该线程会处于等待状态。而在fork/join框架实现中,
如果某个子问题由于等待另外一个子问题的完成而无法继续运行。那么处理
该子问题的线程会主动寻找其他尚未运行的子问题来执行.这种方式减少了
线程的等待时间,提高了性能。

  1. import java.time.Duration;
  2. import java.time.Instant;
  3. import java.util.concurrent.ForkJoinPool;
  4. import java.util.concurrent.ForkJoinTask;
  5. import java.util.concurrent.RecursiveTask;
  6. import java.util.stream.LongStream;
  7.  
  8. import org.junit.Test;
  9.  
  10. public class TestForkJoinPool {
  11.  
  12. public static void main(String[] args) {
  13. Instant start = Instant.now();
  14.  
  15. ForkJoinPool pool = new ForkJoinPool();
  16.  
  17. ForkJoinTask<Long> task = new ForkJoinSumCalculate(0L, 50000000000L);
  18.  
  19. Long sum = pool.invoke(task);
  20.  
  21. System.out.println(sum);
  22.  
  23. Instant end = Instant.now();
  24.  
  25. System.out.println("耗费时间为:" + Duration.between(start, end).toMillis());//166-1996-10590
  26. }
  27.  
  28. @Test
  29. public void test1(){
  30. Instant start = Instant.now();
  31.  
  32. long sum = 0L;
  33.  
  34. for (long i = 0L; i <= 50000000000L; i++) {
  35. sum += i;
  36. }
  37.  
  38. System.out.println(sum);
  39.  
  40. Instant end = Instant.now();
  41.  
  42. System.out.println("耗费时间为:" + Duration.between(start, end).toMillis());//35-3142-15704
  43. }
  44.  
  45. //java8 新特性
  46. @Test
  47. public void test2(){
  48. Instant start = Instant.now();
  49.  
  50. Long sum = LongStream.rangeClosed(0L, 50000000000L)
  51. .parallel()
  52. .reduce(0L, Long::sum);
  53.  
  54. System.out.println(sum);
  55.  
  56. Instant end = Instant.now();
  57.  
  58. System.out.println("耗费时间为:" + Duration.between(start, end).toMillis());//1536-8118
  59. }
  60.  
  61. }
  62.  
  63. class ForkJoinSumCalculate extends RecursiveTask<Long>{
  64.  
  65. /**
  66. *
  67. */
  68. private static final long serialVersionUID = -259195479995561737L;
  69.  
  70. private long start;
  71. private long end;
  72.  
  73. private static final long THURSHOLD = 10000L; //临界值
  74.  
  75. public ForkJoinSumCalculate(long start, long end) {
  76. this.start = start;
  77. this.end = end;
  78. }
  79.  
  80. @Override
  81. protected Long compute() {
  82. long length = end - start;
  83.  
  84. if(length <= THURSHOLD){
  85. long sum = 0L;
  86.  
  87. for (long i = start; i <= end; i++) {
  88. sum += i;
  89. }
  90.  
  91. return sum;
  92. }else{
  93. long middle = (start + end) / ;
  94.  
  95. ForkJoinSumCalculate left = new ForkJoinSumCalculate(start, middle);
  96. left.fork(); //进行拆分,同时压入线程队列
  97.  
  98. ForkJoinSumCalculate right = new ForkJoinSumCalculate(middle+, end);
  99. right.fork(); //
  100.  
  101. return left.join() + right.join();
  102. }
  103. }
  104.  
  105. }

Java Juc学习笔记的更多相关文章

  1. 尚学堂JAVA基础学习笔记

    目录 尚学堂JAVA基础学习笔记 写在前面 第1章 JAVA入门 第2章 数据类型和运算符 第3章 控制语句 第4章 Java面向对象基础 1. 面向对象基础 2. 面向对象的内存分析 3. 构造方法 ...

  2. JUC学习笔记(五)

    JUC学习笔记(一)https://www.cnblogs.com/lm66/p/15118407.html JUC学习笔记(二)https://www.cnblogs.com/lm66/p/1511 ...

  3. JUC学习笔记(四)

    JUC学习笔记(一)https://www.cnblogs.com/lm66/p/15118407.html JUC学习笔记(二)https://www.cnblogs.com/lm66/p/1511 ...

  4. JUC学习笔记(二)

    JUC学习笔记(一)https://www.cnblogs.com/lm66/p/15118407.html 1.Lock接口 1.1.Synchronized 1.1.1.Synchronized关 ...

  5. JUC学习笔记——进程与线程

    JUC学习笔记--进程与线程 在本系列内容中我们会对JUC做一个系统的学习,本片将会介绍JUC的进程与线程部分 我们会分为以下几部分进行介绍: 进程与线程 并发与并行 同步与异步 线程详解 进程与线程 ...

  6. JUC学习笔记——共享模型之管程

    JUC学习笔记--共享模型之管程 在本系列内容中我们会对JUC做一个系统的学习,本片将会介绍JUC的管程部分 我们会分为以下几部分进行介绍: 共享问题 共享问题解决方案 线程安全分析 Monitor ...

  7. JUC学习笔记——共享模型之内存

    JUC学习笔记--共享模型之内存 在本系列内容中我们会对JUC做一个系统的学习,本片将会介绍JUC的内存部分 我们会分为以下几部分进行介绍: Java内存模型 可见性 模式之两阶段终止 模式之Balk ...

  8. 20145213《Java程序设计学习笔记》第六周学习总结

    20145213<Java程序设计学习笔记>第六周学习总结 说在前面的话 上篇博客中娄老师指出我因为数据结构基础薄弱,才导致对第九章内容浅尝遏止地认知.在这里我还要自我批评一下,其实我事后 ...

  9. [原创]java WEB学习笔记95:Hibernate 目录

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

随机推荐

  1. 【原】docker基础(一)

    1.架构 2.说明 Docker daemon( Docker守护进程):Docker daemon是一个运行在宿主机( DOCKER-HOST)的后台进程.可通过 Docker客户端与之通信. Cl ...

  2. Codeforces Round #622 (Div. 2)

    A: 题意: 有ABC的三种菜,现在有a个A,b个B,c个C,问能组成多少种不同菜单 思路: abc都大于等于4,肯定是7种,给abc排个序,从大到小举例删减 #include<bits/std ...

  3. go基础_数组

    数组有2种赋值方式 一种明确指定长度,另一种从赋值数目指定长度 package main import "fmt" func main() { //数组赋值方式1,指定长度 arr ...

  4. MyBatis-Spring整合之方式2

    提前叨叨:此方法优化了上一个方式的事务支持,同时简化了一个bean的配置 1.在方式1的基础上修改UserDaoImp文件,改用使用继承SqlSessionDaoSupport的方式.代码如下: pu ...

  5. MyBatis-Spring整合之方式1

    导入相关包:Spring包:Spring架包 MyBatis包:MyBatis架包 整合包:Mybatis-Spring整合包 编写实体类User,实体类的sql映射文件,映射内容如下: <?x ...

  6. 全局下的isFinite

     isFinite() 函数用于检查其参数是否是无穷大 1. 他是一个全局对象,可以在js代码中直接使用 2. isFinite() 函数用于检查其参数是否是无穷大. 3. 如果 number 是有限 ...

  7. 安卓基础(Navigation)

    今天学习了简单的Navigation:页面导航. 页面导航的简单例子: MainAcitivity: package com.example.navigation; import android.su ...

  8. eclipse链接mySQL数据库常见错误

    1错误: 解决: 2,用户名输入错误 解决:查看自己的正确用户名https://zhidao.baidu.com/question/248308313.html 3. 解决: 链接示例:https:/ ...

  9. Yii2掉index.php?r=

    普通 首先确认apache2配置 1. 开启 apache 的 mod_rewrite 模块 去掉LoadModule rewrite_module modules/mod_rewrite.so前的“ ...

  10. YouTube为创作者提供了更多赚钱的途径

    编辑 | 于斌 出品 | 于见(mpyujian) 大家提到YouTube可能还有些陌生,只是听说过,但因为一些原因并没有实际应用过,但其实YouTube就是设立在美国的一个视频分享网站,让使用者上载 ...