这本书写的很浅,更多的是案例,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()
1、ThreadLocal类
- 主要作用是将数据放到当前运行线程的成员变量Map中,本身不存储数据,作为桥梁
- Map中的key存储的是ThreadLocal对象
- Thread类内有一个内部内ThreadLocalMap,对应创建成员属性threadLocals,使用set的时候,会调用createMap()方法,该方法的实现在ThreadLocal类中和InheriatbleThreadLocal类中
- 初始调用ThreadLocal的实例对象的get()方法返回为null,可以继承ThreadLocal重写initialValue()方法设置初始值
- ThreadLocal类不能实现值继承,创建子线程的父线程不会把设置的值传给子线程,可以使用InheritableThreadLocal体现值的继承性,它继承Thread类
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的值是否不为空,不为空就保存到子线程
/** * 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()可以进行子线程对父线程继承的值进行加工