[关闭]
@contribute 2016-01-19T14:31:48.000000Z 字数 8724 阅读 1812

java多线程变成核心技术--读书笔记

java


第1章 java多线程技能

1.1 进程与线程的概念

1.2 线程的使用

1.3 Thread中的start()方法的理解

  1. start方法的调用,并不代表这个线程就开始运行,执行run中的代码。而是通知系统安排一个时间来此线程,什么时候执行并不确定,在CPU不繁忙的情况下,就会来执行此线程。代码中的start()顺序并不能代表线程的执行顺序。
  2. 如果多次调用start()方法,则会出现异常Exceptionin thread"main"java.lang.IllegalThreadStateException。

1.4 currentThread()

是Thread中的一个静态方法,用于返回当前执行对象的引用。

1.5 isAlive()

  1. 线程处于准备运行状态或正在运行状态,就认为线程是“存活”的。
  2. 线程处于等待状态时线程是“存活”的吗?
  3. 处于睡眠状态是存活的吗?
  4. 处于阻塞状态呢?

1.6 sleep()

让正在执行的线程等待,“正在执行的线程”是指:this.currentThread()返回的线程。

1.7 getId()

获取线程的唯一标识。

1.8 停止线程

interrupt()方法并不能终止线程。调用此方法仅仅是在当前中打一个停止的标记,并不是真的停止线程。但是如果线程处于等待状态或睡眠状态时,此方法的调用会导致线程抛出InterruptException异常,从而导致线程停止。

1.9 this.interrupted()和this.isInterrupted()方法的区别。

1.10 yield()

放弃当前的CPU资源,让给其他的任务去占用CPU资源。放弃的时间不确定,有可能刚放弃CPU资源,又立马获得。

1.11 守护线程

当进程中不存在非守护线程了,则守护线程自动销毁。典型的守护线程就是垃圾回收线程,当进程中没有非守护线程了,则垃圾回收线程也就没有存在的必要了,自动销毁。 用个比较通俗的比喻来解释一下“守护线程”:任何一个守护线程都是整个JVM中所有非守护线程的“保姆”,只要当前JVM实例中存在任何一个非守护线程没有结束,守护线程就在工作,只有当最后一个非守护线程结束时,守护线程才随着JVM一同结束工作。Daemon的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是GC(垃圾回收器),它就是一个很称职的守护者。

1.12 wait()

wait(): 类似sleep(), 不同的是,wait()会先释放锁住的对象,然后再执行等待的动作。注意,这个函数属于Object类。另外,由于wait()所等待的对象必须先锁住,因此,它只能用在同步化程序段或者同步化方法内,否则,会抛出异常IllegalMonitorStateException.


第2章 对象及变量的并发访问

2.1 synchronized

2.1.1 synchronized方法

  1. 关键字synchronized拥有锁重入的功能,也就是在使用synchronized时,当一个线程得到一个对象锁后,再次请求此对象锁时是可以再次得到该对象的锁的。这也证明在一个synchronized方法/块的内部调用本类的其他synchronized方法/块时,是永远可以得到锁的。
  2. 当存在父子类继承关系时,子类是完全可以通过“可重入锁”调用父类的同步方法的。同步不具有继承性。
  3. 当一个线程执行的代码出现异常时,其所持有的锁会自动释放。
  4. 缺点:当两个并发线程访问同一个对象object中的synchronized(this)同步代码块时,一段时间内只能有一个线程被执行,另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。倘若,如果此同步方法中的大部分的执行代码并不影响同步的逻辑而又耗时很长,那么缺点就更加明显。所以,在此情况下应当采用synchronized同步代码块。

2.1.2 synchronized代码块

  1. 在使用同步synchronized(this)代码块时需要注意的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对同一个object中所有其他synchronized(this)同步代码块的访问将被阻塞,这说明synchronized使用的“对象监视器”是一个。
  2. synchronized(this)代码块是锁定当前对象的。

2.1.3 synchronized方法与synchronized代码块的区别

  • synchronized(this)代码块是锁定当前对象的
    • 对其他synchronized同步方法或synchronized(this)同步代码块调用呈阻塞状态。
    • 同一时间只有一个线程可以执行synchronized同步方法中的代码。
  • synchronized(this)同步代码块
    • 对其他synchronized同步方法或synchronized(this)同步代码块调用呈阻塞状态。
    • 同一时间只有一个线程可以执行synchronized(this)同步代码块中的代码。

