Java多线程编程核心技术

多线程基础

概念

  • 进程
  • 线程
  • 线程的调用的随机性:代码的运行结果与代码执行顺序或调用顺序是无关的.CPU以不确定的方式,或者说是以随机的时间来调用线程中的run方法
  • 线程启动顺序与start()执行顺序无关
  • 守护线程
  • Daemon的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是GC

线程实现方式

  • 继承Thread类
  • 实现Runnable接口

线程安全

  • 共享变量:多个线程可访问同一个变量
  • 线程安全:获得的实例变量的值是经过同步处理的,不会出现"脏读"的现象
  • 非线程安全:多个线程对同一个对象中的同一个实例变量进行并发访问时产生,产生的结果就是"脏读"

线程停止

  • 概念:在线程处理完任务之前停掉正在做的操作,也就是放弃当前的操作
  • 三种方式
  • 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止
  • 使用stop方法强行终止线程
  • 使用interrupt方法中断线程
  • 判断线程是否停止方法
  • interrupted()方法
  • isInterrupted()方法
  • 具体停止方法
  • 异常法:建议使用"抛异常"的方法来实现线程的停止,因为在catch块中还可以将异常向上抛,使线程停止的事件得以传播
  • sleep状态下停止:interrupt()和sleep()结合
  • 暴力停止:stop()方法
  • interrupt()和return结合停止线程

线程优先级

  • 继承性:A线程启动B线程,则B线程优先级与A一样
  • 规则性:线程的优先级具有一定的规则性,也就是CPU尽量将执行资源让给优先级比较高的线程
  • 随机性:优先级较高的线程不一定每一次都先执行完

线程状态

  • NEW
  • 线程实例化后还从未执行start()方法时的状态
  • RUNNABLE
  • 线程进入运行的状态
  • BLOCKED
  • 出现在某一个线程在等待锁的时候
  • WAITING
  • 线程执行了Object.wait()方法后所处的状态
  • TIMED_WAITING
  • 代表线程执行了Thread.sleep()方法,呈等待状态,等待时间到达,继续向下运行
  • TERMINATED
  • 线程被销毁时的状态


    方法与状态关系示意图.png

Thread方法

  • start()
  • 通知"线程规划器"此线程已经准备就绪,等待调用线程对象的run()方法
  • 让系统安排一个时间来调用Thread中的run()方法,也就是使线程得到运行,启动线程,具有异步执行的效果
  • run()
  • 执行线程方法
  • currentThread()
  • 获取当前线程信息
  • isAlive()
  • 判断当前的线程是否处于活动状态
  • 活动状态"指线程已经启动且尚未终止.线程处于正在运行或准备开始运行的状态,就认为线程是“存活”的
  • sleep()
  • 在指定的毫秒数内让当前“正在执行的线程”休眠(暂停执行)
  • 不释放锁
  • getId()
  • 取得线程的唯一标识
  • interrupt()
  • 在当前线程中打了一个停止的标记,并不是真的停止线程
  • interrupted()
  • 测试当前线程是否已经中断,执行后具有将状态标志置为清除为false的功能
  • "清除"即调用interrupted()后停止状态被清除,再次调用interrupted()时为false(第一次中断后,且第二次调用interrupted()前再次中断例外)
  • isInterrupted()
  • 测试线程是否已经中断,不清除状态标志
  • yield()
  • 放弃当前的CPU资源,将它让给其他的任务去占用CPU执行时间
  • setPriority()
  • 设置线程优先级
  • join()
  • 等待线程对象销毁
  • join()过程中,若当前线程被中断(interrupt()),则当前线程出现异常
  • join(long)
  • 等待线程对象销毁,设置等待时间
  • setUncaughtExceptionHandler()
  • 对指定的线程对象设置默认的异常处理器
  • setDefaultUncaughtExceptionHandler()
  • 为指定线程类的所有线程对象设置默认的异常处理器
  • join()与synchronized区别
  • join()方法内部使用wait()方法进行等待,而synchronized关键字使用的是"对象监视器"原理作为同步
  • join(long)与sleep(long)区别
  • join(long)方法内部使用wait(long)方法实现,具有释放锁的特点
  • sleep(long)具有不释放锁的特点
  • 注意(已废弃方法)
  • stop():强行终止线程,线程不安全.将锁定的对象进行"解锁",导致数据得不到同步处理,出现数据不一致问题
  • suspend():暂停当前线程
  • resume():恢复线程的执行
  • suspend()和resume()缺陷:独占;因线程暂停而导致数据不同步

