Java并发编程的艺术笔记

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/caoxiaohong1005/article/details/83212792

多线程总结

  • Java内存模型
    • volatile
      • 重排序规则[不允许重排序]
        • volatile读+任何操作
        • 任何操作+volatile写
        • volatile写+volatile读
    • ReentrantLock
      • 实现依赖于Java同步器框架AQS
      • AQS使用一个整型的volatile变量(命名为state)来维护同步状态
      • 这个volatile变量是ReentrantLock内存语义的关键
    • concurrent包下的源码通用模式
      • 声明共享变量为volatile
      • 使用CAS的原子条件更新来实现线程间的同步
      • 配合以volatile的读/写和CAS所具有的volatile读和写的内存语义来实现线程之间的通信。
      • concurrent包的实现示意图
    • DCL方式中singleton被设定为volatile的作用
      • 如果没有volatile修饰singleton,多线程情况下可能导致后来的线程读取了一个未初始化完成的singleton对象 [过程2和3重排序了]
      • 添加了volatile后,保证了后来的线程读取到的singleton对象一定是初始化完成的。原理是volatile禁止了singleton的引用指向内存空间的操作和初始化操作的重排序。[禁止2和3重排序
      • 一个线程生成一个singleton的过程
        1.分配对象的内存空间
        2.初始化对象
        3.设置singleton引用指向内存地址
        4.初次访问singleton对象
    • 线程
      • 6种状态
        • new
          构建完成,但未调用start()方法
        • runnable
          就绪+运行的统称
        • blocked
          阻塞于锁
        • waiting
          进入等待状态,需要等待其它线程做一些特定动作(通知or中断)
        • time_waiting
          超时等待
        • terminated
          线程执行完毕
      • 线程间通信方式
        • volatile和synchronized关键字

        • wait()和notify()、notifyAll()机制

          • wait和notify*()方法调用时,需要先对对象加锁
          • 调用对象的wait()方法后,
            1.线程状态由running变为waiting;
            2.当前线程被放到等待队列里面;
            3.释放对象的锁
          • notify()将同步队列中的一个线程移到阻塞队列中;notifyAll()方法将同步队列中的所有线程移到阻塞队列中。
            ps:线程从同步队列到阻塞队列的状态变化:waiting->blocked
          • 处于阻塞队列中的线程在获取对象的监视器Monitor上,具有同等的优先级,且最终只能有一个线程获取成功。
          • 处于blocked状态的线程只有获取了对象的Monitor后,才能从wait()方法返回。
        • 等待、通知的经典模式[生产者、消费者模式]

          • 实质就是wait和notify的使用

          • 生产者伪代码

               Synchronized(对象) {
              	// 更改条件代码
              	// 唤醒等待线程
              	对象.notifyAll();
                }
            
          • 消费者伪代码

                Synchronized(对象) {
              	 while (条件不满足) {
              		对象.wait(); // 释放对象锁
              	 }
              	 // 处理逻辑代码
                } 
            
        • 管道输入、输出流

            import java.io.IOException;
            import java.io.PipedReader;
            import java.io.PipedWriter;
            import java.util.Scanner;
          
            public class Main {
          
                public static void main(String[] args) throws IOException {
                    Scanner scanner = new Scanner(System.in);
                    PipedWriter producter = new PipedWriter(); //1.将信息读入管道
                    PipedReader consummer = new PipedReader(); //2.从管道读取信息
                    producter.connect(consummer); //3.绑定
          
                    Thread readThread = new Print(consummer);
                    readThread.start();
                    int resive = 0;
                    try {
                        while ((resive = scanner.nextInt()) != -1) {
                            producter.write(resive);
                        }
                    } catch (Exception e) {
          
                    } finally {
                        producter.close();
                    }
          
                }
          
                static class Print extends Thread {
                    private PipedReader pipedReader;
          
                    Print(PipedReader pipedReader) {
                        this.pipedReader = pipedReader;
                    }
          
                    @Override
                    public void run() {
                        int resive = 0;
                        try {
                            while ((resive = pipedReader.read()) != -1) {
                                System.out.println(resive);
                            }
                        } catch (IOException e) {
          
                        }
                    }
                }
            }
          
        • Thread.join的使用

          • 思想
            主线程main执行过程中,启动了一个新的线程thread,并调用了thread.join()方法,则thead必须等待main线程执行完后,才从其join()方法返回。
          • 线程终止时,会主动调用notifyAll()方法,通知所有等待在改线程对象上的线程。
          • join源码的逻辑结构和生产者/消费者模式一样:
            加锁+循环+循环后的逻辑处理。
        • ThreadLocal的使用

    • 线程池
      • 使用线程池的优势

        • 消除了频繁创建和消亡线程的系统资源开销
        • 提高相应速度
        • 提高线程的可管理性
      • ThreadPoolExecutor构造函数的参数 [4个构造函数]

        • 公用
          • int corePoolSize
          • int maximumPoolSize
          • long keepAliveTime
            超过核心线程个数的那些空闲线程最长的存活时间
          • TimeUnit unit
          • BlockingQueue workQueue
        • 非公有
          • ThreadFactory threadFactory
            用于executor创建新线程
          • RejectedExecutionHandler handler
      • 处理流程
        核心线程池是否满了->任务队列是否满了->线程池是否满了->抛弃策略

      • 创建线程池+提交任务+关闭线程池 code

          // 方式1    
          ThreadPoolExecutor executor = new ThreadPoolExecutor(3,10,100, TimeUnit.MILLISECONDS,null);
          Thread task = new Thread();
          executor.execute(task); // 向线程池提交任务.ps:任务类型必须是thread  
          executor.shutdown();// 关闭线程池  
            
          // 方式2  
          ExecutorService executorService = Executors.newFixedThreadPool(10);
          executorService.submit(task);
          executorService.shutdown();   
        
      • Executors.newFixedThreadPool方式默认使用LinkedBlockingQueue作为任务队列

      • Executors.newSingleThreadExecutor()方式默认使用LinkedBlockingQueue作为任务队列

      • Executors.newCachedThreadPool()方式默认使用SynchronousQueue作为任务队列

      • 阻塞队列的介绍

        • ArrayBlockingQueue
          基于数组结构的有界阻塞队列,按照FIFO对元素排序
        • LinkedBlockingQueue
          • 基于链表结构的无界[int最大值]阻塞队列,按照FIFO对元素排序,
          • 吞吐量 > ArrayBlockingQueue
        • SynchronousQueue
          • 一个不存储元素的有界阻塞队列。没有移除操作,则插入操作处于阻塞状态;有移除操作,才进行插入操作。
          • 吞吐量 > LinkedBlockingQueue
    • Executor框架
      • java线程和OS内核线程的对应关系:一对一
      • java线程启动时,会创建一个OS的线程;当java线程终止时,OS线程也被回收
      • 任务的两级调度模型
        在这里插入图片描述
      • Executor框架的使用示意图
        在这里插入图片描述
      • ScheduledThreadPoolExecutor
        • 继承自ThreadPoolExecutor
        • 用途
          • 定期执行任务
          • 按给定的延迟执行任务
        • 对ThreadPoolExecutor的改进
          • 任务队列使用DelayQueue
          • 获取任务方式不同
            可能添加了放入condition的操作
          • 执行周期任务后,增加了额外的处理
            从队列取出任务后,修改time的值再放入队列
        • DelayQueue中对象RunnableScheduledFutur的属性
          • long型time,表示任务要被执行的具体时间
          • long型sequenceNumber,表示添加到ScheduledThreadPoolExecutor的序号
          • long型period,表示任务执行的间隔周期
        • DelayQueue底层实现为PriorityQueue
          优先级排序规则:time->sequenceNumber依次升序
        • ScheduledThreadPoolExecutor获取任务的过程
          在这里插入图片描述
        • ScheduledThreadPoolExecutor添加任务的过程
          在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/caoxiaohong1005/article/details/83212792