当前位置: 首页 > 图灵资讯 > 技术篇> 《Java多线程编程核心技术第二版》读书笔记

《Java多线程编程核心技术第二版》读书笔记

来源:图灵教育
时间:2023-06-28 14:29:09

一、总结

这本书写的很浅,更多的是案例,ThreadLocal部分还可以,大致原理基本讲到了

大致总结了下面部分知识点

二、内容2.1概述

1、在什么情况下使用多线程?

  • 阻塞
  • 依赖,如业务分为两个执行过程,当A业务阻塞的时候,B业务的执行不依赖A业务的执行结果

2、start()方法耗时的原因是因为执行了多个步骤

  • 通过JVM告诉操作系统创建Thread
  • 通过系统开辟内存并使用WindowsSDK中的createThread()函数创建Thread对象
  • 操作系统对Thread对象进行调度,以确定执行时机
  • Thread在操作系统内部成功执行

3、使用常见的命令分析线程

  • jps + jstack.exe:jps查看全部线程,然后jps -l 进程id
  • jmc.exe可视化界面
  • jvisualvm.exe可视化界面

4、停止一个线程

  • 使用退出标志正常退出
  • 使用stop()方法强制终止线程,同方法suspend()、remuse()都是过时的方法,使用可能发生不可意料的结果,因为过程不会释放锁
  • 使用interrupt()方法中断线程
  • yield()方法是暂停当前线程放弃cpu资源,但是放弃的时间不确定

5、优先级

  • 在Java中,线程的优先级1~10共10个等级
  • 线程优先级具有继承性,A线程创建B线程,他们的优先级相同

6、守护线程,Java中有两种线程

  • 非守护线程:即用户线程
  • 守护线程:当非守护线程不存在之后,自动销毁,典型的就是垃圾回收线程

7、同步关键字sychronized概述,使用javap命令反编译class文件为字节码

  • 方法加了synchronized,发现在方法前面多了ACC_SYNCHRONIZED
  • 同步代码块,使用monitorenter、monitorexit指令进行同步处理

注意

  • 加在非静态方法,对象锁锁的是当前类的Object实例对象,锁的并不是当前方法或者代码块的代码,而是第一个持有该方法的线程
  • 如果在X对象中使用了synchronized修饰非静态方法,则X对象就被当成锁
  • 可重入的,在synchronized修饰的代码块、方法内部调用本类的其他synchronized是永远可以拿到锁的
  • 加在静态方法,锁的是当前类的Class类对象,对所有的对象实例起作用

8、volatile三个特性

  • 可见性
  • 不保证原子性:加了volatile的变量执行i++也不是原子操作,需要配合synchronized
  • 禁止代码重排序

9、线程间通信

  • 执行notify()方法不会立即释放锁,执行wait()会立即释放锁,唤醒的顺序与wait的顺序一致,在生产者消费者模式不保证唤醒的是同类还是异类
  • xxx线程实例.join()方法执行,停止当前实例所在线程的执行,执行线程实例的run方法,有线程排队运行的结果
  • wait()方法释放锁,sleep()不释放锁

10、Lock对象的使用

  • JDK1.5新增的ReentrantLock类功能更多了,如嗅探锁定,多路分支等
  • 进程间通信需要借助Condition类的await()、signal()
2.2ThreadLocal和InheritableThreadLocal

1、ThreadLocal类

  • 主要作用是将数据放到当前运行线程的成员变量Map中,本身不存储数据,作为桥梁
  • Map中的key存储的是ThreadLocal对象
  • Thread类内有一个内部内ThreadLocalMap,对应创建成员属性threadLocals,使用set的时候,会调用createMap()方法,该方法的实现在ThreadLocal类中和InheriatbleThreadLocal类中
  • 初始调用ThreadLocal的实例对象的get()方法返回为null,可以继承ThreadLocal重写initialValue()方法设置初始值
  • ThreadLocal类不能实现值继承,创建子线程的父线程不会把设置的值传给子线程,可以使用InheritableThreadLocal体现值的继承性,它继承Thread类