ThreadGroup线程组(了解)

synchronized

synchronized同步方法

  • 概念
  • 可重入锁:自己可以再次获取自己的内部锁.比如有1条线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的,如果不可锁重入的话,就会造成死锁.当存在父子类继承关系时,子类是完全可以通过"可重入锁"调用父类的同步方法的
  • 性质
  • 两个线程访问同一个对象中的同步方法一定是线程安全的(对象锁)
  • 两个线程访问同一个类的两个不同实例的相同名称的同步方法,效果以异步进行(多个对象多个锁)
  • A线程先持有object对象的Lock锁,B线程可以以异步的方式调用object对象中的非synchronized类型的方法
  • A线程先持有object对象的Lock锁,B线程如果在这时调用object对象中的synchronized类型的方法则需等待,也就是同步
  • 锁重入:在使用synchronized时,当一个线程得到一个对象锁后,再次请求此对象锁时是可以再次得到该对象的锁的.这也证明在一个synchronized方法/块的内部调用本类的其他synchronized方法/块时,是永远可以得到锁的
  • 出现异常锁自动释放:当一个线程执行的代码出现异常时,其所持有的锁会自动释放
  • 同步不具有继承性:父类方法的锁不能被子类方法继承,若子类方法要实现同步,使用synchronized关键字

synchronized同步代码块

  • 场景
  • 解决synchronized同步方法执行慢,其他线程等待时间长的问题
  • 性质
  • 当两个并发线程访问同一个对象object中的synchronized(this)同步代码块时,一段时间内只能有一个线程被执行,另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块
  • 当一个线程访问object的一个synchronized同步代码块时,另一个线程仍然可以访问该object对象中的非synchronized(this)同步代码块
  • 当一个线程访问object的一个synchronized同步代码块时,另一个线程仍然可以访问该object对象中的非synchronized(this)同步代码块
  • synchronized代码块间的同步性:当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对同一个object中所有其他synchronized(this)同步代码块的访问将被阻塞,这说明synchronized使用的"对象监视器"是一个
  • 和synchronized方法一样,synchronized(this)代码块也是锁定当前对象的
  • 任意对象作为对象监视器
  • synchronized(非this对象x)优点:若在一个类中有很多个synchronized方法,这时虽能实现同步,但会受到阻塞,影响运行效率;但如果使用同步代码块锁非this对象,则synchronized(非this)代码块中的程序与同步方法是异步的,不与其他锁this同步方法争抢this锁,则可大大提高运行效率
  • 静态synchronized方法和synchronized(this)代码块
  • synchronized关键字加到static静态方法上是给Class类上锁,而synchronized关键字加到非static静态方法上是给对象上锁
  • Class锁可以对类的所有对象实例起作用
  • 同步synchronized(class)代码块的作用和synchronized static方法的作用一样
  • 注意问题
  • 死循环
  • 多线程的死锁:不同的线程都在等待根本不可能被释放的锁,从而导致所有的任务都无法继续完成;设计程序时就要避免双方互相持有对方的锁的情况,因为这会造成线程的"假死"
  • 一句话概括
  • 在将任何数据类型作为同步锁时,需要注意的是,是否有多个线程同时持有锁对象,如果同时持有相同的锁对象,则这些线程之间就是同步的;如果分别获得锁对象,这些线程之间就是异步的

volatile

  • 作用
  • 使变量在多个线程间可见(强制从公共堆栈中取得变量的值,而不是从线程私有数据栈中取得变量的值)


    线程的私有堆栈.jpg
  • 缺点
  • 不支持原子性
  • 使用场景
  • 在多个线程中可以感知实例变量被更改了,并且可以获得最新的值使用,也就是用多线程读取共享变量时可以获得最新值使用


    读取公共内存.png
  • synchronized和volatile比较
  • 关键字volatile是线程同步的轻量级实现,所以volatile性能肯定比synchronized要好,并且volatile只能修饰于变量,而synchronized可以修饰方法,以及代码块
  • 多线程访问volatile不会发生阻塞,而synchronized会出现阻塞
  • volatile能保证数据的可见性,但不能保证原子性;而synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存和公共内存中的数据做同步
  • 关键字volatile解决的是变量在多个线程之间的可见性;而synchronized关键字解决的是多个线程之间访问资源的同步性,而且它还具有将线程工作内存中的私有变量与公共内存中的变量同步的功能
  • synchronized两个特征(外练互斥,内修可见)
  • 互斥性
  • 可见性

