Java多线程小札

概念

  • 进程
    • 进程间通信方式
      • 共享内存
      • 消息传递 Emailbox机制
    • 进程状态
      • PCB负责记录进程信息
      • 创建,就绪,运行,等待,消亡
        • 就绪 意味着只差cpu了
        • 等待 即程序运行缺少非CPU资源的依赖
  • 同步和异步
    • 同步 需要等待依赖于其他程序或资源才能完成本次执行
    • 非阻塞 无需待续结果,立即返回
    • 信号量 实现进程同步的方式
  • 并发(Concurrency)和并行(Parallelism)
    • 并发偏重于多个任务交替执行
    • 在多个CPU中可以同时运行(并行)
    • 高并发 保证系统能够同时并行处理很多请求
  • 临界区
    • 表示一种公共资源或者说是共享数据
    • 每一次,只能有一个线程使用
    • 并行程序中,临界区资源是保护的对象

多线程

  • 使用方式
    • Thread类
      1. 继承该类,重写run方法
      2. 实例start方法启动线程,
      • 以随机时间来调用线程中的run方法
    • 实现Runnable接口
    • 线程池
  • 实例变量与线程安全
    • synchronized 保证任意时刻只能有一个线程执行该方法
    • 利用 AtomicInteger 类
  • 常用方法
    • currentThread()
      • 返回对当前正在执行的线程对象的引用
    • getId()
    • getName()
    • getPriority()
    • isAlive()
      • 测试这个线程是否还处于活动状态
    • sleep(long millis)
      • 使当前正在执行的线程以指定的毫秒数“休眠”(暂时停止执行)
    • interrupt()
      • 标记中断状态为true,并不会影响线程的继续执行
      • 若中断后调用了wait、jion、sleep方法中的一种(即线程状态转移),抛出InterruptedException异常,且中断标志被清除
    • interrupted() 和isInterrupted()
    • setName(String name)
    • isDaemon()
      • 测试线程是否是守护线程
    • setDaemon(boolean on)
    • join()
      • 等待该线程终止
      • 主线程需要等待子线程执行完成之后再结束
    • yield()
      • 放弃当前的CPU资源
    • setPriority(int newPriority)
  • 分类
    • 用户线程 在前台执行具体的任务
    • 守护线程 在后台为其他前台线程服务
    • 特点 一旦所有用户线程都结束运行,守护线程会随JVM一起结束工作
    • 应用
      • 常见的守护线程:垃圾回收线程
      • 数据库连接池中的检测线程,JVM虚拟机启动后的检测线程
  • 线程操作
    • 如何停止一个线程
      • 使用interrupt方法
      • 使用return停止线程
      • 线程的优先级

synchronized 关键字

  • 变量安全性
    • 两个线程同时操作对象中的实例变量,则会出现“非线程安全”
    • 解决办法就是在方法前加上synchronized关键字
    • synchronized取得的锁都是对象锁,而不是把一段代码或方法当做锁
  • 可重入锁
    • 自己可以再次获取自己的内部锁
    • 可重入锁也支持在父子类继承的环境中
    • 同步不具有继承性
  • 同步语句块
    • 当一个线程访问一个对象的synchronized同步代码块时,另一个线程任然可以访问该对象非synchronized同步代码块
    • 不在synchronized代码块中就异步执行,在synchronized代码块中就是同步执行
    • 若两个线程使用了同一个“对象监视器”,运行结果是同步的
  • 类对象同步
    • synchronized关键字加到static静态方法和synchronized(class)代码块都是给class类上锁
    • synchronized关键字加到非静态方法上是给对象上锁
    • 在Jvm中具有String常量池缓存功能

