第一章 Java多线程技能
第二章 对象及变量的并发访问
第三章 线程间通信
第四章 Lock对象的使用
第五章 定时器Timer
第六章 单例模式与多线程
第七章 拾遗增补
第一章 Java多线程技能
1. 单任务的特点是排队执行。
单任务是一个命令执行完才能执行下一个任务
2. 多任务是同一时间可以执行多个任务
任务之间可以来回切换,系统和CPU的运行效率大大提升
3. 多线程也就是在使用异步(多线程是异步的)
4. 实现多线程有两种方式:继承Thread类和实现Runnable接口
5. 线程的优先级具有继承性和传递性
可以通过setPriority()方法设置优先级
优先级范围:1 ~ 10
6. sleep()
Thread.sleep(millis) 当使用这个方法的时候可以运行其他线程
7. start() 和 run()
当运行---.start()的时候,会自动启动---.run() 方法
线程中使用---.start() 是跳到---线程,若使用---.run() 使用的还是当前线程
8. Thread.currentThread().getName() 方法和this.getName()
Thread.currentThread().getName() 目前正在执行这条语句的线程名
this.getName() 创建这个对象得线程名(一般为默认的)
9. 判断线程是否处于中断状态
this.interrupted() 测试当前所在线程是否中断(第一次判断后会,中断状态会清除)
this.isInterrupted() 测试的是这个this线程是否已经终端
10. synchronized关键字
此关键字可以对任意对象及方法加锁,加锁这段代码称之为"互斥区"或"临界区"
11. println方法
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}
此方法内部也加了锁,所以当使用---.suspend() 暂停的时候没如果正在进行输出活动,可能会锁在里面出不开
12. yield() 方法
作用:放弃当前的CPU资源.
第二章 对象及变量的并发访问
1. 方法内的变量为线程安全的
两个线程使用同一个类的时候,类中的方法中的局部变量是线程安全的,因为方法内部的变量具有私有特性
2. 关键字synchronized的锁都是对象锁不是锁定方法
3. 调用关键字synchronized声明的方法一定是排队运行的
4. 同一个对象中的不同方法
如果其中有一个synchronized或者没有的话,可以异步调用两个方法
如果两个方法都有synchronized修饰的话,只能一个一个排队等待,也就是同步
5. 脏读
发生脏读的原因: 当读取实例变量时,此值已经被其他线程更改过了。
脏读的解决:也是通过synchronized修饰方法解决的。
6. synchronized锁重入
解读:一个对象调用了一个synchronized修饰的方法,此时这个对象锁还没有释放,还可以获得其他synchronized修饰的方法。
锁重入支持父子类继承:当存在父子关系时(extends),子类完全可以通过锁重入调用父类的同步方法
7. 当出现异常的时候,锁可以自动释放
8. suspend()和sleep(millis)方法被调用后并不释放锁
9. 重写的方法(extends)如果没加synchronized,视为异步
父类使用了synchronized修饰方法,子类重写的时候没有添加synchronized修饰该方法,视为可以异步调用
10. public static boolean holdsLock(Boject obj)
System.out.println("A " + Thread.currentThread().holdsLock(Test.class));
synchronized(Test.class) {
System.out.println("B " + Thread.currentThread().holdsLock(Test.class));
}
System.out.println("C " + Thread.currentThread().holdsLock(Test.class));
输出 A false B true C false
当currentThread在指定的对象上保持锁定时,才返回true
11. 一半异步,一半同步
想要计算的内容可以让它异步,想输出的内容可以使用同步代码块synchronized(this){} 输出,这样可以做到一半同步,一半异步。
12. synchronized代码块具有同步性
当线程访问object的一个synchronized(this)同步代码块的时候,其他线程对同一个object中所有其他synchronized(this)同步代码块的访问将被阻塞。
13. println()方法也是同步的
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}
14. synchronized(this) 也是锁定当前对象的
synchronized(this)也是锁定的当前对象,但是可以访问异步访问当前对象其他没有被synchronized修饰的方法。
15. synchronized同步方法和synchronized(this)同步代码块的作用
1. 对其他synchronized同步方法或者synchronized(this)同步代码块调用呈同步效果
2. 同一时间只有一个线程可以执行synchronized同步方法/synchronized(this)同步代码块中的代码
16. synchronized(非this对象x)同步代码块的作用
当多个线程争抢相同的"非this对象x"的锁时,同一时间只有一个线程可以执行synchronized(非this对象x)同步代码块中的代码
注意:当非this对象x位于方法内(也就是局部变量)的时候,多个锁就是异步执行
17. 三个结论
当多个线程同时执行synchronized(x){}同步代码时呈同步效果
当其他线程执行x对象中synchronized同步方法时呈同步效果
当其他线程执行x对象方法里面的synchronized(this)代码块呈现同步效果
18. 每一个*.java文件对应Class类的实例都是一个,在内存中是单例的
19. 静态同步synchronized方法与synchronized(class)代码块
synchronized关键字加到static静态方法上的方式是将Class类对象作为锁,而synchronized关键字加到非static静态对象上的方式时间将方法所在类的对象作为锁
20. 同步syn static方法可以对类的所有对象实例起作用
21. 同步syn(class)代码块可以对类的所有实例起作用
同步syn static方法其实和同步syn(class)代码块的作用是一样的(synchronized(类.class))
22. 可以使用同步块来解决synchronized方法无限等待问题
23. 多线程死锁
死锁是程序设计的Bug,在设计程序时要避免双方互相持有对方的锁,只要互相等待对方的释放锁,就有可能出现死锁
24. 所对象改变导致异步执行
synchronized(对象)
25. volatile关键字
三种特性:可见性,原子性,进制代码重排序
只能修饰变量
可见性:指A线程更改变量的值后,B线程立马能看待更改过后的值
26. synchronized关键字也具有volatile关键字的三个特性
第三章 线程间通信
1. wait/notify机制的原理
wait()方法的作用是使当前执行wait()方法的线程等待,在此处所在的代码暂停执行,并释放锁,知道接到通知或者终端为止。
notify()方法用来通知那些可能等待的锁,如果有多个线程等待,则根据执行wait()方法的顺序发出通知,并使该线程重新获得锁,执行notify()方法后,并不是当前线程马上释放改锁,直到退出synchronized同步区域后,当前线程才会释放锁,而呈wait状态的线程才可以获得该对象锁
只能在同步方法中嗲欧总wait()方法和notify()方法
2. notiftAll()方法
按照执行wait()方法相反的顺序依次唤醒全部的线程
3. 关于notify()方法和notifyAll()方法
不是所有的JVM在执行notify()都是按照wait()方法的正序进行唤醒的,也不是所有的JVM在执行notifyAll()都是按照wait()方法的倒叙序进行唤醒的,具体的唤醒顺序依赖JVM的具体实现
4. wait(long)方法
带一个参数的此方法的功能是等待某一段时间内是否有线程对锁进行notify()通知唤醒,如果超过这个时间点则线程会自动唤醒,能继续向下运行的前提是再次持有锁。
5. 假死
就是全部线程都进出wait()状态,出现假死状态很大的可能是唤醒了同类
6. 实现任意数量的几对几生产与消费
使用while结合notifyAll()的方法,这种组合具有通用性
7. 通过管道进行线程间通信—字节流
字节流用到的类有PipedInputStream和PipedOutputStream
字符流用到的类有PipedReader和PipedWriter
8. join()方法的使用
join()方法的作用是使所属的线程对象x正常执行run()方法中的任务,而使当前线程z进行无限期的阻塞,等待线程x销毁后执行z后面的代码,具有串联执行的效果
注意:join()方法和interrupt()方法如果彼此遇到,则出现异常
x.join(long)方法中的参数用于设定等待的时间,不管x线程是否执行完毕,时间到了并且重新获得锁,则当前线程会继续向后运行,如果没有重新获得锁,则一直在尝试
9. join(long)方法和sleep(long)方法的区别
join(long)方法的内部是使用的wait(long)方法来进行实现的,说明会释放锁,而sleep(long)不会 释放锁
10. 使用InheritableThreadLoval可使子线程继承父线程的值
11. 通过重写childValue()方法,子线程可以对父线程继承的值进行修改
通过InheritableThreadLocalExe.set()方法和childValue()都可以对值进行修改,区别:InheritableThreadLocalExe.set()方法可以任意时刻进行修改,而重写childValue()方法实现子线程具有最新值是只有在创建子线程是才会发生,但是仅仅是一次
第四章 Lock对象的使用
1. 使用ReentrantLock对象可以实现同步
调用ReentrantLock对象的lock()锁可以获取锁,调用unlock()方法释放锁,这两个方法成对出现,要想实现一些代码的同步,可以把这些代码放在这两个方法之间
2. Condition对象
Condition对象的作用是控制并处理线程的状态。
3. await()方法
await()方法的作用使当前线程在接到通知或被中断之前一直处于wait状态,他和wait()的作用是一样的
注意:在调用---await()方法之前需要先获得锁lock.lock()
4. 使用await()方法和signal()实现wait()/notify机制
Object类中的wait()方法相当于Condition类中的await()方法
Object类中的wait(long timeout)方法相当于Condition类中的await(long time,TimeUnit unit)方法
Object类中的notify()方法相当于Condition类中的signal()方法
Object类中的notifyAll()方法相当于Condition类中的signalAll()方法
5. await()方法实现暂停效果的原理
内部使用了UNSAFE.park(false, 0L)
6. 唤醒部分线程的方法
Condition对象可以唤醒部分指定线程,可以对线程进行分组,然后唤醒指定组中的线程
7. 公平锁和非公平锁
公平锁:采用先到先得的策略,每次获取锁之前都会检查队列里面有没有排队等待的线程,没有才会尝试获取锁,如果有就将当前线程追加到队列中
非公平锁:采用“有机会插队”的策略,一个线程获取锁之前先去尝试获取锁而不是在排队中等待,如果获取锁成功,说明线程虽然是后启动的,但先获得了锁,这就是“作弊插队的效果。
8. getHoldCount()方法是查询当前线程保持此锁定的个数,也就是调用lock()的次数
9. getQueueLength()方法的使用
返回整等待获取此锁的线程估计数
10. getWaitQueueLength(Condition)方法的使用
返回等待与此锁相关的给定条件Condition的线程估计数,也就是返回有多少个线程执行了condition对象中的await()方法而呈等待状态
11. hasQueuedThread(Thread)方法的使用
查询指定的线程是否正在等待获取此锁,也就是判断参数中的线程是否在等待队列中
12. hasQueueThreads() 方法的使用
查询是够有线程正在等待获取此锁,也就是等待队列中是否有等待线程
13. hasWaiters(Condition)方法的使用
查询是否有线程正在等待与此锁有关的condition条件,也就是是否有线程执行了condition对象中的await()方法而呈等待状态
14. isFair()方法的使用
判断是不是公平锁
15. 默认状态下ReentrantLock类使用的是非公平锁
16. isHeldByCurrentThread()方法的使用
查询当前线程是否保持此锁
17. isLocked()方法的使用
查询此锁是否由任意线程保持并没有释放
18. lockInterruptibly()方法的使用
当某个线程尝试获取锁并且阻塞在lockInterruptibly()方法时,该线程可以被中断
19. tryLock()方法的使用
嗅探拿锁,如果当前线程发现锁在被其他线程持有,则返回false,程序继续执行后面的代码,而不是呈阻塞等待锁的状态
20. tryLock(long timeout, Timeunit unit)方法的使用
如果当前线程在指定的timeout内持有了锁,则返回true,超过时间则返回false,参数timeout代表当前线程抢锁的时间
21. await(long time, TimeUnit unit)方法的使用
作用和wait(long timeout)方法的作用一样,都具有唤醒线程的功能
22. awaitNanos(long nanosTimeout)方法的使用
和wait(long timeout)的作用一样,都具有唤醒线程的功能,时间单位是纳秒(ns)
23. awaitUntil(Date deadline)方法的使用
指定的Date结束等待
24. awaitUninterruptibly()方法的使用
实现线程在等待的过程中,不允许被打断
await()方法是可以被打断的
25. 使用ReentrantReadWriteLock类(这是一种读写锁)(读锁之间可以共享)
读写锁有两个锁:一个是读操作相关的锁,也称为共享锁;另一个是写操作相关的锁,也称为排他锁
注意:读锁之间不互斥,读锁和写锁互斥,写锁和写锁互斥,因此只要出现写锁,就会出现互斥同步的效果
1. ReentrantLock类与ReentrantReadWriteLock类相比,ReentrantLock类的主要缺点是使用ReentrantLock对象时,所有的操作都同步,哪怕只是对实例变量的读操作。
2. 总结:读写互斥,写读互斥,谢谢互斥,读读异步
第五章 定时器Timer
1. 定时器Timer的使用
1. Timer类的主要作用是设置计划任务
2. TimerTask类的主要作用是封装任务
3. 执行计划任务的代码要放入TimerTask的子类中,因为TImerTask是一个抽象类
2. schedule(TimerTask task, Date time)方法的测试
3. 使用public void cancel()方法可以实现线程TImerThread销毁
作用:终止此计时器,丢弃所有当前已安排的任务,这不会干扰当前正在执行的任务(如果存在)
※ 可以重复调用此方法,但是第二次和后续调用无效
4. 如果执行任务的时间早于当前时间,则立刻执行task任务
5. schedule(TimerTask task, Date firstTime, long period)方法
作用:指定日期之后按指定的间隔周期无线循环地执行某一任务
6. Timer类的cancel()方法
和 TImerTask类中的cancel()方法清除自身不同,Timer类中的cancel方法的作用是将任务队列中的全部任务清空
7. Timer类中的cancel()方法并不一定会停止计划任务
原因:Timer中的cancel()方法有事并没有正抢到queue锁
8. schedule(TimerTask task, long delay)方法
以当前时间为参考,在此时间的基础上延迟指定的毫秒数后执行一次TimerTask任务
9. schedule(TimerTask task, long delay, long period)方法
在此时间的基础上延迟指定的毫秒数再以某一间隔时间无限次数地执行某一任务
10. 任务的延时和不延时
任务的不延时:第一次执行任务的时间是任务的开始时间加上delay时间,接下来执行任务的时间是上一次任务的开始时间加上period时间
任务延时:下一次任务的执行时间参考上一次任务"结束"时的时间来开始的
11. scheduleAtFixedRate()方法和schedule()方法的区别
scheduleAtFixedRate()方法具有追赶型
比如我任务开始的时间早于当前时间,schedule()方法会在当前时间进行线程计时运行,但是scheduleAtFixedRate()方法会先追平(弥补)回来,再从当前点接着执行
第六章 单例模式与多线程
1. 立即加载/饿汉模式
public classs MyObject{
private static MyObject myobject = new MyObject();
private MyObject(){}
public static MyObject getInstance() {
return MyObject;
}
}
※ 此版本为立即加载型,但此版本代码的缺点是不能有其他实例变量,因为getInstance()方法没有同步,所以有可能出现非线程安全问题
2. 延迟加载/懒汉模式
public classs MyObject{
private static MyObject myobject;
private MyObject(){}
public static MyObject getInstance() {
if(myobject != null) {
} else {
myobject = new MyObject();
}
return myobject;
}
}
缺点:多线程环境下会出现“错误的单例”创建出“多例”的情况
3. DCL机制(Double-Check Locking, 双检查锁)
DCL机制是大多数对县城结合单例模式使用的解决方案
※ 使用volatile修饰变量使该变量在多个线程间达到可见性,另外也禁止了初始化的时候的代码重排序
4. 使用静态内置类实现单例模式
public class MyObject {
private static class MyObjectHandler {
private static MyObject myObject = new MyObject();
}
private MyObject(){}
public static MyObject getInstance() {
return MyObjectHandler.myObject;
}
}
5. 使用static代码块实现单例模式
public class MyObject {
private static MyObject instance = null;
private MyObject(){}
static {
instance = new MyObject();
}
public static MyObject getInstance() {
return instance;
}
}
6. 使用enum枚举数据类型实现单例模式
第七章 拾遗增补
1. 线程的状态
NEW: 线程实例化后还未执行start()方法
RUNNABLE状态下,线程进入运行状态
TERMINATED状态下,线程被销毁了
TIMED_WAITING状态下,代表线程执行了Thread.sleep()方法,呈等待状态
BLOCKED状态是某个线程在等待锁的时候
WAITING状态是线程执行了Object.wait()方法后的状态
2. 线程组
线程对象关联线程组:一级关联: 就是父对象中有子对象,但并不创建子孙对象
线程对象关联线程组:多级关联:就是父对象中有个子对象,子对象中再创建子对象,即子孙对象
可以实现多级关联的关键代码是ThreadGroup类的构造方法
※ 在实例化一个ThreadGroup线程组x时,如果不指定所属的线程组,则x自动归属到当前线程对象所属的线程组中
※ 通过把线程归属到线程组中,当调用线程组ThreadGroup的interrupt()方法时可以中断该组中所有正在运行的线程
3. activeGroupCount() 方法
取得子孙组的数量
4. Thread.activeCount()方法
返回当前线程的线程组中活动线程的数量
5. Thread.enumerate(Thread tarray[])方法的使用
Thread.enumerate(Thread tarray[])方法的作用是将当前线程的线程组及其子组中的每一个活动线程复制到指定的数组中。