线程通信

等待/通知机制

  • wait()方法:使线程停止运行(可以使调用该方法的线程释放共享资源的锁,然后从运行状态退出,进入等待队列,直到被再次唤醒)
  • Object类的方法
  • 调用wait()前,线程必须获得该对象的对象级别锁,即只能在同步方法或同步块中调用wait()方法
  • 执行wait()方法后,当前线程释放锁;遇到异常而导致线程终止,锁也会被释放
  • 若调用wait()时没有持有适当的锁,则抛出IllegalMonitorStateException
  • 当线程呈wait()状态时,调用线程对象的interrupt()方法会出现InterruptedException异常
  • wait(long)方法:等待某一时间内是否有线程对锁进行唤醒,如果超过这个时间则自动唤醒
  • notify()方法:使停止的线程继续运行(可以随机唤醒等待队列中等待同一共享资源的"一个"线程,并使该线程退出等待队列,进入可运行状态,也就是notify()方法仅通知"一个"线程)
  • Object类的方法
  • 调用notify()前,线程必须获得该对象的对象级别锁,即只能在同步方法或同步块中调用notify()方法
  • 在执行notify()方法后,当前线程不会马上释放该对象锁,呈wait状态的线程也并不能马上获取该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出synchronized代码块后,当前线程才会释放锁,而呈wait状态所在的线程才可以获取该对象锁
  • 当第一个获得了该对象锁的wait线程运行完毕以后,它会释放掉该对象锁,此时如果该对象没有再次使用notify语句,则即便该对象已经空闲,其他wait状态等待的线程由于没有得到该对象的通知,还会继续阻塞在wait状态,直到这个对象发出一个notify或notifyAll
  • 若发出notify操作时没有处于阻塞状态中的线程,那么该命令会被忽略
  • 若调用notify()时没有持有适当的锁,则抛出IllegalMonitorStateException
  • notifyAll()方法:可以使所有正在等待队列中等待同一共享资源的"全部"线程从等待状态退出,进入可运行状态

进入Runnable状态五种情况

  • 调用sleep()方法后经过的时间超过了指定的休眠时间
  • 线程调用的阻塞IO已经返回,阻塞方法执行完毕
  • 线程成功地获得了试图同步的监视器
  • 线程正在等待某个通知,其他线程发出了通知
  • 处于挂起状态的线程调用了resume()恢复方法

出现阻塞(Blocked)几种情况

  • 线程调用sleep方法,主动放弃占用的处理器资源
  • 线程调用了阻塞式IO方法,在该方法返回前,该线程被阻塞
  • 线程试图获得一个同步监视器,但该同步监视器正被其他线程所持有
  • 线程等待某个通知
  • 程序调用了suspend方法将该线程挂起(易导致死锁,避免使用)

问题:造成程序逻辑混乱

  • 通知过早
  • wait被唤醒后执行synchronized方法或代码块中剩余代码时,变量被更改
  • wait等待的条件发生变化
  • wait被唤醒后执行synchronized方法或代码块中剩余代码时,变量被更改

生产者/消费者模式

  • 原理:基于wait/notify
  • 假死:生产者唤醒生产者,消费者唤醒消费者,积少成多,导致所有线程都呈WAITING状态,也就是程序假死.使用notifyAll解决假死问题.
  • 模式类型
  • 一生产一消费:操作值
  • 多生产多消费:操作值-假死
  • 多生产与多消费操作值
  • 一生产一消费:操作栈
  • 一生产多消费:操作栈-解决wait条件改变与假死
  • 多生产一消费:操作栈
  • 多生产与多消费:操作栈

