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

Java多线程技能

  • 进程是操作系统结构的基础,是一次程序的执行,是一个程序及其数据在处理机上顺序执行时所发生的活动,是程序在一个数据集合上运行的过程,他是系统进行资源分配和调度的一个独立单位。
  • 生活中,我们可以简单的把一个运行中的exe程序理解成“进程”
  • 线程是在进程中独立运行的子任务,一个进程正在运行时至少会有一个线程。
  • 关于进程与线程的解释,可以看这篇文章
  • 疑问:一个jvm进程中有多个线程,多核的cpu,某一个时刻,某一cpu可以运行某一进程,是不是说这多个线程只能在某一个核的该jvm进程的时间片内运行?网友解释
  • Thread.currentThread().getName();当前线程名
  • 实现线程,1.继承Thread 2.实现Runnable,java单继承的原因,优先考虑实现接口
  • public class Thread implements Runnable ; 类Thread源码中继承了Runnable接口
  • 使用多线程时,代码顺序并不是实际执行顺序,启动多个线程(start方法),cpu空闲时会随机选取一个准备好的线程运行。
  • 启动线程,必须调用start(),如果直接thread.run(),则不会开一个新的线程,直接是在main线程中运行
  • Thread类的构造方法,Thread(Runnable target),不光可以传入Runnable接口的对象,也可以传入一个Thread类的对象,这样做是相当于将一个Thread对象中的run方法交由其他的线程进行调度。
  • 普通方法加synchronized关键字,提供是一个对象锁
  • static方法加synchronized关键字,提供的是class锁
  • isAlive()判断当前线程是否存活
  • sleep(s)的作用是让当前正在执行的线程休眠s秒,不交出所获得的锁。
  • getId()是去的线程的唯一标识
  • 停止线程。方法一:Thread.stop()方法已经被废弃,最好不用,原因是暴力停止的不安全,可能导致一些资源没有释放,带来死锁。并且,调用stop(),会产生ThreadDeath异常。方法二:Thread.interrupt()方法,推荐方法。方法三:设置退出标志,使得正常退出,也就是当run方法运行完后线程终止
  • thread.interrupt()将某个线程终止,含义是:将这个线程的中断标志设置为true,具体去终止线程,需要在该线程中去判断这个中断标志,然后结束代码
  • 判断线程是否中断,static interrupted(),静态方法,无论调用者是谁,都返回的是当前线程的中断状态,具体表现是,在main方法中,thread.interrupted(),具体线程thread是已经调用中断的,返回false,即返回是main线程的中断状态;另外一个特点是,调用一次本方法,中断标志会复位为false
  • isInterrupted(),不是static,调用者的中断状态,也没有擦除效果,一般使用这个方法。
  • 在线程内部,通过查看中断标志为true后,可以抛出一个异常,达到后续代码不运行的目的
  • 同上,可以使用return达到同样目的
  • 在线程sleep状态下中断,会进入catch语句,并清除中断标志,变为false
  • 线程中断后,再遇到sleep方法,会进入异常代码
  • suspend()暂停,resume()恢复。均废弃
  • yield(),放弃cpu资源,时间未知。
  • setPriority()设置线程优先级,优先级具有随机性
  • thread.setDaemon(true),将thread设置为守护线程,从而main结束时thread也结束

