Omooo 4 years ago
commit 9f9e7ff330
  1. 6
      blogs/Java/口水话/并发相关口水话.md
  2. 2
      blogs/Java/口水话/线程池.md

@ -32,13 +32,13 @@
#### 一些 Object、Thread 方法的理解? #### 一些 Object、Thread 方法的理解?
首先就是线程的等待通知机制,等待通知机制设计到了两个函数 wait 和 notify/notifyAll,它们就是 Object 的方法,wait 函数是当一个线程调用了共享变量的 wait 方法时,该调用线程就会被阻塞挂起,直到其他线程调用了该共享变量的 notify/notifyAll 方法唤醒。需要注意的是,调用 wait 方法的线程需要先获取该对象的监视器锁,不然就会抛出 IllegalMonitorStateException,那么如何获取一个共享变量的监视器锁呢?其实就是加 synchronzied 即可。调用 wait 方法会释放当前共享变量的监视器锁,不然就会死锁了。wait 还有一个超时重载函数,如果在指定时间没有被唤醒就直接返回了,无参 wait 调用的其实就 wait(0) 方法。调用 notify 函数是随机唤醒一个等待线程,而 notifyAll 就是唤醒所有等待线程。同 wait 一致,只有获取了共享变量的监视器锁后,才可以调用其 notify/notifyAll 方法。 首先就是线程的等待通知机制,等待通知机制涉及到了两个函数 wait 和 notify/notifyAll,它们就是 Object 的方法,wait 函数是当一个线程调用了共享变量的 wait 方法时,该调用线程就会被阻塞挂起,直到其他线程调用了该共享变量的 notify/notifyAll 方法唤醒。需要注意的是,调用 wait 方法的线程需要先获取该对象的监视器锁,不然就会抛出 IllegalMonitorStateException,那么如何获取一个共享变量的监视器锁呢?其实就是加 synchronzied 即可。调用 wait 方法会释放当前共享变量的监视器锁,不然就会死锁了。wait 还有一个超时重载函数,如果在指定时间没有被唤醒就直接返回了,无参 wait 调用的其实就 wait(0) 方法。调用 notify 函数是随机唤醒一个等待线程,而 notifyAll 就是唤醒所有等待线程。同 wait 一致,只有获取了共享变量的监视器锁后,才可以调用其 notify/notifyAll 方法。
还有等待线程执行完成的 join 方法,以及让线程休眠的 sleep 方法,sleep 方法和 wait 方法不同的是,sleep 并不会释放锁。Object#wait、Thread#join、Thread#sleep 在阻塞期间,其他线程调用了该线程的 interrupt 方法中断线程,都会抛出 InterruptedException。 还有等待线程执行完成的 join 方法,以及让线程休眠的 sleep 方法,sleep 方法和 wait 方法不同的是,sleep 并不会释放锁。Object#wait、Thread#join、Thread#sleep 在阻塞期间,其他线程调用了该线程的 interrupt 方法中断线程,都会抛出 InterruptedException。
其次就是让出 CPU 执行权的 Thread#yield 方法,让线程调度器进行下一轮的线程调度,不过需要注意的是,下一轮调度的时候也有可能会再次调度到自己。yield 方法一般是用在自旋中。 其次就是让出 CPU 执行权的 Thread#yield 方法,让线程调度器进行下一轮的线程调度,不过需要注意的是,下一轮调度的时候也有可能会再次调度到自己。yield 方法一般是用在自旋中。
最后就是线程中断了,线程中断是一种线程间的协作模式,通过设置线程的中断标志并不能直接中断线程的执行,而是被中断的线程根据中断状态自行处理。现在中断包括两个函数,一个 interrupt() 方法,中断线程,还有一个是 Thread 的静态方法 interrupted 方法,前者不会清标志位,后者会清除标志位。 最后就是线程中断了,线程中断是一种线程间的协作模式,通过设置线程的中断标志并不能直接中断线程的执行,而是被中断的线程根据中断状态自行处理。现在中断包括两个函数,一个 interrupt() 方法,中断线程,还有一个是 Thread 的静态方法 interrupted 方法,前者不会清标志位,后者会清除标志位。
#### 线程死锁 #### 线程死锁
@ -94,7 +94,7 @@ Lock 和 synchronized 相比,都是可重入的独占锁,不同的是 Lock
原子类即是指 Java 中的 Atomic 类,比如 AtomicInteger、AtomicLong、AtomicStampedReference、AtomicReference 等。都是通过 CAS 来做的。 原子类即是指 Java 中的 Atomic 类,比如 AtomicInteger、AtomicLong、AtomicStampedReference、AtomicReference 等。都是通过 CAS 来做的。
CAS 即比较并替换,它通过硬件来保证操作的原子性。 CAS 即比较并替换,它通过硬件来保证操作的原子性。
在 Java 中,UnSafe 类提供了对 CAS 的简单封装,Atomic 类内部也都是使用 UnSafe 类来做的,UnSafe 类是可以直接操作内存的,一般在应用程序中是不能使用的,它是由启动类加载器加载的。UnSafe 类提供了一系列的 compareAndSwapXxx 方法,它们都是 native 方法。除此之外,UnSafe 还有一对 park/unpark 阻塞唤醒线程的方法,LockSupport 便是对它的包装。 在 Java 中,UnSafe 类提供了对 CAS 的简单封装,Atomic 类内部也都是使用 UnSafe 类来做的,UnSafe 类是可以直接操作内存的,一般在应用程序中是不能使用的,它是由启动类加载器加载的。UnSafe 类提供了一系列的 compareAndSwapXxx 方法,它们都是 native 方法。除此之外,UnSafe 还有一对 park/unpark 阻塞唤醒线程的方法,LockSupport 便是对它的包装。