《Java多线程编程核心技术第二版》读书笔记_多线程

《Java多线程编程核心技术第二版》读书笔记_java_02

《Java多线程编程核心技术第二版》读书笔记_intellij-idea_03

2、InheriatbleThreadLocal类

  • InheriatbleThreadLocal继承ThreadLocal。inheritableThreadLocals同threadLocals都是Thread的成员变量,开始为null。
  • InheriatbleThreadLocal重写了childValue()、getMap()、createMap()方法。其中get方法调用getMap方法

父子线程值传递具体的原理就是:

  • 父线程调用InheriatbleThreadLocal实例的set()方法,调用createMap()是重写的方法,就是向InheriatbleThreadLocal容器存数据
  • 进而在创建子线程的时候,子类主动引用其父线程的InheriatbleThreadLocal值

package java.lang;import java.lang.ref.*;public class InheritableThreadLocal<T> extends ThreadLocal<T> {        protected T childValue(T parentValue) {        return parentValue;    }       ThreadLocalMap getMap(Thread t) {       return t.inheritableThreadLocals;    }      void createMap(Thread t, T firstValue) {        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);    }}

在Thread类,创建Thread的时候,会判断父线程的inheritableThreadLocal的值是否不为空,不为空就保存到子线程

《Java多线程编程核心技术第二版》读书笔记_子线程_04

《Java多线程编程核心技术第二版》读书笔记_jar_05

/**     * Initializes a Thread.     *     * @param g the Thread group     * @param target the object whose run() method gets called     * @param name the name of the new Thread     * @param stackSize the desired stack size for the new thread, or     *        zero to indicate that this parameter is to be ignored.     * @param acc the AccessControlContext to inherit, or     *            AccessController.getContext() if null     * @param inheritThreadLocals if {@code true}, inherit initial values for     *            inheritable thread-locals from the constructing thread     */    private Thread(ThreadGroup g, Runnable target, String name,                   long stackSize, AccessControlContext acc,                   boolean inheritThreadLocals) {        if (name == null) {            throw new NullPointerException("name cannot be null");        }        this.name = name;        Thread parent = currentThread();        SecurityManager security = System.getSecurityManager();        if (g == null) {            /* Determine if it's an applet or not */            /* If there is a security manager, ask the security manager               what to do. */            if (security != null) {                g = security.getThreadGroup();            }            /* If the security manager doesn't have a strong opinion               on the matter, use the parent thread group. */            if (g == null) {                g = parent.getThreadGroup();            }        }        /* checkAccess regardless of whether or not threadgroup is           explicitly passed in. */        g.checkAccess();        /*         * Do we have the required permissions?         */        if (security != null) {            if (isCCLOverridden(getClass())) {                security.checkPermission(                        SecurityConstants.SUBCLASS_IMPLEMENTATION_PERMISSION);            }        }        g.addUnstarted();        this.group = g;        this.daemon = parent.isDaemon();        this.priority = parent.getPriority();        if (security == null || isCCLOverridden(parent.getClass()))            this.contextClassLoader = parent.getContextClassLoader();        else            this.contextClassLoader = parent.contextClassLoader;        this.inheritedAccessControlContext =                acc != null ? acc : AccessController.getContext();        this.target = target;        setPriority(priority);// 就是这里实现父子线程值传递        if (inheritThreadLocals && parent.inheritableThreadLocals != null)            this.inheritableThreadLocals =                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);        /* Stash the specified stack size in case the VM cares */        this.stackSize = stackSize;        /* Set thread ID */        this.tid = nextThreadID();    }

  • 子线程将父线程的table对象以赋值的方式赋值给子线程,这个过程是创建Thread对象的时候自动完成的,只要父线程的InheriatbleThreadLocal不为null
  • 父线程更新了InheriatbleThreadLocal的数据之后,子线程也不会更新,反之子线程更新也不影响父线程,但是指针指向的如一个对象的属性值,还是会变得。另外就是可以重写childValue()可以进行子线程对父线程继承的值进行加工