volatile关键字

  • 特性
    • 定义
      • volatile 指示jvm变量是不稳定的,每次使用它都到主存中进行读取
      • volatile关键字声明的变量或对象通常具有与优化、多线程相关的特殊属性
      • 无volatile关键字,编译器可能优化读取和存储,将出现数据不一致的现象
      • 在C中,volatile关键字可以用来提醒编译器它后面所定义的变量随时有可能改变,都会直接从变量地址中读取
    • 可见性
      • Java的内存模型是如何读取变量
        • volatile 修饰的成员变量在每次被线程
          • 访问时 强迫从主存(共享内存)中重读该成员变量的值
          • 在变量发生变化时 强迫线程将变化值回写到主存(共享内存)
          • 目的 保证同步数据的可见性
      • while死循环中加入输出语句或sleep方法会出现什么效果
        • jvm尽可能保证内存的可见性
    • volatile 关键字无法保证对变量的原子性
    • 要保证数据的原子性还是要使用synchronized关键字
  • synchronized和volatile之比较
    • 运行效率
      • volatile关键字是线程同步的轻量级实现
      • volatile关键字只能用于变量而synchronized关键字可以修饰方法以及代码块
    • 原子性
      • volatile关键字能保证数据的可见性,但不能保证数据的原子性
      • synchronized关键字两者都能保证
    • 是否会产生阻塞
      • 多线程访问volatile关键字不会发生阻塞
      • synchronized关键字可能会发生阻塞
    • 处理问题
      • volatile关键字用于解决变量在多个线程之间的可见性
      • synchronized关键字解决的是多个线程之间访问资源的同步性

wait/notify

  • 等待/通知机制
    • 需求
      • 若轮询时间的间隔太小会浪费CPU资源,太大可能取不到自己想要的数据
    • 不使用等待/通知机制
      • 两个线程之间存在生产和消费关系
        • 第一个线程(生产者)做相应的操作然后第二个线程(消费者)感知到变化又进行相应的操作
    • 机制定义
      1. 线程A调用了对象O的wait()方法进入等待状态
      2. 另一个线程B调用了对象O的notify()或notifyAll()方法
      3. 线程A收到通知后退出等待队列,进入可运行状态,进而执行后续操作
    • 相关方法
      • notify() 随机唤醒等待队列中等待同一共享资源的一个线程,退出等待队列
      • notifyAll() 使所有正在等待队列中等待同一共享资源的全部线程退出等待队列,进入可运行状态
      • wait() 使用该方法的线程释放共享资源锁,从运行状态退出,进入等待队列,直到被再次唤醒
    • 方法使用
      • Java为每个Object都实现了等待/通知(wait/notify)机制的相关方法,必须用在synchronized关键字同步的Object的临界区内
        1. 调用wait()方法可以使处于临界区内的线程进入等待状态,同时释放被同步对象的锁
        2. notify()方法可以唤醒一个因调用wait操作而处于阻塞状态中的线程,使其进入就绪状态
        3. 重新唤醒的线程会视图重新获得临界区的控制权也就是锁
    • notify()锁不释放
      • 当方法wait()被执行后,锁自动被释放
      • 执行完notify()方法后,锁不会自动释放
      • 必须执行完notify()方法所在的synchronized代码块后才释放
      • 当线程呈wait状态时,对线程对象调用interrupt方法会出现InterrupedException异常

线程间通信

  • 管道输入/输出流
    • 用于线程之间的数据传输,传输媒介为内存
    • 方式分类
      • 面向字节 PipedOutputStream,PipedInputStream
      • 面向字符 PipeWriter,PipedReader
  • Thread.join()
    • 应用场景
      • 主线程需要等待子线程执行完成之后再结束
      • 一个线程需要等待另一个线程
    • 原型 join(long mills)
  • ThreadLocal类
    • 作用
      • 让每个线程绑定自己的值,可存储每个线程的私有数据
    • 相关方法
      • get(), set(T value),remove(),initalValue()
    • InheritableThreadLocal
      • 解决子线程并不能取到父线程的ThreadLocal类的变量的问题

