读书笔记——Java多线程编程核心技术

第一章 Java多线程技能

     1.1进程/线程/多线程

     进程:受操作系统管理的基本运行单位,即操作系统中正在运行的exe程序。

     线程:在进程中独立运行的子任务。

     1.2

     实现多线程方法:1.继承Thread 2.实现Runnable接口

     1.继承Thread

     Thread.start():通知”线程规划器“此线程已经准备就绪,等待调用线程对象的run()。即让系统安排一个时间来调用run()。使得其具有异步执行效果。

     Thread.run():同步执行,此线程对象不是由”线程规划器“处理,而是由main主线程来调用run(),即必须等run()执行完后才可以执行下面的代码。

     线程对象的构造方法是被main线程调用的,而run方法是被Thread-0的线程调用的,它是自动调用的方法。

     注意在 MyThread t = new MyThread();Thread t = new Thread(c)时,此时得到的t1是Thread.currentThread,是一个新的线程,而this则表示的仍然是MyThread的引用,二者是不一样的。

     1.4 isAlive()方法

     作用:判断当前线程是否处于活动状态。

     活动状态:指线程已经启动且尚未终止,即执行.start()方法后,到线程终止的这一段时间。

     1.5 sleep()方法

     作用:在指定的毫秒数内让当前的”正在执行的线程“休眠(暂停执行)

     1.6 getId()方法

     作用:取得线程的唯一标识

     1.7 停止线程

     1.停止方法:三种,1,使用退出标志,当run方法完成后线程终止;2,使用stop方法强行终止,不推荐,其与suspend,reesume为过期方法,不安全且已废弃;3,使用interrupt方法中断线程。

     1.7.3 能停止的线程--异常法

     虽然线程已停止,但仍会执行下面的语句,此时只要在判断线程已终止后抛出异常,就不会再执行后面的语句了。

     1.7.4 在沉睡中停止

     如果在sleep状态下停止某一线程,会进入catch语句,并且清除停止状态值,使之变成false。

     1.7.5 暴力停止--使用stop()方法

     第二章

      2.1.6 synchronized锁重入

      1.自己可以再次获取自己的锁。即在一个synchronized方法/块的内部调用本类的其他synchronized方法/块时,是永远可以得到锁的。若不可重入锁,就会造成死锁。

      2.当存在父子类继承关系时,子类完全可以通过“可重入锁”调用父类的同步方法。

      2.1.7 出现异常,锁自动释放。

      2.1.8 同步不具有继承性

     2.2 synchronized同步块

      应用场景:用关键字synchronized声明方法,若A线程调用同步方法执行一个长时间任务,则B线程必须较长时间。此时可以用synchronized同步语句块来解决。

      2.2.2 同步代码块的使用(只在必须同步的地方使用)

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

      2.2.4 一半同步,一半异步

      不在块中的就是异步,在块中的就是同步执行。

     2.2.5 synchronized代码块间的同步性

      当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对同一个object中所有其他synchronized(this)同步代码块的访问将被阻塞,这说明synchronized使用的“对象监视器”是一个。

     2.2.6 synchronized(this)代码块是锁定当前对象的。

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

     多个线程调用同一个对象中的不同名称的同步方法还是同步代码块,调用的效果就是按顺序执行,也就是同步的,阻塞的。Java还支持对“任意对象”作为“对象监视器”来实现同步的功能。这个“任意对象”大多是实例变量及方法的参数,使用格式为synchronized(非this对象)。

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

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

     优点:1.如果一个类中有许多个synchronized方法,若使用同步代码块锁非this对象,则synchronized(非this)代码块中的程序与同步方法是异步的,不与其他锁this同步方法争抢this锁,提高运行效率。

                2.解决脏读问题:同步代码块放在非同步synchronized方法中进行声明,并不能保证调用方法的线程的执行同步/顺序性,即线程调用方法是无序的,这样很容易出现”脏读“问题,而使用它可以解决此类问题。

     2.8.2 三个结论

     1.当多个线程同时执行synchronized(x){}同步代码块时呈同步效果。

     2.当其他线程执行x对象中synchronized同步方法时呈同步效果。

     3.当其他线程执行x对象方法里面的synchronized(this)代码块时也呈现同步效果。

     以上三个结论呈现的效果与将synchronized关键字加到非static方法上使用的效果是一样的。

     ps:synchronized关键字加到static方法上是给Class类上锁(作用与同步synchronized(class)一样),加到非static方法上是给对象上锁。

     2.2.10 数据类型String的常量池特性

     若使用synchronized(str),当字符串相同时,则两个线程持有相同的锁,则会造成后一个线程不能执行。因此大多数情况下,synchronized代码块都不使用String作为锁对象,而改用其他,比如new Object()实例化一个Object对象,但它并不放入缓存中。(可查看书本104例子)

     2.2.11 同步synchronized方法无限等待与循环

     可改用同步块来解决。
     2.2.12 多线程的死锁

     造成死锁的可能情形:synchronized嵌套使用

     2.2.13 内置类与静态内置类

     若PrivateClass为PublicClass的内置类,若想要实例化内置类,则要使用如下代码:

     PrivateClass privateClass = publicClass.new PrivateClass();

     2.2.14 内置类与同步

     1.若内置类中有两个同步方法,但使用的为不同的锁,则为异步操作。

     2.同步代码块synchronized(class2)对class2上锁后,其他线程只能以同步的方式调用class2中的静态同步方法。

     2.2.16 锁对象的改变

     在将任何数据类型作为同步锁时,需要注意的是,是否有多个线程同时持有锁对象,如果同时持有相同的锁对象,则这些线程之间就是同步的;如果是分别获得锁对象,这些线程之间就是异步的。

     以及,只要对象不变,即使对象的属性被改变,运行的结果还是同步的。

    2.3 volatile 关键字

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

     2.3.2 解决同步死循环

     可用多线程解决。

     2.3.3 解决异步死循环

     volatile关键字。

     volatile缺点:不支持原子性。(即同步性)

     与synchronized对比:1.volatile是线程同步的轻量级实现,故volatile性能肯定比synchronized要好,并且volaile只能修饰变量,而synchronized可以修饰方法以及代码块。随着JDK版本提升,synchronized关键字在执行效率上有很大提升,在开发中使用synchronized的机率是比较大的。

     2.多线程访问volatile不会阻塞,synchronized会阻塞。

     3.volatile能保证数据的可见性,但不能保证原子性,而synchronized可以保证原子性,也可间接保证可见性,因为它会将私有内存和公共内存中的数据做同步。

     4.volatile解决的是变量在多个线程之间的可见性,而synchronized解决的是多个线程之间资源的同步性。

     2.3.5 使用原子类进行i++操作(AtomicInteger)

     注意:在具有逻辑性的操作下原子类也不完全安全,它的输出解决也具有随机性。因为AtomichXX类的操作是原子性的,但是方法和方法之间的调用不是原子的,因此要在方法上加上synchronized关键字来解决。(书本P128例子)

    第三章 线程间通信

     3.1.1 不使用等待/通知机制实现线程间通信

     弊端:浪费 CPU资源。若轮询时间间隔很小,更浪费CPU资源;若轮询时间间隔很大,可能会取不到想要得到的数据。

     3.1.2 等待/通知机制

     实现:使用wait和notify(只能在同步方法或代码块中使用),wait使线程停止运行,而notify使停止的线程继续运行。一般为一对出现,若获得了某对象锁的wait线程运行完毕,对象已被释放,但没有再次使用notify语句,即使对象已经空闲,还会继续阻塞在wait状态。

     线程状态切换示意图:

     Runnable状态:准备运行的阶段,系统为此线程分配CPU资源

     一.线程进入Runnable状态的5种情况:1.调用sleep()方法后经过的时间超过了指定的休眠时间;

      2.线程调用的阻塞IO已经返回,阻塞方法执行完毕;

      3.线程成功地获得了试图同步的监视器;

      4.线程正在等待某个通知,其他线程发出了通知;

      5.处于挂起状态的线程调用了resume恢复方法。

     二.出现阻塞状态的5种情况:1.线程调用sleep()方法,主动放弃占用的处理器资源;

      2.线程调用了阻塞式IO方法,在该方法返回之前,此线程被阻塞;

      3.线程试图获得一个同步监视器,但该同步监视器正在被其他线程所持有;

      4.线程等待某个通知;

      5.线程带哦用了suspend方法将该线程挂起。此方法容易导致死锁,尽量避免使用。

     3.1.4 方法wait()调用后锁自动释放;方法notify()调用后要直到同步代码块后锁才释放。

     3.1.5当interrupt方法遇到wait方法

      当线程呈现wait()状态时,调用线程对象的interrupt()方法会出现InterruptedException异常。

     3.1.11 生产者/消费者模式实现

      模式一:一生产与一消费:操作值

     

猜你喜欢

转载自blog.csdn.net/lianup/article/details/81085013