Java多线程/并发编程学习笔记

一、Java多线程入门

Tips

点击标题跳转到笔记文档

学习资源分享点这里

1、线程基础

2、线程安全

3、Java内存模型

4、锁

5、线程池

6、并发基础

7、阻塞队列

附:代码示例

二、Java 多线程进阶

8. 并发工具类

CountDownLatch

/**
 * 同步计数器:CountDownLatch, 同步计数器,等待一组线程(某个任务)全部执行完成
 * <p>
 * 实际应用场景:使用多线程上传多个文件时,需要等所有线程都上传完成,任务才算完成。
 * Created by PingWurth
 * 2019-03-18 15:32
 */
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        // 同步计数器初始值为 3,执行 countDownLatch.await() 的线程会一直等待
        // 直到 countDownLatch.countDown() 被调用 3 次,才被唤醒
        CountDownLatch countDownLatch = new CountDownLatch(3);
        // 传递 countDownLatch,用于在线程结束时调用 countDownLatch.countDown()
        Worker worker1 = new Worker("工作线程1", countDownLatch);
        Worker worker2 = new Worker("工作线程2", countDownLatch);
        Worker worker3 = new Worker("工作线程3", countDownLatch);
        worker1.start();
        worker2.start();
        worker3.start();
        countDownLatch.await();// 当前线程阻塞,直到 countDownLatch 的计数器为零
        System.out.println("main end.");
    }

    static class Worker extends Thread {
        private String workerName;
        private CountDownLatch countDownLatch;

        public Worker(String workerName, CountDownLatch countDownLatch) {
            this.workerName = workerName;
            this.countDownLatch = countDownLatch;
        }

        @Override
        public void run() {
            System.out.println(workerName + " 开始上传文件...");
            try {
                TimeUnit.SECONDS.sleep((long) (Math.random() * 10));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(workerName + " 上传完成...");
            countDownLatch.countDown();// 每个线程工作结束后,同步计数器减1
        }
    }
}

CyclicBarrier

/**
 * 循环屏障: CyclicBarrier
 * <p>
 *     允许一组线程互相等待,直到最后一个线程到达屏障时,
 *     屏障才会开门,所有被屏障拦截的线程才会继续运行
 * Created by PingWurth
 * 2019-03-18 16:00
 */
public class CyclicBarrierDemo {
    public static void main(String[] args) {
        // 可以指定屏障打开后优先执行某个线程
        CyclicBarrier cyclicBarrier = new CyclicBarrier(3, new TotalTask());
        BillTask worker1 = new BillTask("bill1", cyclicBarrier);
        BillTask worker2 = new BillTask("bill2", cyclicBarrier);
        BillTask worker3 = new BillTask("bill3", cyclicBarrier);
        worker1.start();
        worker2.start();
        worker3.start();
        System.out.println("main end.");
    }

    static class TotalTask extends Thread {
        @Override
        public void run() {
            System.out.println("所有线程到达指定屏障,芝麻开门。。。");
        }
    }

    static class BillTask extends Thread {
        private String billName;
        private CyclicBarrier cyclicBarrier;
        public BillTask(String billName, CyclicBarrier cyclicBarrier) {
            this.billName = billName;
            this.cyclicBarrier = cyclicBarrier;
        }

        @Override
        public void run() {
            try {
                System.out.println(billName + " 开始计算");
                TimeUnit.SECONDS.sleep((long)(Math.random() * 10));
                System.out.println(billName + " 计算完成,等待其他线程计算完成...");
                // 假设继续运行需要依赖其他线程的计算结果,所以等待
                cyclicBarrier.await();
                System.out.println(billName  + " 继续运行!!!");
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }
}

Semaphore

/**
 * 信号量:Semaphore
 *
 *     -> 用来控制同时访问特定资源的线程数量
 *
 * Created by PingWurth
 * 2019-03-18 19:03
 */
public class SemaphoreDemo {
    public static void main(String[] args) {
        /**
         * 场景模拟:红绿灯路口有3个车道,有10辆车要通过路口。
         *           以停车线为准,车行驶至停车线即获得通过许可,
         *           可以通过路口,当一辆车完全通过停车线后,下一辆车就能行驶至停车线(获得通过许可)。
         *           在同一时刻,停车线上永远都只有3辆车通过。
         *           new Semaphore(3, true); true为可选参数(公平设置:先获得许可的先释放),
         */
        final Semaphore semaphore = new Semaphore(3, true);
        for (int i = 0; i < 10; i++) {
            final int no = i;
            Runnable thread = new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println("car" + no + " 等待通过路口");
                        TimeUnit.SECONDS.sleep(1);
                        // 继续往下运行需要获取 semaphore 的许可
                        semaphore.acquire();
                        System.out.println("car" + no + " 获得许可,正在通过路口...");
                        TimeUnit.SECONDS.sleep((long)(Math.random() * 10));
                        // 许可释放
                        semaphore.release();
                        System.out.println("car" + no + " 已通过路口!!!");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            new Thread(thread).start();
        }
        System.out.println("main end...");
    }
}

Exchanger

/**
 * Exchanger: 用于线程间数据交换.
 * <p>
 *     用于线程间数据交换:允许两个线程之间定义同步点,
 *     当两个线程都到达同步点时,它们交换数据.
 * Created by PingWurth
 * 2019-03-18 19:57
 */
public class ExchangerDemo {
    private static final Exchanger<String> exchanger = new Exchanger<String>();
    private static ExecutorService threadPool = Executors.newFixedThreadPool(2);