对象及变量的并发访问

  • 方法内部的变量,是线程安全的。因为每个线程调用这个方法时都会创建一份私有变量。
  • 多个线程访问同一个对象的实例变量(属性),则存在“非线程安全”问题。
  • 多个线程访问多个对象(同一个class的对象)的static(静态)变量,则存在“非线程安全”问题。
  • 多个线程,访问同一个对象的同步(synchronized)普通方法,同一时间,只有一个线程可以执行该方法,是同步的,是线程安全的
  • 多个线程,访问多个对象(同class)的同一个同步(synchronized)方法,是异步的,由于每个对象一个同步锁。即同步普通方法锁的对象。
  • 多线程环境,某一线程获取同步方法的锁(对象锁),其它线程可以访问该对象的非同步方法。
  • 多线程环境,某一线程获取某一同步方法的锁(对象锁),其它线程不能访问该对象的其它同步方法。即同一个对象,多个同步方法是用的同一个对象锁。
  • 关键字synchronized拥有锁重入的功能:当一个线程获取到该对象的锁,执行代码过程中,再次获取锁是可以的,即一个同步方法中再去调用该对象其它同步方法,是可以的。自己可以获取自己内部的锁
  • 如果关键字synchronized不具备锁重入功能,则可能造成 死锁
  • 当存在父子类继承关系时,子类是完全可以通过“可重入锁”调用父类的同步方法,这是由于java的继承特性。
  • 当一个线程执行的代码出现异常,其所持有的锁会自动释放。
  • synchronized同步代码块(同步语句块),相比同步方法,会灵活点
  • synchronized(this){//coding};也是对象锁
  • synchronized(任意对象非this){//coding};锁的是()中的对象,多线程竞争同一个对象,则同步。
  • synchronized(任意对象非this){//coding};解决了前两种,同一对象只用一个锁的问题,增加了灵活性
  • 静态同步方法(static synchronized)、synchronized(Class.class),锁的是class,与普通同步方法的对象锁不是同一个,即同一对象的静态同步方法和普通同步方法是异步的。
  • 多线程,多个对象,如果是锁的class,则是同步
  • JVM有一个String常量池,使用synchronized(String str){}可能带来一些例外,需要注意。
  • 使用jdk自带工具检查死锁情况,cmd,jps(查看进程id列表),jstack pid (查看该进程的情况)
  • 内部类,静态内部类的synchronized用法与常规的基本相同
  • synchronized(任意对象){},当这个对象改变了,会出现异步的可能(比如字符串对象,赋新值),但是只是改这个对象的属性,还是同步的
  • 关键字volatile,作用一:使变量在多个线程间可见;作用二:禁止指令重排。
  • 作用一解释:关键字volatile会强制从公共堆栈中取得变量的值,而不是从线程私有数据栈中取得变量的值。
  • volatile的缺点是:不支持原子性
  • 可以使用volatile加上原子类,保证多线程安全问题
  • 原子类 AtomicInteger…
  • 关键字synchronized可以使得多线程访问同一资源具有同步性,而且它还具有将线程工作内存中的私有变量与公共内存中的变量同步的功能,也就是保证了可见性。

等待/通知机制

  • 多线程之间可以通过共享变量,来实现通信,但是不够灵活。
  • 等待/通知机制,wait(),notify()…
  • wait(),是object的方法,该方法用来将当前线程置入“预执行队列”,并且在调用wait的代码行处停止执行,直到接到通知或者被中断。
  • 调用wait前必须获取到该对象的对象级别锁,调用时会释放该锁,若无锁调用,会抛出异常,IllegalMonitorStateException。
  • notify()也是object的方法,如果有多个线程等待,则由线程规划器随机挑选其中一个呈现wait的线程,对其发出notify通知,使其可以进去等待锁的状态。
  • notify调用前,线程也必须获取该对象的对象级别的锁,否则抛出IllegalMonitorStateException。
  • 调用notify后,不能及时释放锁,必须等该线程执行完。
  • notifyAll(),唤醒所有等待这一共享资源的线程。
  • wait状态的线程,被中断interrupt(),会抛出异常
  • wait(long),等待long指定的时间,如果期间没有被唤醒,则超出这个时间自动唤醒
  • 消费者和生产者问题
  • 通过管道进行线程间的通信:PipedInputStream PipedInputStream PipedReader PipedWriter
  • join()方法
  • ThreadLocal的使用

Lock的使用

定时器的使用

单例模式与多线程

拾遗增补

文章目录
  1. 1. Java多线程技能
  2. 2. 对象及变量的并发访问
  3. 3. 等待/通知机制
  4. 4. Lock的使用
  5. 5. 定时器的使用
  6. 6. 单例模式与多线程
  7. 7. 拾遗增补
|