2.1.4 synchronized(非this对象x)

  • 在多个线程持有“对象监视器”为同一个对象的前提下,同一时间只有一个线程可以执行synchronized(非this对象x)同步代码块中的代码。
  • 当持有“对象监视器”为同一个对象的前提下,同一时间只有一个线程可以执行synchronized(非this对象x)同步代码块中的代码。
  • 使用“synchronized(非this对象x)同步代码块”格式进行同步操作时,对象监视器必须是同一个对象。如果不是同一个对象监视器,运行的结果就是异步调用了,就会交叉运行。
  • 当多个线程同时执行synchronized(x){}同步代码块时呈同步效果。
  • 当其他线程执行x对象中synchronized同步方法时呈同步效果。
  • 当其他线程执行x对象方法里面的synchronized(this)代码块时也呈现同步效果。
  • 优点:如果在一个类中有很多个synchronized方法,这时虽然能实现同步,但会受到阻塞,所以影响运行效率;但如果使用同步代码块锁非this对象,则synchronized(非this)代码块中的程序与同步方法是异步的,不与其他锁this同步方法争抢this锁,则可大大提高运行效率。

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

  1. 关键字synchronized还可以应用在static静态方法上,如果这样写,那是对当前的*.java文件对应的Class类进行持锁。
  2. 静态同步synchronized方法和将synchronized关键字加到非static方法上使用的效果是一样的。其实还是有本质上的不同的,synchronized关键字加到static静态方法上是给Class类上锁,而synchronized关键字加到非static静态方法上是给对象上锁。
  3. Class锁可以对类的所有对象实例起作用。
  4. synchronized(Object)中的Object避免使用String

2.2 volatile关键字

关键字volatile的主要作用是使变量在多个线程间可见。强制从公共堆栈中取得变量的值,而不是从线程私有数据栈中取得变量的值。
一个普通的对象的普通方法中执行死循环,靠判断变量跳出死循环。外部设置这个的对象的变量来达到跳出巡循环的目的是行不通的。这是因为私有堆栈中的值和公共堆栈中的值不同步造成的。解决这样的问题就要使用volatile关键字了。
使用volatile关键字增加了实例变量在多个线程之间的可见性。但volatile关键字最致命的缺点是不支持原子性。

2.2.1 synchronized和volatile比较

  1. 关键字volatile是线程同步的轻量级实现,所以volatile性能肯定比synchronized要好,并且volatile只能修饰于变量,而synchronized可以修饰方法,以及代码块。随着JDK新版本的发布,synchronized关键字在执行效率上得到很大提升,在开发中使用synchronized关键字的比率还是比较大的。
  2. 多线程访问volatile不会发生阻塞,而synchronized会出现阻塞。
  3. volatile能保证数据的可见性,但不能保证原子性;而synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存和公共内存中的数据做同步。
  4. 关键字volatile解决的是变量在多个线程之间的可见性;而synchronized关键字解决的是多个线程之间访问资源的同步性。
  5. 关键字volatile主要使用的场合是在多个线程中可以感知实例变量被更改了,并且可以获得最新的值使用,也就是用多线程读取共享变量时可以获得最新值使用。
  6. 关键字volatile主要使用的场合是在多个线程中可以感知实例变量被更改了,并且可以获得最新的值使用,也就是用多线程读取共享变量时可以获得最新值使用。关键字volatile提示线程每次从共享内存中读取变量,而不是从私有内存中读取,这样就保证了同步数据的可见性。但在这里需要注意的是:如果修改实例变量中的数据,比如i++,也就是i=i+1,则这样的操作其实并不是一个原子操作,也就是非线程安全的。将此变量用volatile关键字修饰也不能达到线程安全的目的。
  7. 关键字synchronized可以使多个线程访问同一个资源具有同步性,而且它还具有将线程工作内存中的私有变量与公共内存中的变量同步的功能。关键字synchronized可以保证在同一时刻,只有一个线程可以执行某一个方法或某一个代码块。它包含两个特征:互斥性和可见性。同步synchronized不仅可以解决一个线程看到对象处于不一致的状态,还可以保证进入同步方法或者同步代码块的每个线程,都看到由同一个锁保护之前所有的修改效果。

第3章 Lock的使用

4.1 ReentrantLock

关键字synchronized与wait()和notify()/no-tifyAll()方法相结合可以实现等待/通知模式,类ReentrantLock也可以实现同样的功能,但需要借助于Condition对象。在使用notify()/notifyAll()方法进行通知时,被通知的线程却是由JVM随机选择的。但使用ReentrantLock结合Condition类是可以实现前面介绍过的“选择性通知”,这个功能是非常重要的,而且在Condition类中是默认提供的。而synchronized就相当于整个Lock对象中只有一个单一的Condition对象,所有的线程都注册在它一个对象的身上。线程开始notifyAll()时,需要通知所有的WAITING线程,没有选择权,会出现相当大的效率问题。
condition.await()方法调用之前调用lock.lock()代码获得同步监视器。