@ -19,7 +19,7 @@
这种缓存对象实例的池化思想在 Android 中也有应用,比如 Handler 中使用到的 Message 消息池。 这种缓存对象实例的池化思想在 Android 中也有应用,比如 Handler 中使用到的 Message 消息池。
在 Java 中的线程池是用 ThreadPoolExecutor 类来实现的,它的构造方法中有一系列参数需要指定。首先是 corePoolSize 核心线程数量,当有任务通过 execute 方法提交时,如果当前运行的线程数 workCount 小于核心线程数,就新建一个线程去执行任务,如果 workCount 大于等于核心线程数并且队列未满时就扔进队列里面,等待有空闲的线程自己去从队列里面去取然后执行;如果此时队列满了,但是未达到最大线程数,就新建一个线程去执行任务,如果已经达到了最大线程数并且队列满了,就执行拒绝策略。第二个参数就是指定最大线程数,第三个参数是阻塞队列,在任务提交时,如果线程池中的线程数量大于等于 corePoolSize 时,就会把该任务封装成一个 Worker 对象放入等待队列里。在使用 Executors 的 newFixedThreadPool 或 newSingleThreadPool 创建线程池时使用的是 LinkedBlockingQueue,而在使用 newCachedThreadPool 使用的是 SynchronousQueue。但是通常不建议直接使用 Executors 创建线程池,因为 LinkedBlockingQueue 默认大小是 Integer.MAX_VALUE,可能会撑爆内存。第四个参数 keepAliveTime 是非核心线程的存活时间,第五个参数 threadFactory 是用来创建新线程的,默认是 DefaultThreadFactory,在 newThread 里面会设置线程的 group、优先级、是否是守护线程以及线程名称。最后一个参数是拒绝策略,默认是 AbortPolicy 直接抛异常。其次还有其他三个,这四个都定义在了 ThreadPoolExecutor 的内部类中,CallerRunsPolicy(使用调用者所在线程来运行任务)、DiscardOldestPolicy(调用 poll 丢弃一个任务,执行当前任务)和 DiscardPolicy(默认丢弃,不抛异常),ThreadPoolExecutor 一共有五个内部类,还有一个就是上面讲到的 Worker。 在 Java 中的线程池是用 ThreadPoolExecutor 类来实现的,它的构造方法中有一系列参数需要指定。首先是 corePoolSize 核心线程数量,当有任务通过 execute 方法提交时,如果当前运行的线程数 workCount 小于核心线程数,就新建一个线程去执行任务,如果 workCount 大于等于核心线程数并且队列未满时就扔进队列里面,等待有空闲的线程自己去从队列里面去取然后执行;如果此时队列满了,但是未达到最大线程数,就新建一个线程去执行任务,如果已经达到了最大线程数并且队列满了,就执行拒绝策略。第二个参数就是指定最大线程数,第三个参数是阻塞队列,在任务提交时,如果线程池中的线程数量大于等于 corePoolSize 时,就会把该任务封装成一个 Worker 对象放入等待队列里。在使用 Executors 的 newFixedThreadPool 或 newSingleThreadPool 创建线程池时使用的是 LinkedBlockingQueue,而在使用 newCachedThreadPool 使用的是 SynchronousQueue。但是通常不建议直接使用 Executors 创建线程池,因为 LinkedBlockingQueue 默认大小是 Integer.MAX_VALUE,可能会撑爆内存。第四个参数 keepAliveTime 是非核心线程的存活时间,第五个参数 threadFactory 是用来创建新线程的,默认是 DefaultThreadFactory,在 newThread 里面会设置线程的 group、优先级、是否是守护线程以及线程名称。最后一个参数是拒绝策略,默认是 AbortPolicy 直接抛异常。其次还有其他三个,这四个都定义在了 ThreadPoolExecutor 的内部类中,CallerRunsPolicy(使用调用者所在线程来运行任务)、DiscardOldestPolicy(调用 poll 丢弃一个任务,执行当前任务)和 DiscardPolicy(默认丢弃,不抛异常),ThreadPoolExecutor 一共有五个内部类,还有一个就是上面讲到的 Worker。
然后再说说 ThreadPoolExecutor 的一个比较重要的字段,即原子类 ctl,它的高三位表示 runState 运行状态,后二十九位表示 workCount。线程池一共有五种运行状态,RUNNING 状态就是能够接受新提交的任务,并且也能处理阻塞队列中的任务;SHUTDOWN 关闭状态,就是不在接受新提交的任务,但却可以继续处理阻塞队列中已保存的任务,当调用 shutdown 方法会使线程池进入到该状态;STOP 状态不能接受新任务,也不处理队列中的任务,会尝试中断正在处理任务的线程,当调用 shutdownNow 时进行该状态;TIDYING 状态是所有任务已经终止了,workCount 为零。线程池进入该状态后就会调用 terminated 方法进入 TERMINATED 状态。 然后再说说 ThreadPoolExecutor 的一个比较重要的字段,即原子类 ctl,它的高三位表示 runState 运行状态,后二十九位表示 workCount。线程池一共有五种运行状态,RUNNING 状态就是能够接受新提交的任务,并且也能处理阻塞队列中的任务;SHUTDOWN 关闭状态,就是不在接受新提交的任务,但却可以继续处理阻塞队列中已保存的任务,当调用 shutdown 方法会使线程池进入到该状态;STOP 状态不能接受新任务,也不处理队列中的任务,会尝试中断正在处理任务的线程,当调用 shutdownNow 时进行该状态;TIDYING 状态是所有任务已经终止了,workCount 为零。线程池进入该状态后就会调用 terminated 方法进入 TERMINATED 状态。

Loading…
Cancel
Save