根据<JAVA多线程编程核心技术> 整理
1.停止线程
使用退出标志,使线程正常退出,也就是当run完成后线程终止
使用stop强行终止线程,但不推荐使用,已经作废
使用interrupt中断线程
2.停止不了的线程
调用interrupt给当前线程打一个停止标记
3.判断线程是否是停止状态
interrupted():是否已经被中断,执行后将具有状态标志清除为false
isInterrupted():是否已经中断,但不清除状态标记
4.在sleep状态下终止线程,会进入catcha语句,并且清除状态值,变为false
5.使用return停止线程
interrupt()与return结合
6.暂停线程
suspend()暂停线程
resume():恢复线程的执行
以上两个方法:
1.独占:容易造成公共的同步对象的独占,使得其他线程无法访问公共共同对象。
2.不同步:会因线程的暂停而导致数据不同步
7.yield()
放弃当前的CPU资源,将它让给其他的任务去占用CPU执行时间,但放弃的时间不确定,有可能刚刚放弃,马上又获得CPU时间片
8.线程优先级
默认为5,
最高10, 最低1
如何小于1或者大于10,抛出IllegalArgumentException()
优先级的继承特性:
线程A启动线程B,则B的优先级与A是一样的。
优先级的规则性:
高优先级的线程总是大部分先执行完,但不代表高优先级的线程全部先执行完。
当线程优先级的等级差距较大时,谁先执行完和代码的调用顺序无关。
优先级的随机性:
高优先级的线程并不一定每次都优先执行完。
9.守护线程
线程分为守护线程和用户线程
当进程中不存在非守护线程,则守护线程自动销毁。
典型的守护线程是垃圾回收线程,当进程中不存在非守护线程,则垃圾回收线程也就没有必要存在了。
只要JVM中存在任何一个非守护线程没有结束,守护线程就在工作,只有当最后一个非守护线程结束时,守护线程才随着jvm一起结束。
Thread.setDaemon();
10.synchronized同步方法
10.1 方法内的变量为线程安全:非线程安全的问题存在于实例变量中,如果是方法内部的私有变量,则不存在“非线程安全问题”
10.2 实例变量非线程安全
如果多个线程共同访问1个对象中的实例变量,则有可能出现“非线程安全”问题。
如果对象仅有一个实例变量,则有可能出现覆盖的情况。
调用关键字synchronized声明的方法一定是排队运行的,只有共享的资源才需要同步化,如果不是共享资源,根本没有同步的必要。
脏读是可以通过synchronized解决的。
1.当线程A调用anyObject对象加入synchronized关键字的X方法时,A线程就获得了X方法锁,更准确的讲,是获得了对象锁,所以其他线程必须等A线程执行完才可以调用X方法,但B线程可以随意调用其他非synchronized同步方法。
2.当A线程调用anyObject对象加入synchronized关键字X方法,A就获得了X方法所在的对象的锁,所以其他线程必须等A执行完才可以调用X方法,而B线程如果调用声明了synchronized的非X方法,必须等A线程将X方法执行完,也就是释放对象锁后才可以调用,这时A线程已经执行了一个完整的任务,就不会出现脏读的情况。
10.3 synchronized锁重入
synchronized拥有锁重入的功能,也就是在使用synchronized时,当一个线程得到一个对象锁后,再次请求此对象锁时是可以再次得带该对象的锁的,这就证明在一个synchronized方法/块的内部调用本类的其他synchronized方法/块时,是永远可以得到锁的。
当存在父子类继承关系时,子类是完全可以通过“可重入锁”调用父类的同步方法的。
10.4 出现异常,锁自动释放
当一个线程执行的代码出现异常时,其所持有的锁会自动释放。
10.5 同步不具有继承性
11.synchronized同步代码块
使用synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法执行一个长时间的任务,那么B线程则必须等待比较长的时间,这种情况是可以使用synchronized同步语句块来解决。
11.1 synchronized同步代码块的使用
当两个并发线程访问同一个对象object中的synchronized(this)同步代码块时,一段时间内只能有一个线程被执行,另一个线程必须等待当前线程执行完这个代码块后才能呢个执行该代码块。
11.2 用同步代码块解决同步方法的弊端
当一个线程访问object的一个synchronized同步代码块时,另一个线程仍然可以访问该object对象中的非synchronized(this)同步代码块。
虽然时间缩短,运行效率加快,但同步synchronized代码块真的是同步吗真的持有当前调用对象的锁吗?
---是,但必须通过代码的方式验证。
一半同步,一半异步:不在synchronized块中的就是异步的,在synchronized块中就是同步的。
11.3 synchronized代码块间的同步性
在使用同步synchronized(this)代码块时应该注意:
当一个线程访问object的一个synchronized(this)同步代码块时,其他线程随同一个object中所有其他synchronized(this)同步代码块的访问江北阻塞,这说明synchronized使用的“对象监视器”。
synchronized(this)代码块是锁定当前对象的。
11.4 将任意对象作为对象监视器
多个线程调用同一个对象中的不同名称的synchronized同步方法或者synchronized(this)同步代码块时,是按照顺序执行的,也就是同步的,阻塞的。
说明synchronized同步方法或者synchronized(this)同步代码块分别有两种作用:
synchronized方法:
1.对其他synchronized同步方法或synchronized(this)同步代码块调用呈阻塞状态
2.同一时间只有一个线程可以执行synchronized同步方法中的代码
synchronized(this)同步代码块:
1.对其他synchronized同步方法或者synchronized(this)同步代码块调用呈阻塞状态
2.同一时间只有一个线程可以执行synchronized(this)同步代码块中的代码
在多线程只有“对象监视器”为同一个对象的前提下,同一时间只有一个线程可以执行synchronized(非this对象x)同步代码块中的代码
当持有“对象监视器”为同一个对象的前提下,同一时间室友一个线程可以执行synchronized(非this对象x)同步块中的代码。
锁非this对象
如果在一个类中有多个synchronized方法,这时虽然能实现同步,但会受到阻塞,所以影响运行效率,但如果使用同步代码块锁非this对象,则synchronized(非this)代码块中的程序与同步方法是异步的,不与其他锁this同步方法争抢this锁,则可大大提高运行效率。
使用synchronized(非this对象x)同步代码块格式进行同步操作时,对象监视器必须是同一个对象,如果不是同一个对象监视器,运行的结果就是异步调用了,就会交叉运行。
??
同步代码块放在菲同步synchronized方法中进行声明,并不能保证调用方法的线程的执行同步/顺序性,也就是线程调用方法的顺序是无需的,瑞瑞在同步块中执行的顺序是同步的,这样极易出现脏读
使用synchronized(非this对象x)同步代码块也可以解决脏读问题。
synchronized(非this对象x):
1.当多个线程同时执行synchronized(x){}同步代码块时呈同步效果
2.当其他线程执行x对象中synchronized同步方法时呈现同步效果
3.当其他线程执行x对象方法里面的synchronized(this)代码块时也呈现同步效果。
12.静态同步synchronized方法与synchronized(class)代码块
synchronized用在static静态方法,是不对当前的*.java文件对应的Class类进行持锁。
13.数据类型String的常量池对象
JVM中具有String常量池混村功能
在大多数情况下,synchronized代码块都不会私用String作为锁对象,而改用其他,比如new Object()十里花一个Object对象。
14.同步synchronized方法无线等待与解决
同步块解决
15.线程的死锁
使用jdk自带的工具检测是否有死锁:执行bin目录下的jps命令,得到运行线程 的id值,再执行jstack命令
16.锁对象的改变
在将任何数据类型作为同步锁时,需要注意:是否有多个线程同伙私持有锁对象,如果同时持有相同的锁对象,则这些线程之间就是同步的,如果分别获得锁对象,这些线程之间就是异步的。
只要对象不变,及时对象的属性变了,运行的结果还是同步的。
17.volatile关键字
作用是使变量在多个线程间可见:强制从公共堆中取得变量的值,而不是从线程的私有数据栈中取得变量的值。
但是volatile不支持原子性。
synchronized和volatile的比较:
1.volatile是线程同步的轻量级实现,所以volatile性能肯定比synchronized要好,并且volatile只能修饰变量,而synchronized可以修饰方法的代码块,随着jdk新版本的发布,synchronized关键字在执行效率上得到很大提升,在开发中使用synchronized关键字的比率还是比较大的。
2.多线程访问volatile不会发生阻塞,而synchronized会发生阻塞。
3.volatile能保证数据的可见性,但不能保证原子性,而synchronized可以保证原子性,也可以间接保证可见性,因为他会将私有内存和公共内存中的数据做同步。
4.volatile解决的是变量在多个线程之间的可见性,而synchronized解决的是多个线程之间访问资源的同步性。
线程安全包含原子性和可见性,java同步机制都是围绕这两个方面来确保线程安全。
18.线程间的通信
18.1 等待/通知机制:wait()/notify()
wait():
使当前执行代码的线程进行等待,wait(0是Object的方法,该方法用来将当前线程置入“预执行队列”中,冰吻在wait()所在的代码处停止执行,直到接到通知或者被中断为止。在调用wait()前,线程必须获得该对象的对象级别锁,即只能在同步方法或者同步块中调用wait(),在执行wait()后,当前线程释放锁。在从wait()返回前,线程与其他线程竞争重新获得锁。如果调用wait()时没有持有适当的锁,则抛出IllegalMonitorStataException,IllegalMonitorStateException是RuntimeException的子类,不需要try..catch语句进行捕捉异常。
notify():
也需要用在同步方法或者同步块中,即在调用前,线程必须获得该对象的对象级别锁,如果调用notify()时没有持有适当的锁,抛出IllegelMonitorStateException。该方法用来通知那些可能等待该对象的对象锁的其他线程,如果有多个线程等待,则有线程规则器随机挑选出一个呈wait状态的线程,对其发通知notify,并使它等待获取该对象的对象锁。在执行notify()后,当前线程不会马上释放该对象锁,呈wait状态的线程也不能马上获取该对象锁,要等待执行notify()方法的线程将程序执行完,也就是退出synchronized代码块后,当前线程才会释放锁,呈wait状态所在的线程才可以获取该对象锁。当第一个获得了该对象锁的wait线程执行完后,它会释放掉该对象锁,此时如果该对象没有在此使用notify语句,则即便该对象已经空闲,其他wait状态等待的线程由于没有得到该对象的通知,还会继续阻塞在wait状态,直到这个对象发出一个notify或者notifyAll。
一句话总结:wait让线程停止运行,notify使停止的线程继续运行。
18.2 synchronized可以将任何一个Object对象作为同步对象来看待,而java为每个Object都实现了wait(0和notify(),他们必须用在被synchronized同步的Object临界区内。通常调用wait(0可以使处于临界区的线程进入等待状态,同时释放被同步对象的锁。
而notify()操作可以唤醒一个因调用了wait()操作而处于阻塞状态中的线程,使其进入就绪状态。被重新唤醒的线程会试图重新获得临界区的控制权,也就是锁,并继续执行临界区内wait之后的代码,如果发出notify操作时没有处理阻塞状态中的线程,那么该命令会被忽略。
wait()可以调用该方法的线程释放共享资源的锁,然后从运行状态退出,进入等待队列,直到被再次唤醒。
notify()可以随机唤醒等待队列中等待同一共享资源的“一个”线程,并使该线程退出等待队列,进入可运行状态,仅仅通知一个线程。
notifyAll()可以使所有正在等待队列中等待同一资源的“全部”线程从等待状态退出,进入可运行状态,此时优先级最高的那个线程最先执行,但也可能是随机执行。这取决于jvm虚拟机的实现。
线程执行步骤:
1.新创建一个新的线程对象后,在调用他的start(),系统会为此线程分配CPU资源,使其处于runnable(可运行)状态,这是一个准备运行阶段,如果线程抢占到CPU资源,此线程就处于running(运行)状态。
2.runnable状态和running状态可相互切换,因为有可能线程运行一段时间后,有其他高优先级的线程抢占了CPU资源,此时线程就从running状态切换到runnable状态。
线程进入runnable的5种情况:
1.调用sleep()后经过的时间超过了指定的休眠时间
2.线程调用的阻塞IO已经返回,阻塞方法执行完毕
3.线程成功的获得了试图同步的监视器
4.线程正在等待某个通知,其他线程发出了通知
5.处于挂起状态的线程调用了resume回复方法。
3.Blocked是阻塞的意思,例如遇到了一个IO操作,此时CPU处于空闲状态,可能会转而把CPU时间片分配给其他线程,这时也可称为“暂停”状态,Blocked结束后,进入runnable状态,等待系统重新分配资源。
出现阻塞的5中情况:
1.线程调用sleep(),主动放弃占用的处理器资源
2.线程调用了阻塞式IO,在该方法返回前,线程被阻塞
3.线程试图获得一个同步监视器,但该同步监视器正被其他线程所持有
4.线程等待某个通知
5.程序调用了suspend方法将该线程挂起,此方法容易导致死锁,尽量避免使用。
4.run()方法运行结束后进入销毁阶段,整个线程执行完毕。
每个锁对象都有两个队列,一个就绪队列,一个阻塞队列。就绪队列存储了将要获得锁的线程,阻塞队列存储了被阻塞的线程。一个线程被唤醒后,才会进入就绪队列,等待CPU调度,反之,一个线程被wait后,就会进入阻塞队列,等待下一次被唤醒。
18.3 wait()被执行后,锁自动被释放;notify()执行后,锁却不会自动释放(notify仅仅是通知)。
18.4 Interrupt()遇到wait()
当线程呈wait状态,调用线程的interrupt()会抛出InterruptedException异常。
在执行同步块中,遇到异常而导致线程终止,锁也会被释放
18.5 只通知一个线程
notify()一次只随机通知一个线程进行唤醒。
当多次调用notify()时,会随机将wait状态的线程唤醒
18.6 唤醒所有线程:notifyAll()
18.7 方法wait(long)使用
wait(long)带一个时间参数,等待某一时间内是否有线程对锁进行唤醒,如果超过这个时间则自动唤醒。
18.8 通知过早
如果通知过早会打乱程序的正常运行逻辑
18.9 等待wait的条件发生变化
如果wait等待的条件发生率变化,也容易造成程序逻辑的混乱。
19 生产者消费者模式 (没看懂)
20.通过管道进行线程间的通信:字节流/字符流
pipeStream用于在不同线程间直接传送数据。一个线程发送数据到输出管道,另一个线程从输入管道中读数据,通过使用管道,实现不同线程间通信,而无需借助类似临时文件之类的东西。
PipedInputStream和PipedOutputStream
PipedReader和PipedWriter
pipedInputStream.connect(pipedOutputStream)/ pipedOutputStream.connect(pipedInputStream);
pipedReader.connect(pipedWriter) / pipedWriter.connect(pipedReader)
21.join()
在很多情况下,主线程创建并启动子线程,如果子线程中要进行大量的耗时运算,主线程将远远早于子线程结束之前结束。这时,如果主线程想等待子线程执行完成之后再结束,如果子线程要处理一个数据,主线程要取得这个数据中的值,就要用到join。
join()作用:等待线程对象销毁
21.1 join与synchronized
join使线程排队运行,内部使用wait进行等待
synchronized使用“对象监视器”原理作为同步
21.2 join()与interrupt()
在join过程中,如果当前线程被中断,则当前线程抛出InterrupedException
21.3 join(long)
参数设定等待时间
21.4 join(long)与sleep(long)区别
join(long)在内部使用wait(long)来实现,所以join(long)具有释放锁的特点
sleep(long) 不释放锁
22 ThreadLocal
变量值的共享可以使用public static变量的形式,所有的线程都使用同一个public static变量。
如果想每一个线程都有自己的共享变量,就需要使用ThreadLocal来解决。
23 InheritableThreadLocal
在子线程中取得父线程继承下来的值。
如果子线程在取得值的同时,主线程将InheritableThreadLocal中的值进行更改,那么子线程取得的值还是旧值。
24 jdk 5以后的Lock也能实现同步
24.1. ReentrantLock
实现线程之间的同步互斥,还具有嗅探锁定,多路分支通知等功能。
ReentrantLock对象的lock()获取锁,unlock()释放锁。
调用lock()就持有了“对象监视器”,其他线程只有等待锁被释放时再次争抢。线程之间还是顺序执行。
24.2 ReentrantLock使用Condition实现等待/通知
Condition在jdk5中出现,可以实现多路通知功能,即在一个Lock对象里可以创建多个Condition(对象监视器)实例,线程对象可以注册在指定的Condition中,从而可以有选择性的进行线程通知,在调度线程上更灵活。
在使用notify()或者notifyAll()进行通知时,被通知的线程时jvm随机选择的,但是用ReentrantLock结合Condition类是默认提供的。
synchronized就相当于整个Lock对象中只有一个单一的Condition对象,所有的线程都注册在它一个对象身上。线程开始notifyAll()时,需要通知所有的waiting线程,没有选择权,会出现相当大的效率问题。
正确使用Condition实现等待/通知:
Object类中的wait()相当于Condition类中的await()方法
Object类的wait(long timeout)相当于Condition类中的await(long time, TimeUnit unit)方法
Object类的 notify()相当于Condition的signal()
Object的notifyAll()相当于Condition的signalAll()
24.3 使用多个Condition实现通知部分线程
24.4 生产者--消费者模式实现一对一交替打印:
package com.xkf.reentrantLock.demo.procon;/*** 生产者-消费者模式:* 一对一交替打印* @author katy.xie**/public class ConditionProducerConsumer {public static void main(String[] args) {ProConService service = new ProConService();ThreadB b = new ThreadB(service);b.start();ThreadC c = new ThreadC(service);c.start();}}
package com.xkf.reentrantLock.demo.procon;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.ReentrantLock;public class ProConService {private ReentrantLock lock = new ReentrantLock();private Condition condition = lock.newCondition();private Boolean hasvalue = false;public void set(){try {lock.lock();while(hasvalue == true){condition.await();}System.out.println("----");hasvalue = true;condition.signal();} catch (Exception e) {e.printStackTrace();}finally{lock.unlock();}}public void get(){try {lock.lock();while(hasvalue == false){condition.await();}System.out.println("++++");hasvalue = false;condition.signal();} catch (Exception e) {e.printStackTrace();}finally{lock.unlock();}}}
package com.xkf.reentrantLock.demo.procon;public class ThreadB extends Thread{private ProConService service ;public ThreadB(ProConService service) {this.service = service;}@Overridepublic void run() {for (int i = 0; i < 10; i++) {service.set();}}}
package com.xkf.reentrantLock.demo.procon;public class ThreadC extends Thread{private ProConService service ;public ThreadC(ProConService service) {this.service = service;}@Overridepublic void run() {for (int i = 0; i < 10; i++) {service.get();}}}
24.5生产者--消费者模式实现多对多交替打印:
package com.xkf.reentrantLock.demo.procon;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.ReentrantLock;public class AlternateProConService {private ReentrantLock lock = new ReentrantLock();private Condition condition = lock.newCondition();private Boolean hasValue = false;public void produce(){try {lock.lock();while(hasValue == true){System.out.println("有可能++连续");condition.await();}System.out.println("打印++");hasValue = true;condition.signalAll();} catch (Exception e) {e.printStackTrace();}finally{lock.unlock();}}public void consume(){try {lock.lock();while(hasValue == false){System.out.println("有可能--连续");condition.await();}System.out.println("打印--");hasValue = false;condition.signalAll();} catch (Exception e) {e.printStackTrace();}finally{lock.unlock();}}}
package com.xkf.reentrantLock.demo.procon;/*** 生产者---消费者模式:交替打印* @author katy.xie**/public class AlternateProCon {public static void main(String[] args) {AlternateProConService service = new AlternateProConService();ThreadD[] d = new ThreadD[10];ThreadE[] e = new ThreadE[10];for (int i = 0; i < 10; i++) {d[i] = new ThreadD(service);e[i] = new ThreadE(service);d[i].start();e[i].start();}}}
package com.xkf.reentrantLock.demo.procon;public class ThreadD extends Thread{private AlternateProConService service ;public ThreadD(AlternateProConService service) {this.service = service;}@Overridepublic void run() {for (int i = 0; i < 10; i++) {service.produce();}}}
package com.xkf.reentrantLock.demo.procon;public class ThreadE extends Thread{private AlternateProConService service ;public ThreadE(AlternateProConService service) {this.service = service;}@Overridepublic void run() {for (int i = 0; i < 10; i++) {service.consume();}}}
26 公平锁与非公平锁
Lock分为公平锁和非公平锁。
公平锁:表示线程获取锁的顺序时按照线程加锁的顺序来分配的,即先来先得。
非公平锁:获取锁的抢占机制,是随机获得锁的,这个方式可能造成某些线程一直拿不到锁,也就造成不公平。
27.int getHoldCount() 查询当前线程保持锁定的个数,也就是调用lock()的次数
28. int getQueueLength()返回正在等待获取此锁定的线程估计数
29.int getWaitQueueLength(Condition condition) 返回等待与此锁定相关的给定条件Condition的线程估计数
30. boolean hasQueuedThread(Thread thread)查询指定的线程是否正在等待此锁定
31.boolean hasQueuedThreads() 查询是否有线程正在等待获取此锁定。
32.boolean hasWaiters(Condition condition) 是否有线程正在等待与此锁定有关的condition条件
33.boolean isFair() 判断是不是公平锁
34. boolean isHeldByCurrentThread() 当前线程是否保持此锁定
35.boolean isLocked() 查询此锁定是否由任意线程保持
36. void lockInterruptibly()如果当前线程没有被中断,则获取锁定,如果已经中断则抛出异常。
37.boolean tryLock() 仅在调用时锁定未被另一个线程保持的情况下,才获取该锁定
38.boolean tryLock(long timeout, TimeUnit unit) 如果锁定在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁定。
39. void awaitUninterruptibly()
调用的前提,当前线程已经成功获得与该条件对象绑定的重入锁,否则会抛出IllegalMonitorStateException。
调用结束后,结束等待的唯一方法是其他线程调用该条件对象的signal()或signalAll()。
等待过程中如果当前线程被中断,该方法仍会继续等待,同时保留该线程的中断状态。
package com.xkf.reentrantLock.demo.awaitUninterruptibly;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.ReentrantLock;public class MyService {private ReentrantLock lock = new ReentrantLock();private Condition condition = lock.newCondition();public void testMethod(){try {lock.lock();System.out.println("wait begin");condition.awaitUninterruptibly();System.out.println("wait end");} catch (Exception e) {e.printStackTrace();}finally{lock.unlock();}}}
package com.xkf.reentrantLock.demo.awaitUninterruptibly;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.ReentrantLock;public class MyService {private ReentrantLock lock = new ReentrantLock();private Condition condition = lock.newCondition();public void testMethod(){try {lock.lock();System.out.println("wait begin");condition.awaitUninterruptibly();System.out.println("wait end");} catch (Exception e) {e.printStackTrace();}finally{lock.unlock();}}}
package com.xkf.reentrantLock.demo.awaitUninterruptibly;public class MyThread extends Thread{private MyService service;public MyThread(MyService service) {this.service = service;}@Overridepublic void run() {service.testMethod();}}
40.awaitUntil()
41.使用Condition实现顺序执行
使用Condition对象可以对线程执行的业务进行排序规划。
package com.xkf.reentrantLock.demo.awaitUninterruptibly;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.ReentrantLock;/*** 使用Condition对象对线程执行业务进行排序规划* @author katy.xie**/public class ConditionPerformOrder {volatile private static int nextPrintWho = 1;private static ReentrantLock lock = new ReentrantLock();final private static Condition conditionA = lock.newCondition();final private static Condition conditionB = lock.newCondition();final private static Condition conditionC = lock.newCondition();public static void main(String[] args) {Thread threadA = new Thread(){@Overridepublic void run() {try {lock.lock();while(nextPrintWho != 1){conditionA.await();}for (int i = 0; i < 3; i++) {System.out.println("ThreadA :" + (i + 1));}nextPrintWho = 2;conditionB.signalAll();} catch (Exception e) {e.printStackTrace();}finally{lock.unlock();}}};Thread threadB = new Thread(){@Overridepublic void run() {try {lock.lock();while(nextPrintWho != 2){conditionB.await();}for (int i = 0; i < 3; i++) {System.out.println("ThreadB :" + (i+ 1));}nextPrintWho = 3;conditionC.signalAll();} catch (Exception e) {e.printStackTrace();} finally{lock.unlock();}}};Thread threadC = new Thread(){@Overridepublic void run() {try {lock.lock();while(nextPrintWho != 3){conditionC.await();}for (int i = 0; i < 3; i++) {System.out.println("ThreadC :" + (i + 1));}nextPrintWho = 1;conditionA.signalAll();} catch (Exception e) {e.printStackTrace();} finally{lock.unlock();}}};Thread[] aThreadArray = new Thread[5];Thread[] bThreadArray = new Thread[5];Thread[] cThreadArray = new Thread[5];for (int i = 0; i < 5; i++) {aThreadArray[i] = new Thread(threadA);bThreadArray[i] = new Thread(threadB);cThreadArray[i] = new Thread(threadC);aThreadArray[i].start();bThreadArray[i].start();cThreadArray[i].start();}}}
42.ReentrantReadWriteLock
ReentrantLock具有完全排斥其他的效果,即同一时间只有一个线程在执行ReentrantLock.lock()后面的任务。
这样做虽然保证了实例变量的线程安全性,但是效率却很低下。
ReentrantReadWriteLock读写锁,在某些不需要操作实例变量的方法中,完全可以使用ReentrantReadWriteLock提升运行效率。
ReentrantReadWriteLock包含读操作相关的锁,也称共享锁,写操作锁,也叫排他锁。
多个读锁之间不排斥,读锁与写锁互斥,写锁与写锁互斥。
在没有线程Thread进行写入操作时,进行读取操作的多个Thread都可以获取读锁,而进行写入操作的Thread只有在获取写锁后才能进行写入操作。
多个Thread可以同时进行读取操作,但是同一时刻只允许一个Thread进行写入操作。
43.定时器Timer
Timer主要作用是设置计划任务,即在指定的时间开始执行某一个任务
封装任务的类是TimerTask,执行计划任务的代码要放在TimerTask里, TimerTask是一个抽象类。
schedul(TimerTsk task, Date time):在指定的日期执行一次某一任务。
TimerTask是以队列的方式一个个被顺序执行,所以执行时间有可能也与语气的时间不一致,因为前面的任务有可能消耗的时间较长,则后面的任务运行时间也会被延迟。
schedule(TimerTask task, Date firstTime, long period)
在指定的日期后,按照指定的间隔周期性的无限循环地执行某一任务。
schedule(TimerTask task, long delay): 以当前时间为参考,在此基础上延迟指定的毫秒数后执行一次TimerTask任务。
schedule(TimerTask task, long delay, long period):以当前时间为参考,在此基础上延迟指定的毫秒数,再以某一间隔时间无限次数地执行某一任务。
TimerTask 的 cancel() 将自身从队列任务中清除,其他任务不受影响。
Timer的cancale() 将任务队列中的全部任务清除,有时并不一定停止执行计划任务,而是正常执行。
scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
如果执行任务的时间没有延迟,那么下一次执行任务的时间是参考上一次任务的“开始”时间来计算。
scheduleAtFixedRate(TimerTask task, Date firstTime, long period) 与 schedule(TmerTask task, Date firstTime, long period) 比较:
1.都会按照顺序执行,所以不要考虑非线程安全的情况。
2.主要区别在于不延时的情况:
scheduleAtFixedRate如果不延时,那么下一次执行任务的时间参考上一次任务开始的时间。
延时的情况没有区别,即如果执行任务的时间被延时,那么下一次执行任务的时间是按照上一次任务结束的时间来计算。
schedule方法不具有追赶执行性,scheduleAtFixedRate具有追赶执行性(设置的时间比当前时间早的情况)。
44.单例模式与多线程
立即加载/饿汉模式:立即加载就是使用类的时候已经将对象创建完毕,常见的办法是直接new实例化
/*** //立即加载/饿汉模式* @author katy.xie**/public class Singleton {private static Singleton singleton = new Singleton();private Singleton() {}public static Singleton getInstance(){return singleton;}}
延迟加载/懒汉模式:在调用方法时实例才被创建。
利用DCL双检查锁机制来实现多线程环境中的延迟加载单例设计模式
public class Singleton {private static Singleton singleton;private Singleton(){}public static Singleton getInstance(){try {if(singleton != null){}else{Thread.sleep(3000);synchronized (Singleton.class) {if(singleton == null){singleton = new Singleton();}}}} catch (Exception e) {e.printStackTrace();}return singleton;}}
使用静态内置类实现单例模式
public class Singleton {//静态内置类private static class SingletonHandler{private static Singleton singleton = new Singleton();}private Singleton(){}public static Singleton getInstance(){return SingletonHandler.singleton;}}
序列化与反序列化的单例模式实现
public class Singleton implements Serializable{private static final long serialVersionUID = 1L;//内部类方式private static class SingletonHandler {private static final Singleton singleton = new Singleton();}private Singleton(){}public static Singleton getInstance(){return SingletonHandler.singleton;}}
使用static代码块实现单例模式
public class Singleton{private static Singleton singleton = null;private Singleton(){}static{singleton = new Singleton();}public static Singleton getInstance(){return singleton;}}
使用enum枚举数据类型实现单例模式
枚举enmu和静态代码块的特性相似,在使用枚举时,构造方法会被自动调用,也可以应用其这个特性实现单例设计模式。
45.线程状态
NEW:线程实例化后还没有执行start()的状态
RUNNABLE:线程进入运行的状态(就绪)
TERMINATED:线程被销毁时的状态
TIMED_WAITING:线程执行了sleep(),呈等待状态,等待时间到达,继续向下运行。
BLOCKED:出现在某个线程在等待锁的时候。
WAITING:执行了wait()方法
46. 线程组
可以批量的管理线程或者线程组对象,有效的对线程或者线程组对象进行组织。
46.1 线程对象关联线程组:1级关联
即父对象中有字对象,但并不创建子孙对象。这种情况经常出现在开发中,比如创建一些线程时,为了有效的对这些线程进行组织管理,通常的情况下是创建一个线程组,然后再将部分线程归属到该组中,这样的处理可以对线程对象进行有效的组织与规划。
46.2 线程对象关联线程组:多级关联
多级关联:父对象中有子对象,子对象中再创建子对象,也就是出现子孙对象,但这种写法在开发不常见,如果线程树结构设计的非常复杂反而不利于线程对象管理,但JDK提供了支持多级关联的线程树结构。
46.3 线程组自动归属属性
自动归属就是自动归到当前的线程组中。
46.4 获取根线程组
46.5 线程组里加线程组
46.6 组内的线程批量停止
46.7 递归与非递归取得组内对象
47. 使线程具有有序性
48.SimplaDateFormat非线程安全
49. 线程中出现异常的处理
使用UncaughtExceptionHandler捕捉。
setUncaughtExceptionHandler()对指定的线程对象设置默认的异常处理器
如果觉得《Java多线程编程核心技术》对你有帮助,请点赞、收藏,并留下你的观点哦!