[Java多线程编程核心技术]

来源:互联网 发布:网络品牌注册 怎么回事 编辑:程序博客网 时间:2024/06/05 01:02

基本概念

创建线程

  • 继承Thread类
  • 实现Runnable接口

Thread常用方法

  • currentThread()

    可以返回代码段正在被哪个线程调用信息,注意Thread.currentThread与线程对象里this的区别,前者指调用的线程,后者表示当前对象本身

  • isAlive()

    测试线程是否存活

  • sleep()

    当前正在执行的线程在指定的毫秒数内休眠,也就是Thread.currentThread获取到的线程

  • getId()

    获取线程的唯一标识

停止线程

类别

  • 正常终止
  • stop(已废弃)
  • interrupt

状态判断

  • interrupted(当前线程,静态方法)

    当前线程是指运行interrupted判断的线程是否中断,且interrupted具有清除判断功能,当连续两次调用该方法,及时第一次返回true,第二次也返回false

  • isInterrupted(对象线程,非静态方法)

    调用该方法的线程对象是否中断,且不具有清除功能

停止方案

  • 正常停止

    用interrupted或者isInterrupted方法判断是否停止,停止就抛出InterruptedException异常或者做其它处理

  • stop停止(已废弃)

    stop方法可以终止线程,且抛出运行异常ThreadDeath,但该方法可能会导致清理工作未完成或者使锁定的对象被解锁,引发同步问题

暂停线程

类别

  • suspend(暂停)
  • resume(恢复)

bugs

独占

由于线程A调用方法C时suspend,但线程B也想调用方法C,由于方法C属于同步调用且被线程A占用,导致线程B阻塞

yield方法

放弃当前CPU资源,让其它任务线程执行,放弃资源时间具有不确定性

线程优先级

线程优先级继承特性

线程A创建线程B,线程B具有线程A相同的优先级

线程优先级具有规则性、随机性

高优先级的大部分情况优先执行,但也不都是优先执行完成

守护线程(对线程setDaemon)

并发同步

synchronized同步方法

方法内变量为线程安全

“非线程安全”存在与实例变量中,方法内变量不存在“非线程安全”问题

实例变量非线程安全

对于多个线程访问同一个实例变量就会引发“非线程安全”问题

多个对象访问多个锁

不同对象JVM会创建不同的锁

synchronized与对象锁

关键字synchronized获取的是对象锁,多线程只有获取同一对象锁时才具有同步作用,synchrozized方法对非synchronized方法的调用无任何影响

出现异常,锁自动释放

当一个线程执行的代码出现异常时,其所持有的对象锁自动释放

synchronized同步语句块

synchronized同步代码块的使用

当两个并发线程访问同一个对象中的synchronized(this)同步代码块时,一段时间内只能有一个线程被执行,另一个线程必须等待当前线程执行完这个代码块后才能执行该代码块

synchronized同步方法与synchronized(this)

synchronized同步方法对其他synchronized同步方法或synchronized(this)同步代码块调用呈阻塞状态,同一时间只有一个线程可以执行synchronized同步方法中代码;synchronized(this)同步代码块对其他同步synchronized同步方法或synchronized(this)同步代码块调用呈阻塞状态,同一个时间只有一个线程可以执行synchronized(this)同步代码块中的代码

将任意对象作为对象监视器

synchronized(非this对象)在多个线程持有“对象监视器”为同一个对象的前提下,同一时间只有一个线程可以执行synchronized(非this对象)同步代码块中的代码,当持有“对象监视器”为同一个对象的前提下,同一时间只有一个线程可以执行synchronized(非this对象)同步代码块中的代码。

静态同步synchronized方法与synchronized(class)代码块

关键字synchronized应用在static静态方法上表示对当前的*.java文件对应的Class类进行持锁,Class类锁与对象锁不是同一个锁。同步synchronized(class)代码块的作用和synchronized static方法的作用一样。

内置类与同步

内置类中具有不同对象锁的同步方法,执行结果也是异步的;synchronized(内置类)对内置类A上锁后,其它线程只能以同步的方式调用内置类A中的同步方法

特性

锁重入

当线程A获取对象B的对象锁时,在未释放该锁的前提下可以再次获取该对象锁

不可继承

类A有synchronized同步方法C,类B继承类A且覆盖方法C,类B中覆盖的方法C也需要添加关键字synchronized才具有同步功能

bugs

脏读

线程A调用synchronized方法时,需要对变量C与变量D同时赋值,但执行了变量C的赋值未执行到变量D的赋值就发生线程B对变量C与变量D的访问,从而导致脏读,解决方法对变量C与变量D的读取与赋值方法做同步

String常量池同一锁

由于JVM具有String常量池缓存功能,所以使用String常量作为容易引起非期望的同步问题,因此一般不使用String常量作为”对象监视器“

死锁(jstack检测)

线程A占有对象锁C而请求对象锁D,线程B占有对象锁D请求对象锁C,在这种情况下就发生了线程间的死锁。利用工具jstack

无限等待