4.4 正确使用Condition实现等待/通知

  1. package com.zhaoliang.thread.study.four.UseConditionWaitNotifyOK;
  2. import java.util.concurrent.locks.Condition;
  3. import java.util.concurrent.locks.Lock;
  4. import java.util.concurrent.locks.ReentrantLock;
  5. public class MyService {
  6. private Lock lock = new ReentrantLock();
  7. public Condition condition = lock.newCondition();
  8. public void await() {
  9. try {
  10. lock.lock();
  11. System.out.println(" await时间为" + System.currentTimeMillis());
  12. condition.await();
  13. } catch (InterruptedException e) {
  14. e.printStackTrace();
  15. } finally {
  16. lock.unlock();
  17. }
  18. }
  19. public void signal() {
  20. try {
  21. lock.lock();
  22. System.out.println("signal时间为" + System.currentTimeMillis());
  23. condition.signal();
  24. } finally {
  25. lock.unlock();
  26. }
  27. }
  28. }
  29. package com.zhaoliang.thread.study.four.UseConditionWaitNotifyOK;
  30. public class Run {
  31. public static void main(String[] args) throws InterruptedException {
  32. MyService service = new MyService();
  33. ThreadA threadA = new ThreadA(service);
  34. threadA.start();
  35. Thread.sleep(3000);
  36. //通知
  37. service.signal();
  38. }
  39. }
  40. /**
  41. * 运行MyService中的await();
  42. * @author zhaoliang
  43. *
  44. */
  45. class ThreadA extends Thread {
  46. private MyService service;
  47. public ThreadA(MyService service) {
  48. super();
  49. this.service = service;
  50. }
  51. @Override
  52. public void run() {
  53. service.await();
  54. }
  55. }

4.1.6 使用多个Condition实现通知部分线程

  1. package com.zhaoliang.thread.study.four.ConditionTest;
  2. import java.util.concurrent.locks.Condition;
  3. import java.util.concurrent.locks.Lock;
  4. import java.util.concurrent.locks.ReentrantLock;
  5. public class MyService {
  6. private Lock lock = new ReentrantLock();
  7. private Condition condition = lock.newCondition();
  8. private boolean hasValue = false;
  9. public void set() {
  10. try {
  11. lock.lock();
  12. while (hasValue == true) {
  13. condition.await();
  14. }
  15. System.out.println("打印★★★★★★★★★★★★★★★★★★★★★★★★★★★★");
  16. hasValue = true;
  17. condition.signal();
  18. } catch (InterruptedException e) {
  19. e.printStackTrace();
  20. } finally {
  21. lock.unlock();
  22. }
  23. }
  24. public void get() {
  25. try {
  26. lock.lock();
  27. while (hasValue == false) {
  28. condition.await();
  29. }
  30. System.out.println("打印☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆");
  31. hasValue = false;
  32. condition.signal();
  33. } catch (InterruptedException e) {
  34. e.printStackTrace();
  35. } finally {
  36. lock.unlock();
  37. }
  38. }
  39. }
  40. package com.zhaoliang.thread.study.four.ConditionTest;
  41. public class Run {
  42. public static void main(String[] args) {
  43. MyService myService = new MyService();
  44. ThreadA threadA = new ThreadA(myService);
  45. ThreadB threadB = new ThreadB(myService);
  46. threadA.start();
  47. threadB.start();
  48. }
  49. }
  50. class ThreadA extends Thread {
  51. private MyService myService;
  52. public ThreadA(MyService myService) {
  53. this.myService = myService;
  54. }
  55. @Override
  56. public void run() {
  57. for (int i = 0; i < Integer.MAX_VALUE; i++) {
  58. myService.get();
  59. }
  60. }
  61. }
  62. class ThreadB extends Thread {
  63. private MyService myService;
  64. public ThreadB(MyService myService) {
  65. this.myService = myService;
  66. }
  67. @Override
  68. public void run() {
  69. for (int i = 0; i < Integer.MAX_VALUE; i++) {
  70. myService.set();
  71. }
  72. }
  73. }

4.1.9 公平锁与非公平锁

公平与非公平锁:锁Lock分为“公平锁”和“非公平锁”,公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的,即先来先得的FIFO先进先出顺序。而非公平锁就是一种获取锁的抢占机制,是随机获得锁的,和公平锁不一样的就是先来的不一定先得到锁,这个方式可能造成某些线程一直拿不到锁,结果也就是不公平的了。

  1. // isFair表示是否使用公平锁。
  2. ReentrantLock lock = new ReentrantLock(isFair);

419a.jpg-27.2kB 419b.jpg-30.3kB
第一张图采用公平锁,而第二张图采用非公平锁。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注