第一章 快速认识线程
1.Thread源码中start方法中的会对线程状态进行判断(该状态是由jvm控制的,源码中看不到对状态改变赋枚举值这部分的代码)
threadStatus的状态由JVM控制。
2.线程的执行单元就是run方法。创建线程只有一种方式就是构造Thread对象然后调用.start()才算是创建了一个线程。如果一个类继承Thread时就会重写Thread类的run方法,如果这个类实现了runable,就要实现run方法,并且这个类没有start方法,还是要构造一个Thread对象,并且将该对象作为Thread对象的构造参数传入(高内聚,低耦合思想),源码中thread中的run方法会先判断是否有构造参数runable,有的话就执行runable的执行单元run方法,没有的话就什么都不做,
3使用到的第一个设计模式---------------------------策略模式
Thread和runable的目的是为了将线程控制(start…)和业务逻辑(run)分割开,这使用了一种设计模式(策略模式)。一种应用场景:不同系统对我们权限系统请求要求返回不同格式的数据,权限系统可以一个接口,实现这个接口的类,分为多种,不同的了类实现不同的数据格式转换。
第二章 深入理解Thread的构造函数
1.Thread的构造函数中都会执行一个init方法在这个方法中用一个currentThread()(获取当前线程),但是此时我们只是构造一个线程对象并不是一个线程,所以说当前线程就是创建我们线程对象的一个已经存在线程(默认是jvm启动的main线程),也就是说,每一个线程都是由另一个线程创建的,也就是说每一个线程都是有一个创建他的父线程。
2.如果构建thread对象时不指定线程组的话,就会在对象执行start方法的时候将线程本身加入父线程所在的线程组中
3.Init方法中有一个特殊的的方法参数(stackSize 可以通过jvm -Xss设置大小,默认是0),这个参数对应的就是jvm内存模型中的虚拟机栈,线程私有的,在jvm运行时动态创建的,它的大小反映了线程方法调用的层次深度(栈的特性:先进后出,所以如果调用深度大于jvm设置的大小就会出现栈溢出异常)又因为一个进程的内存大小可以分为:堆内存+虚拟机栈内存*线程数量,当堆内存非固定不变的时候,线程数量就和虚拟机栈的内存大小成反比,因为一个线程创建,jvm就会为其划分一块虚拟机栈内存(虚拟机栈包含多个栈帧(一个栈帧代表一个方法调用),一个栈帧包含:局部变量表,操作数栈,动态链接,方法出口)---------本地方法栈也是线程私有的
方法区:常量,静态变量,类信息
堆:新生代,老年代。
4.守护线程---------------------------垃圾回收是守护线程
一个线程可以设置成守护线程,正常情况main线程执行完时,jvm正常退出,如果执行完main线程时,进程中还有其他线程正在运行(非守护线程),jvm则无法退出,如果将线程设置为守护线程,main线程执行完毕时,jvm也会随后退出了,线程也会结束。守护线程具有传递性,即如果一个线程是守护线程,那么它的子线程也会是守护线程
5.如果用户线程和守护线程同时存在,会以用户线程为主,用户线程执行完成之后,jvm就会退出,如果此时还存在钩子线程,jvm会先去执行钩子线程然后执行用户线程,在退出jvm。(守护线程默默地站在一边,什么都不敢做了)Runtime.getRuntime().addShutdownHook()。
6.线程的优先级的设置,还应该考虑线程所在线程组的优先级别,(源码告诉我们线程的优先级不能大于线程组的优先级,否则以线程组的优先级为主)
第三章 Thread的API详细介绍
Sleep会持有锁,陷入短暂的阻塞
Yeld看天意
第四章 数据安全和数据同步
1.Synchronized关键字提供了一种锁机制(monitor对象锁,保证共享对象的互斥性),必须保证monitor enter开始时必须保证从主内存中读取数据,monitor exit 必须保证共享对象被写到了主内存。Synchronized作用域范围应该尽可能放在共享数据的读写位置,这样的话会提高效率。范围太广的话效率就会很低。Synchronized同步线程不可被中断
Jconsole----线程–可以查看阻塞线程和堆栈跟踪。
JvisualVM----进程–可以查看进程信息和jvm相关信息和活跃线程
Jstatck------查看线程的堆栈信息
Javap-----可以对类进行反编译
Monitor对象锁 一个线程获得锁,monitor计数器会+1,获得锁的线程重入,monitor计数器会+1,其他线程想要获取锁会被阻塞。
死锁出现原因:
1.交叉锁导致死锁
2.内存不足
3.死循环引起的死锁(hashmap)
第五章 线程间通信(wait,notify)
1.线程休息室(wait set)----------每一个monitor都有一个
Wait方法必须要拥有对象monitor,也就是必须在Synchronized的同步方法中使用,并且两个方法要使用的是同一个对象的monitor,一旦某个线程调用wait方法就会放弃该对象的monitor的锁,其他线程就有可能或得到锁(什么时候获得的锁?解释:因为在调用wait这个方法之前,就已经在Synchronized的同步方法中获得了monitor锁)进入线程休息室,等待被唤醒然后重新去竞争monitor锁。
2.自定义boolearnLock锁。
3.钩子线程(jvm退出之前)-------会做一些资源释放和告警通知。
Handler会掉的方式获取线程运行期间的异常信息??????
第六章 ThreadGroup详解
第七章 Hook线程以及捕获线程执行异常
第八章 线程池的实现(自定义模仿juc ExecutorService框架)
1.有一个Threadpool接口定义线程池应该具备的基本操作好方法
2.有一个阻塞队列的接口用已提交存放的runable
3.有一个拒绝策略的接口定义达到队列容量上线时的处理方法
4.有一个线程工厂的接口个性换定制线程,比如应该加到那个线程组中
第九章 类的加载过程(处理的都是静态变量)
1.类的加载使用延迟加载模式(首次使用的时候才会被加载初始化),初始化子类会导致父类被初始化,子类调用父类的方法,只会导致父类静态变量或方法被初始化,子类不会初始化
2.引用类的静态常量(编译阶段能确定,而不会有复杂逻辑计算)不会导致类的初始化。新建数组只会分配内存空间却不会初始化类信息(Test[] we = new Test[10])同时在堆内存生成一个Class对象,作为访问方法区数据结构的入口,并把静态属性转化为方法区的数据结构
1.加载----ClassLoader(抽象类)将磁盘二进制文件加载到jvm的内存区域
2.链接----分为三个阶段(验证,准备,解析)
A.验证–验证类的版本信息,魔术因子等
B.准备–为类的静态变量分配内存,并为其初始化默认值
C.解析–将类中的符号引用转化为直接引用(内训地址)
3.初始化----为类的静态变量赋予代码编写中的值,和静态语句块执行(父类的先执行),静态代码值只能为后面的静态变量赋值,而不能访问(编译不通过)
JVM会为拥有静态变量或静态代码块的类生成一个只用虚拟机能够执行的()方法,在初始化得时候为静态变量赋值,执行静态代码块
第十章 类加载器(遵循父委托机制)
1.BootStrapClassloader----根加载器没有父类加载器,加载jdk/jre/lib/.jar或者jdk/jre/classes。通过sun.boot.class.path这个系统属性可以获得根加载路径信息
2.ExtClassLoader----扩展类加载器父加载器(根加载器)加载jdk/jre/lib/ext/.jar。通过java.ext.dirs这个系统属性可以获得扩展加载路径信息
3.ApplicationClassLoader------负者加载classpath路径下的所有类库资源和引入的第三方jar包。其父加载器是扩展类加载器,同时是所有自定义加载器的父加载器,通过java.class.path这个系统属性可以获得系统加载路径信息
4.自定义类加载器
双亲委托加载机制----当前类加载器委托父类去已加载的类信息(加载记录表–缓存)查看当前类是否已经被加载,如果被加载就返回,如果没被加载就继续向上委托,最上级如果没有查询到就尝试加载,加载成功过就返回,加载不成功就先下委托,如果都不行最终由当前类加载器加在----------这样就能保证整个内存中只有一个class对象了
初始类加载器--------会维护其他类加载器首次加载的class对象。
第十一章 线程类上下文类加载器
第十二章 和 第十三章 volatile关键字(只能修饰类变量和实例变量)不能保证原子操作,但能保证可见性。一个呗volatile修饰的变量,一个线程读取操作。一个线程写入操作,写入操作要发生在读取操作之前。禁止jvm进行指令重排序(内存屏障)
并发编程三大特性---------原子性,可见性,有序性
保证可见性— Synchronized(具备原子性),显示锁(juc)(具备原子性),volatile(不具备原子性)
1.当CPU操作缓存中的数据时发现该数据是共享变量,也就是说在其他cpu工作缓存中也存在该变量的副本,读取操作时不做任何处理,将缓存数据读到寄存器,写入操作时发信号通知其他CPU将该变量的缓存数据标记为无效状态,导致其他CPU不得不到主存中重新获取该变量的副本---------------缓存一致性协议的思想
Jvm内存模型只保证了基本的读取和赋值是原子操作
第十四章 单例模式(类被final修饰不能被继承如String类)
1.饿汉式----私有化构造函数(防止外部new),实例对象作为类得变量,在类初始化的时候会实例化(已经new了),公共方法获得类变量。
2.懒汉式----私有化构造函数(防止外部new),实例对象作为类得变量,在类初始化的时候为null(没有new),公共方法先判断引用是否为null,为null就创建,不为null就返回----------无法保证唯一性(线程不安全)
3.懒汉式+Synchronized—在公共方法上加上关键字,线程安全
4.Dubbo+check--------公共方法不加锁,首次创建对象时加锁,自后所有线程够可以访问,效率高
第15-29章都是设计模式(以待后续)