    public static void main(String[] args) {
        threadPool.execute(() -> {
            String strA = Thread.currentThread().getName() + "的数据";
            String strB = "";
            try {
                System.out.println(Thread.currentThread().getName() + "先执行了 exchange(要交换的数据)");
                System.out.println("...等待另一个线程执行 exchange, 以便完成数据交换");
                strB = exchanger.exchange(strA, 10, TimeUnit.SECONDS);
            } catch (InterruptedException | TimeoutException e) { e.printStackTrace(); }

            System.out.println(Thread.currentThread().getName() + "获得了" + strB);
        });

        threadPool.execute(() -> {
            String strB = Thread.currentThread().getName() + "的数据";
            String strA = "";
            try {
                TimeUnit.SECONDS.sleep(4);
                strA = exchanger.exchange(strB, 10, TimeUnit.SECONDS);
            } catch (InterruptedException | TimeoutException e) { e.printStackTrace(); }

            System.out.println(Thread.currentThread().getName() + "获得了" + strA);
        });
    }
}

9. 线程安全容器

CopyOnWriteArrayList (代替 ArrayList)

ConcurrentLinkedQueue(代替 LinkedList)

ConcurrentHashMap(代替 HashMap)

ConcurrentSkipListMap(代替 TreeMap)

10. 原子类

11. final

12. ThreadLocal

ThreadLocal 结构

ThreadLocal 的弱引用

扩展 —— 关于引用

13. 深入线程池

线程数添加规则

14. 查缺补漏

Timer 定时器

Fork-Join 框架

  • 代码示例

/**
 * Fork-Join 框架实现 一个计算任务分配多个工作线程完成.
 * <p>
 * @author PingWurth
 * @date 2019-03-18 20:28
 */
public class ForkJoinTest {
    public static void main(String[] args) {
        // 创建一个长度为 SIZE 的随机数组
        final int SIZE = 10000000;
        double[] numbers = new double[SIZE];
        for (int i = 0; i < SIZE; i++) {
            numbers[i] = Math.random();
        }

        // 使用 Fork-Join 需要提供一个 RecursiveTask<T> 或 RecursiveAction,
        // 前者是需要返回结果时使用,后者不需要返回结果
        // 一般通过继承并实现 RecursiveTask 类的 compute() 方法来构建
        Counter counter = new Counter(numbers, 0, numbers.length, x -> x > 0.5);
        ForkJoinPool pool = new ForkJoinPool();
        pool.invoke(counter);

        // 输出计算结果
        System.out.println(counter.join());
    }

    static class Counter extends RecursiveTask<Integer> {

        public static final int THRESHOLD = 1000;
        private double[] values;
        private int from;
        private int to;
        private DoublePredicate filter;

        public Counter(double[] values, int from, int to, DoublePredicate filter) {
            this.values = values;
            this.from = from;
            this.to = to;
            this.filter = filter;
        }

        @Override
        protected Integer compute() {
            // 自定义一个阈值 THRESHOLD,在阈值范围内,执行计算任务
            // 超过阈值,任务继续分解,递归创建任务对象 RecursiveTask
            if (to - from < THRESHOLD) {
                int count = 0;
                for (int i = from; i < to; i++) {
                    if (filter.test(values[i])) count++;
                }
                return count;
            } else {
                int mid = (from + to) / 2;
                Counter first = new Counter(values, from, mid, filter);
                Counter second = new Counter(values, mid, to, filter);
                // 将任务 push 到队列中
                invokeAll(first, second);
                // 合并任务结果并返回
                return first.join() + second.join();
            }
        }
    }
}

常用线程安全类

三、深入底层原理

15. 并发特性

三个特性

先行发生原则

指令重排序

机器级的优化操作,CPU 允许将多条指令不按程序规定的顺序分开发送给各相应电路单元处理。

16. volatile

Java 内存模型

情景分析 —— DCL (Double Check Lock) 单例模式

17. synchronized

使用 synchronized

关于 monitor 对象

monitorenter 详解

  

monitorexit 详解

锁优化

  • 自旋锁

  • 锁消除

  • 锁粗化

  • 偏向锁

  • 轻量级锁

自旋锁

锁消除

锁粗化

偏向锁

轻量级锁

锁的工作流程(完整版)

synchronized 总结

18. ReentrantLock

使用 ReentrantLock

ReentrantLock 底层原理

ReentrantLock 中所有的方法都是通过调用 java.util.concurrent.locks.ReentrantLock.Sync 实例的方法实现的。

而 Sync 除了少数几个定制化的功能之外,其他方法都是通过继承 AbstractQueuedSynchronizer 获得,下面简称 AQS

AQS 是什么?

AQS 的节点(Node)

AQS 三要素

ReentrantLock 源码解析

ReentrantLock.lock

AbstractQueuedSynchronizer.acquire

ReentrantLock.unlock

ReentrantLock 总结

扩展 —— 锁的分类

四、并发编程框架 Disruptor

认识 Disruptor

核心概念

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值