@@ -115,7 +115,7 @@ G-->H[double]
115115
116116* float 与 double:
117117
118- Java 不能隐式执行**向下转型**,因为这会使得精度降低
118+ Java 不能隐式执行**向下转型**,因为这会使得精度降低(参考多态)
119119
120120 ```java
121121 //1.1字面量属于double类型,不能直接将1.1直接赋值给 float 变量,因为这是向下转型
@@ -3180,14 +3180,15 @@ public class MyArraysDemo {
31803180
31813181包:java.util.Date。
31823182构造器:
3183- `public Date()`: 创建当前系统的此刻日期时间对象。
3184- `public Date(long time)`: 把时间毫秒值转换成日期对象。
3183+ `public Date()`: 创建当前系统的此刻日期时间对象。
3184+ `public Date(long time)`: 把时间毫秒值转换成日期对象。
31853185方法:
3186- `public long getTime()`: 返回自 1970 年 1 月 1 日 00:00:00 GMT 以来总的毫秒数。
3186+ `public long getTime()`: 返回自 1970 年 1 月 1 日 00:00:00 GMT 以来总的毫秒数。
31873187
31883188时间记录的两种方式:
3189- 1.Date日期对象。
3190- 2.时间毫秒值:从1970-01-01 00:00:00开始走到此刻的总的毫秒值。 1s = 1000ms
3189+
3190+ 1. Date日期对象
3191+ 2. 时间毫秒值:从1970-01-01 00:00:00开始走到此刻的总的毫秒值。 1s = 1000ms
31913192
31923193```java
31933194public class DateDemo {
@@ -13193,7 +13194,7 @@ public class Demo3_6_1 {
1319313194 info = "abc"; //Python
1319413195 ```
1319513196
13196- 虚方法表:在面向对象编程中,会频繁使用到动态分派 ,如果在每次动态分派的过程中都要重新在类的方法元数据中搜索合适目标就会影响到执行效率。为了提高性能,JVM采用在类的方法区建立一个虚方法表(virtual method table)来实现,使用索引表来代替查找,每个类中都有一个虚方法表,表中存放着各个方法的实际入口
13197+ 虚方法表:在面向对象编程中,会频繁使用到**动态分派** ,如果在每次动态分派的过程中都要重新在类的方法元数据中搜索合适目标就会影响到执行效率。为了提高性能,JVM采用在类的方法区建立一个虚方法表(virtual method table)来实现,使用索引表来代替查找,每个类中都有一个虚方法表,表中存放着各个方法的实际入口
1319713198
1319813199
1319913200
@@ -14752,7 +14753,7 @@ public class GeneratedMethodAccessor1 extends MethodAccessorImpl {
1475214753
1475314754对于一个应用来说通常重点关注的性能指标主要是吞吐量、响应时间、QPS、TPS等、并发用户数等,而这些性能指标又依赖于系统服务器的资源,如:CPU、内存、磁盘IO、网络IO等。对于这些指标数据的收集,通常可以根据Java本身的工具或指令进行查询
1475414755
14755- JDK 自带了监控工具 ,位于 JDK 的 bin 目录下,其中最常用的是 jconsole 和 jvisualvm 这两款视图监控工具:
14756+ JDK 自带了**监控工具 ,位于 JDK 的 bin 目录下** ,其中最常用的是 jconsole 和 jvisualvm 这两款视图监控工具:
1475614757
1475714758* jconsole:用于对 JVM 中的内存、线程和类等进行监控;
1475814759* jvisualvm:JDK 自带的全能分析工具,可以分析:内存快照、线程快照、程序死锁、监控内存的变化、gc 变化等
@@ -15063,8 +15064,9 @@ Thread类API:
1506315064
1506415065#### run start
1506515066
15066- 直接调用 run 是在主线程中执行了 run,没有启动新的线程
15067- 使用 start 是启动新的线程,通过新的线程间接执行 run 中的代码
15067+ run:称为线程体,包含了要执行的这个线程的内容,方法运行结束,此线程随即终止。直接调用 run 是在主线程中执行了 run,没有启动新的线程,需要顺序执行
15068+
15069+ start:使用 start 是启动新的线程,此线程处于就绪(可运行)状态,通过新的线程间接执行 run 中的代码
1506815070
1506915071说明:**线程控制资源类**
1507015072
@@ -15714,6 +15716,7 @@ Mark Word 中就被设置指向 Monitor 对象的指针,这就是重量级锁
1571415716* 开始时 Monitor 中 Owner 为 null
1571515717* 当 Thread-2 执行 synchronized(obj) 就会将 Monitor 的所有者 Owner 置为 Thread-2,Monitor中只能有一
1571615718 个 Owner,**obj对象的Mark Word指向Monitor**
15719+ <img src="https://gitee.com/seazean/images/raw/master/Java/JUC-Monitor工作原理1.png" style="zoom:67%;" />
1571715720* 在 Thread-2 上锁的过程中,Thread-3、Thread-4、Thread-5也来执行 synchronized(obj),就会进入
1571815721 EntryList BLOCKED
1571915722* Thread-2 执行完同步代码块的内容,然后唤醒 EntryList 中等待的线程来竞争锁,竞争是**非公平的**
@@ -15748,7 +15751,7 @@ public static void main(String[] args) {
1574815751```java
15749157520: new #2 // new Object
15750157533: dup
15751- 4: invokespecial #1 // invokespecial <init>:()V
15754+ 4: invokespecial #1 // invokespecial <init>:()V,非虚方法
15752157557: astore_1 // lock引用 -> lock
15753157568: aload_1 // lock (synchronized开始)
15754157579: dup //一份用来初始化,一份用来引用
@@ -15911,7 +15914,7 @@ public class SpinLock {
1591115914 new Thread(() -> {
1591215915 //占有锁
1591315916 lock.lock();
15914- Thread.sleep(5000 );
15917+ Thread.sleep(10000 );
1591515918
1591615919 //释放锁
1591715920 lock.unlock();
@@ -15968,7 +15971,7 @@ public class SpinLock {
1596815971* 如果开启了偏向锁(默认开启),那么对象创建后,markword 值为 0x05 即最后 3 位为 101,这时它的
1596915972 thread、epoch、age 都为 0
1597015973* 偏向锁是默认是延迟的,不会在程序启动时立即生效,如果想避免延迟,可以加VM参数 `-XX:BiasedLockingStartupDelay=0` 来禁用延迟
15971- * 如果禁用了偏向锁,那么对象创建后,markword 值为 0x01 即最后 3 位为 001,这时它的 hashcode、age 都为 0,第一次用到 hashcode 时才会赋值,添加 VM 参数 `-XX:-UseBiasedLocking` 禁用偏向锁
15974+ * 如果禁用了偏向锁,那么对象创建后,markword 值为 0x01 即最后 3 位为 001,这时它的 hashcode、age 都为 0,** 第一次用到 hashcode 时才会赋值** ,添加 VM 参数 `-XX:-UseBiasedLocking` 禁用偏向锁
1597215975
1597315976
1597415977
@@ -16002,37 +16005,39 @@ public class SpinLock {
1600216005
1600316006锁消除主要是通过逃逸分析来支持,如果堆上的共享数据不可能逃逸出去被其它线程访问到,那么就可以把它们当成私有数据对待,也就可以将它们的锁进行消除(同步消除:JVM内存分配)
1600416007
16005- 一些看起来没有加锁的代码,其实隐式的加了很多锁:
1600616008
16007- ```java
16008- public static String concatString(String s1, String s2, String s3) {
16009- return s1 + s2 + s3;
16010- }
16011- ```
1601216009
16013- String 是一个不可变的类,编译器会对 String 的拼接自动优化。在 JDK 1.5 之前,转化为StringBuffer对象的连续 append() 操作,每个append() 方法中都有一个同步块
16010+ ***
16011+
1601416012
16015- ```java
16016- public static String concatString(String s1, String s2, String s3) {
16017- StringBuffer sb = new StringBuffer();
16018- sb.append(s1);
16019- sb.append(s2);
16020- sb.append(s3);
16021- return sb.toString();
16022- }
16023- ```
1602416013
16014+ ##### 锁粗化
1602516015
16016+ 对相同对象多次加锁,导致线程发生多次重入,频繁的加锁操作就会导致性能损耗,可以使用锁粗化方式优化
1602616017
16027- ***
16018+ 如果虚拟机探测到一串零碎的操作都对同一个对象加锁,将会把加锁的范围扩展(粗化)到整个操作序列的外部
1602816019
16020+ * 一些看起来没有加锁的代码,其实隐式的加了很多锁:
1602916021
16022+ ```java
16023+ public static String concatString(String s1, String s2, String s3) {
16024+ return s1 + s2 + s3;
16025+ }
16026+ ```
1603016027
16031- ##### 锁粗化
16028+ * String 是一个不可变的类,编译器会对 String 的拼接自动优化。在 JDK 1.5 之前,转化为StringBuffer对象的连续 append() 操作,每个append() 方法中都有一个同步块
1603216029
16033- 对相同对象多次加锁,导致线程发生多次重入,频繁的加锁操作就会导致性能损耗,可以使用锁粗化方式优化
16030+ ```java
16031+ public static String concatString(String s1, String s2, String s3) {
16032+ StringBuffer sb = new StringBuffer();
16033+ sb.append(s1);
16034+ sb.append(s2);
16035+ sb.append(s3);
16036+ return sb.toString();
16037+ }
16038+ ```
1603416039
16035- 上一节的示例代码中连续的 append() 方法就属于这类情况。如果虚拟机探测到由这样的一串零碎的操作都对同一个对象加锁,将会把加锁的范围扩展(粗化)到整个操作序列的外部。对于上一节的示例代码就是扩展到第一个 append() 操作之前直至最后一个 append() 操作之后,只需要加锁一次就可以
16040+ 扩展到第一个 append() 操作之前直至最后一个 append() 操作之后,只需要加锁一次就可以
1603616041
1603716042
1603816043
@@ -16171,9 +16176,7 @@ class HoldLockThread implements Runnable {
1617116176
1617216177定位死锁的方法:
1617316178
16174- * 检测死锁可以使用 jconsole工具,或者使用 jps 定位进程 id,再用 `jstack id`定位死锁
16175-
16176- 找到死锁的线程去查看源码,解决优化
16179+ * 使用 jps 定位进程 id,再用 `jstack id`定位死锁,找到死锁的线程去查看源码,解决优化
1617716180
1617816181 ```sh
1617916182 "Thread-1" #12 prio=5 os_prio=0 tid=0x000000001eb69000 nid=0xd40 waiting formonitor entry [0x000000001f54f000]
@@ -16206,11 +16209,13 @@ class HoldLockThread implements Runnable {
1620616209 - locked <0x000000076b5bf1c0> (a java.lang.Object)
1620716210 at thread.TestDeadLock$$Lambda$1/495053715
1620816211 ```
16209-
16212+
1621016213* linux 下可以通过 top 先定位到CPU 占用高的 Java 进程,再利用 `top -Hp 进程id` 来定位是哪个线程,最后再用 jstack 排查
1621116214
1621216215* 避免死锁:避免死锁要注意加锁顺序
1621316216
16217+ * 也可以使用 jconsole工具,在jdk\bin目录下
16218+
1621416219
1621516220
1621616221***
@@ -18637,6 +18642,14 @@ private static int ctlOf(int rs, int wc) { return rs | wc; }
1863718642
1863818643##### Executor
1863918644
18645+ 存放线程的容器:
18646+
18647+ ```java
18648+ private final HashSet<Worker> workers = new HashSet<Worker>();
18649+ ```
18650+
18651+ 构造方法:
18652+
1864018653```java
1864118654public ThreadPoolExecutor( int corePoolSize,
1864218655 int maximumPoolSize,
@@ -18681,7 +18694,7 @@ public ThreadPoolExecutor( int corePoolSize,
1868118694
1868218695
1868318696
18684- 1. 在创建了线程池后, 等待提交过来的任务请求。
18697+ 1. 创建线程池,这时没有创建线程(**懒惰**), 等待提交过来的任务请求,调用execute方法才会创建线程
1868518698
18686186992. 当调用execute()方法添加一个请求任务时,线程池会做如下判断:
1868718700 * 如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务
@@ -18760,7 +18773,7 @@ Executors提供了四种线程池的创建:newCachedThreadPool、newFixedThrea
1876018773
1876118774
1876218775
18763- ##### 开发要求
18776+ #### 开发要求
1876418777
1876518778阿里巴巴Java开发手册要求
1876618779
@@ -18778,6 +18791,25 @@ Executors提供了四种线程池的创建:newCachedThreadPool、newFixedThrea
1877818791 - CacheThreadPool和ScheduledThreadPool:
1877918792 - 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM
1878018793
18794+ 创建多大容量的线程池合适?
18795+
18796+ * 一般来说池中**总线程数是核心池线程数量两倍**,确保当核心池有线程停止时,核心池外有线程进入核心池
18797+
18798+ * 过小会导致程序不能充分地利用系统资源、容易导致饥饿
18799+
18800+ * 过大会导致更多的线程上下文切换,占用更多内存
18801+ 上下文切换:当前任务在执行完 CPU 时间片切换到另一个任务之前会先保存自己的状态,以便下次再切换回这个任务时,可以再加载这个任务的状态,任务从保存到再加载的过程就是一次上下文切换
18802+
18803+ 核心线程数常用公式:
18804+
18805+ - **CPU 密集型任务(N+1):** 这种任务消耗的是 CPU 资源,可以将核心线程数设置为 N (CPU 核心数)+1,比 CPU 核心数多出来的一个线程是为了防止线程发生缺页中断,或者其它原因导致的任务暂停而带来的影响。一旦任务暂停,CPU 就会处于空闲状态,而在这种情况下多出来的一个线程就可以充分利用 CPU 的空闲时间
18806+
18807+ CPU 密集型简单理解就是利用 CPU 计算能力的任务比如你在内存中对大量数据进行分析
18808+
18809+ - **I/O 密集型任务:** 这种系统 CPU 处于阻塞状态,用大部分的时间来处理 I/O 交互,而线程在处理 I/O 的时间段内不会占用 CPU 来处理,这时就可以将 CPU 交出给其它线程使用,因此在 I/O 密集型任务的应用中,我们可以多配置一些线程,具体的计算方法是 2N 或 CPU核数/ (1-阻塞系数),阻塞系数在0.8~0.9之间
18810+
18811+ IO 密集型就是涉及到网络读取,文件读取此类任务 ,特点是 CPU 计算耗费时间相比于等待 IO 操作完成的时间来说很少,大部分时间都花在了等待 IO 操作完成上
18812+
1878118813
1878218814
1878318815
@@ -18861,33 +18893,6 @@ System.out.println(future.get());
1886118893
1886218894
1886318895
18864- ***
18865-
18866-
18867-
18868- #### 数量设置
18869-
18870- 创建多大容量的线程池合适?
18871-
18872- * 一般来说池中**总线程数是核心池线程数量两倍**,确保当核心池有线程停止时,核心池外有线程进入核心池
18873-
18874- * 过小会导致程序不能充分地利用系统资源、容易导致饥饿
18875-
18876- * 过大会导致更多的线程上下文切换,占用更多内存
18877- 上下文切换:当前任务在执行完 CPU 时间片切换到另一个任务之前会先保存自己的状态,以便下次再切换回这个任务时,可以再加载这个任务的状态,任务从保存到再加载的过程就是一次上下文切换
18878-
18879- 核心线程数常用公式:
18880-
18881- - **CPU 密集型任务(N+1):** 这种任务消耗的是 CPU 资源,可以将核心线程数设置为 N (CPU 核心数)+1,比 CPU 核心数多出来的一个线程是为了防止线程发生缺页中断,或者其它原因导致的任务暂停而带来的影响。一旦任务暂停,CPU 就会处于空闲状态,而在这种情况下多出来的一个线程就可以充分利用 CPU 的空闲时间
18882-
18883- CPU 密集型简单理解就是利用 CPU 计算能力的任务比如你在内存中对大量数据进行分析
18884-
18885- - **I/O 密集型任务:** 这种系统 CPU 处于阻塞状态,用大部分的时间来处理 I/O 交互,而线程在处理 I/O 的时间段内不会占用 CPU 来处理,这时就可以将 CPU 交出给其它线程使用,因此在 I/O 密集型任务的应用中,我们可以多配置一些线程,具体的计算方法是 2N 或 CPU核数/ (1-阻塞系数),阻塞系数在0.8~0.9之间
18886-
18887- IO 密集型就是涉及到网络读取,文件读取此类任务 ,特点是 CPU 计算耗费时间相比于等待 IO 操作完成的时间来说很少,大部分时间都花在了等待 IO 操作完成上
18888-
18889-
18890-
1889118896
1889218897
1889318898***
@@ -18936,6 +18941,13 @@ private static void method1() {
1893618941
1893718942构造方法:`Executors.newScheduledThreadPool(int corePoolSize)`
1893818943
18944+ ```java
18945+ public ScheduledThreadPoolExecutor(int corePoolSize) {
18946+ super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
18947+ new DelayedWorkQueue());
18948+ }
18949+ ```
18950+
1893918951常用API:
1894018952
1894118953* `ScheduledFuture<?> schedule(Runnable/Callable<V>, long delay, TimeUnit u)`:延迟执行任务
@@ -19663,4 +19675,4 @@ public class BufferedInputStrem extends InputStream {
1966319675
1966419676
1966519677
19666-
19678+
0 commit comments