Lock锁

  • Lock接口
    • 用于通过多个线程控制对共享资源的访问工具
      • 在Lock接口出现之前,Java程序是靠synchronized关键字实现锁功能的
    • Lock接口提供synchronized关键字不具备的特性
      • 尝试非阻塞地获取锁
        • 当前线程尝试获取锁,如果这一时刻锁没有被其他线程获取到,则成功获取并持有锁
      • 能被中断的获取锁
        • 获取到锁的线程能够响应中断,当获取到锁的线程被中断时,中断异常将会被抛出,同时锁会被释放
      • 超时获取锁
        • 在指定的截止时间之前获取锁, 超过截止时间后仍旧无法获取则返回
    • 常用方法
      • void lock()
      • Condition newCondition()
      • boolean tryLock()
      • void unlock()
  • Lock接口的实现类ReentrantLock
    • 作用:和synchronized关键字一样可以用来实现线程之间的同步互斥
    • Condition接口
    • 使用Condition实现等待/通知机制
      • 单个condition
      • 多个condition
      • 使用condition实现顺序执行
    • 公平锁与非公平锁
      • ReentrantLock(boolean fair)方法
  • ReadWriterLock接口的实现类:ReentrantReadWriteLock
    • 读读共享
    • 写写互斥
    • 读写互斥
    • 写读互斥

并发编程

  • 并发是否好?
    • CPU通过给每个线程分配CPU时间片来实现伪同时运行
    • 并发编程并不总是能提高程序运行速度的
  • 上下文切换
    • 任务从保存到再加载的过程就是一次上下文切换
    • 上下文切换对系统来说意味消耗大量的CPU时间
    • 上下文切换的方式
      • 让步式上下文切换
      • 抢占式上下文切换
  • 减少上下文切换
    • 减少锁的使用
    • 使用CAS无锁算法
      • CAS比较与交换(Compare and swap)
    • 减少线程的使用
      • 创建大量的线程会导致大量的线程都处于等待状态
      • CAS算法和协程
    • 使用协程
  • 避免死锁
    • 使用不当会造成死锁
  • 减少资源限制
    • 资源限制引发
      • 在并发编程中,程序运行加快的原因是运行方式从串行运行变为并发运行
      • 若某段程序的并发执行由于资源限制仍然在串行执行的话,反而会变慢,因可能增加资源调度时间
    • 解决资源限制
      • 硬件资源限制,使用集群并行执行程序
      • 软件资源限制,使用资源池将资源复用
        • 用连接池将数据库和Socket复用

线程池与Executor框架

  • 用线程池优点
    • 降低资源消耗
    • 提高响应速度
    • 提高线程的可管理性
  • Executor
    • Java5+之后,通过Executor来启动线程比使用Thread的start方法好
    • 框架结构组成
      • 任务
        • Runnable接口或Callable接口实现类
      • 执行
        • ScheduledThreadPoolExecutor
        • ThreadPoolExecutor
      • 异步计算的结果
        • Future接口
        • Future接口的实现类FutrueTask类
  • ThreadPoolExecutor
    • 常用属性
      • corePoolSize
      • maxinumPoolSize
      • queue
      • rejectedExecutionHandler
    • 4个构造方法
    • 创建
      • FixedThreadPool 可重用固定线程数的线程池,常在需要限制当前线程数量的应用场景
      • SingleThreadExecutor 使用单个worker线程的Excutor
        • 适用于需要保证顺序地执行各个任务并且在任意时间点,不会有多个线程是活动的应用场景
      • CachedThreadPool 根据需要创建新线程的线程池
        • 适用于执行很多的短期异步任务的小程序,或者是负载较轻的服务器
  • ScheduledThreadPoolExecutor
    • 作用 延迟或定欺执行任务
    • 执行周期任务的步骤
  • 线程池的应用场景
    • ScheduledThreadPoolExecutor: 适用于需要多个后台执行周期任务
    • SingleThreadScheduledExecutor: 适用于需要单个后台线程执行周期任务

猜你喜欢

转载自blog.csdn.net/u011584949/article/details/86511834