线程A与线程B争夺调用不同同步方法,其中线程A的同步方法是个无限循环,如果线程A执行后,线程B就会处于无限等待状态。可以将同步方法修改为同步块或者根据具体场景修改代码逻辑

volatile

关键字volatile的主要作用使变量在多个线程间可见

关键字和volatile比较

  • 关键字volatile是线程同步的轻量级实现,所有volatile性能肯定比synchronized要好,并且volatile只能修饰于变量,二synchronized可以修饰方法,以及代码块。

  • 多线程访问volatile不会发生阻塞,而synchronized会出现阻塞

  • volatile能保证数据的可见性,但不能保证原子性;而synchronized可以保证原子性,也可以间接保证可见性

  • 关键字volatile解决的是变量在多线程之间的可见性;而synchronized解决的是多线程之间访问资源的同步性

Lock的使用

ReentrantLock

ReentrantLock不仅具有同步互斥功能,还具有嗅探锁定、多路分支等功能

类型

  • 公平锁

    公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的,即先来先得的FIFO先进先出顺序。ReentrantLock构造函数为true表示公平锁,反之为非公平锁

  • 非公平锁

    非公平锁就是一个获取锁的抢占机制,是随机获得锁的,和公平锁不一样的就是先来的不一定先得到锁,这个方式可能造成某些线程一直拿不到锁,结果也就是不公平的了

方法

  • getHoldCount()

    查询当前线程保持此锁定的个数,也就是调用lock方法的次数

  • getQueueLength()

    返回正等待获取此锁定的线程估计数

  • getWaitQueueLength(Condition condition)

    返回等待与此锁定相关的给定条件Condition的线程估计数

  • hasQueuedThread(Thread thread)

    查询指定线程是否正在等待获取此锁定

  • hasWaiters(Condition condition)

    查询是否有线程正在等待与此锁定有关的condition条件

  • isFair()

    判断是不是公平锁

  • isLocked()

    查询此锁定是否有任意线程保持

  • isHeldByCurrentThread()

    查询当前线程是否保持此锁定

  • lockInterruptibly()

    如果当前线程未中断,则获取锁定,如果已经被中断则出现异常

  • tryLock()

    仅在调用时锁定未被另一个线程保持的情况下,才获取该锁定

  • tryLock(long timeout, TimeUnit unit)

    锁定在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁定

ReetrantWriteReadLock

  • 读读共享
  • 读写互斥
  • 写写互斥
  • 写读互斥

并发通信

等待/通知(wait/notify)机制

  • 加对象锁
  • wait调用后释放锁
  • notify调用后不释放锁
  • notify随机通知一个线程
  • notifyAll通知所以线程
  • wait(long)指定等待时长

不使用等待/通知机制的情况下,原始方式是通过循环检测条件是否满足,它的主要弊端是浪费CPU资源,因此需要一种新机制来实现多线程间通信,它就是“wait/notify”机制。方法wait()被调用之前,线程必须获取该对象的对象级别锁,在执行wait()方法后,当前线程释放锁,在从wait()返回前,线程与其它线程竞争获取锁,如果调用wait()时没有持有适当的锁,则抛出IllegalMonitorStateException,当线程呈wait()状态时,调用线程对象的interrupt()方法会出现InterruptedException异常;方法notify()也要在同步方法或者同步块中调用,即在调用前,线程也必须获取该对象的对象级别锁,在执行notify()方法后,当前线程不会马上释放该对象锁,呈wait状态的线程也并不能马上获取该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出synchronized代码块后,当前线程才会释放锁,而呈wait状态所在线程才可以获取该对象锁

bugs

  • notify通知早于wait调用

  • wait条件发生变化

  • wait/notify错乱导致假死

pipe

字节流

  • PipedInputStream
  • PipedOutputStream

字符流

  • PipedReader
  • PipedWriter

join

对于主线程想等待子线程执行完成之后再结束,就可以使用join()方法,它的作用就是等待线程对象销毁,在join过程中,如果当前线程对象被中断,则当前线程出现异常,join(long)中的参数是设定等待的时间,join(long)内部使用wait(long),具有释放锁特点,从而使其它线程可以调用此线程的同步方法或者synchronized(线程对象锁)调用块,而Thread.sleep(long)方法不释放锁

ThreadLocal

主要包含set与get方法,可以使线程变量隔离

InheritableThreadLocal

功能与ThreadLocal相同,且可以使子线程获取父献策继承值,但需要注意主线程将InheritableThreadLocal中值进行更改,子线程取到的值还是旧值

Condition

关键字synchronized与wait()和notify()/notifyAll()方法相结合可以实现等待/通知模式,Condition类借助ReentrantLock可以实现同样功能,Condition有更好的灵活性,比如可以实现多路通知功能,也就是一个Lock对象里面可以创建多个Condition(即对象监视器)实例,线程对象可以注册在指定的Condition中,从而可以有选择性的进行线程通知,也就是说虽然Condition也需要获取对象监听器(Lock调用lock()),但可以指定唤醒线程。synchronized相当于整个Lock对象中只有一个单一的Condition对象

原创粉丝点击