通过管道进行线程间通信

  • 管道流(pipeStream):用于在不同线程间直接传送数据.一个线程发送数据到输出管道,另一个线程从输入管道中读数据.通过使用管道,实现不同线程间的通信
  • 字节流
  • PipedInputStream和PipedOutputStream
  • 字符流
  • PipedReader和PipedWriter

ThreadLocal

  • 作用:每个线程绑定自己的值,可以将ThreadLocal比喻成全局存放数据的盒子,盒子里可以存放每个线程的私有数据
  • initialValue():重写方法设置默认值

InheritableThreadLocal

  • 作用:可以在子线程中取得父线程继承下来的值
  • initialValue():重写方法设置默认值
  • childValue():值继承再修改
  • 注意:若子线程取得值的同时,主线程将InheritableThreadLocal中的值进行修改,那么子线程获取到的值还是旧值

Lock使用

ReentrantLock

  • 概念
  • 多路通知:在一个Lock对象里面创建多个Condition(即对象监视器)实例,线程对象注册在指定的Condition中,从而可以有选择性地进行线程通知,在调度线程上更加灵活
  • 公平锁:表示线程获取锁的顺序是按照线程加锁的顺序来分配的,即先来先得的FIFO先进先出顺序
  • 非公平锁:一种获取锁的抢占机制,是随机获得锁的,可能造成某些线程一直拿不到锁
  • 作用
  • 具有和synchronized同样的功能,并且在扩展功能上更加强大,比如具有嗅探锁定/多路分支通知等功能,而且在使用上比synchronized更加灵活
  • 借助Condition实现等待/通知
  • await():相当于Object类中wait()方法
  • await(long time, TimeUnit unit):相当于Object类中wait(long time)方法
  • signal():相当于Object类中notify()方法
  • signalAll():相当于Object类中notifyAll()方法
  • 借助多个Condition实现多路通知
  • Condition实现等待/通知和wait()/notify()区别
  • 在使用notify()/notifyAll()方法进行通知时,被通知的线程是由JVM随机选择的;但使用ReentrantLock结合Condition类可以实现"选择性通知"
  • synchronized相当于整个Lock对象中只有一个单一的Condition对象,所有的线程都注册在它一个对象的身上.线程开始notifyAll()时,需要通知所有的WAITING线程,没有选择权
  • ReentrantLock方法
  • lock()
  • unlock()
  • getHoldCount():查询当前线程保持此锁定的个数也就是调用lock()方法次数
  • getQueueLength():返回正等待获取此锁定的线程估计数
  • getWaitQueueLength(Condition condition):返回等待与此锁定相关的给定条件Condition的线程估计数
  • hasQueuedThread(Thread thread):查询指定的线程是否正在等待获取此锁定
  • hasQueuedThreads():查询是否有线程正在等待获取此锁定
  • hasWaiters(Condition condition):查询是否有线程正在等待与此锁定有关的condition条件
  • isFair():判断是不是公平锁
  • isHeldByCurrentThread():查询当前线程是否保持此锁定
  • isLocked():查询此锁定是否由任意线程保持
  • lockInterruptibly():若当前线程未被中断,则获取锁定,若已经被中断则出现异常
  • tryLock():仅在调用时锁定未被另一个线程保持的情况下,才获取该锁定
  • tryLock(long timeout,TimeUnit unit):若锁定在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁定
  • awaitUninterruptibly()
  • awaitUntil()

ReentrantReadWriteLock

  • 概念
  • 读写锁:表示两把锁,一个共享锁,一个排他锁
  • 共享锁:读操作相关的锁
  • 排他锁:写操作相关的锁
  • 性质
  • 读读共享,写写互斥,读写互斥,写读互斥.即多个Thread可以进行读操作,但是同一时刻只允许一个Thread进行写操作

单例模式

立即加载/饿汉模式

  • 在调用方法前,实例已经被创建

延迟加载/懒汉模式

  • 在调用方法时实例才被创建
  • 缺点
  • 线程不安全
  • 解决方案
  • 声明synchronized关键字
  • DCL(Double-Check Locking)双检查锁机制,使用volatile加synchronized代码块

静态内置类实现单例模式

使用static代码块实现单例模式

使用enum枚举数据类型实现单例模式

定时器Timer(了解)

  • 实现指定时间执行任务

  • 实现按指定周期执行任务

你可能感兴趣的:(Java多线程编程核心技术)