Java 多线程学习笔记(一)入门
Tread 与 Runnable 的区别
众所周知,实现线程有两种模式,其一继承Thread类,覆盖run方法,其二,实现Runnable接口。
前一种体现的模板类设计模式,后者体现的是策略模式,二者并无优劣高低之分,各有各的区别,
1 | // SimpleThread的num是各个线程的私有成员,线程对自己num递增不会影响其他线程,若想改成线程相互影响,则需要加入static |
Java 内存结构,Thread与虚拟机栈
- 程序计数器 线程私有
- java虚拟机栈 线程私有 通过-xss配置
- 本地方法栈 jni调用使用 线程私有
- 堆内存,线程共享,几乎存放java运行期间创建的所有对象,是GC重点关注区域,又称“GC堆”
- 方法区 线程共享
- java 8 元空间
粗略认为Java进程内存大小为 堆内存 + 线程数量*栈内存
守护线程
又称后台线程,随jvm退出而退出,当jvm存在一个非守护线程时,jvm不会自动退出。
Thread sleep 与 yield
sleep使当前线程进入睡眠状态。可以使用TimeUnit来代替sleep方法。
yield是启发式方法,提醒调度器自愿放弃当前CPU资源,当CPU资源不紧张时,可能会忽略。
item | sleep | yield |
---|---|---|
一定执行 | 是 | 否 |
捕获中断 | 是 | 否 |
Thread 优先级
Thread 优先级也是个hint操作,你不能严重依赖优先级来实现业务逻辑。
线程优先级在1-10之间,且不能超过group的优先级。默认情况下,优先级和父线程优先级一致,通常是5,因为main优先级默认是5。
Thread interrupt
线程 interrupt 与 可中断方法
以下方法会使线程进入阻塞状态,调用该线程的interrupt方法,可以打断阻塞。
- Object wait 及其重载方法
- Thread sleep 及其重载方法
- Thread join 及其重载方法
- InterruptibleChannel 的 io 操作
- Selector 的 wakeup 方法
- 其他方法
以上方法又称可中断方法。记住打断一个线程并不等于该线程生命周期结束,仅仅是打断了当前线程的阻塞状态。
一旦线程在阻塞情况下被打断,会抛出InterruptException的异常,该异常就像一个signal信号一样,通知当前线程被打断了。
isInterrupted 和 Interrupted
isInterrupted。该方法是线程成员方法,对线程Interrupt标识的一个判断,并不会影响该标识发生任何改变。
interrupted。是一个静态方法,调用该方法会擦除线程的interrupt标识。如果线程被打断,那么调用该方法,第一次返回true,随后调用只会返回false,除非线程再次被打断。
可中断方法(如sleep)在捕获到中断信号,会重置interrupt标识。
1 | public static void noSleep() throws InterruptedException { |
1 | public static void withSleep() throws InterruptedException { |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(){
public void run() {
while (true) {
System.out.println(Thread.interrupted());
}
}
};
thread.setDaemon(true);
thread.start();
TimeUnit.MILLISECONDS.sleep(2);
thread.interrupt();
}
isInterrupted 和 interrupted均调用同一个本地方法:1
private native boolean isInterrupted(boolean clearInterrupted);
当线程在可中断方法前被中断,则后续的可中断方法将立即中断。
Thread join
当前线程A join 某个线程B,会使当前线程A处于blocked状态进入等待,直到B线程结束或者达到给定的时间。
Thread退出
线程完成后正常结束
手动控制线程退出
线程创建与销毁,具有较高的成本。因此一个线程往往循环执行某些工作,比如心跳检查或是其他工作。当决定退出的时候可以使用线程中断等方式使其退出。1
2
3
4
5public void run() {
while (!isInterrupted()) {
// working
}
}
当其他线程调用interrupt后,可使线程退出。
当然,由于interrupt受中断方法影响,故可以采用volatile修饰的开关flag来控制,volatile关键字是使对象修改后强制在各个线程内进行同步。
1 | private volatile boolean closed = false; |
通过unchecked exception使线程异常退出
可以手动checked异常封装程unchecked exception(Runtime Exception)抛出来结束线程。
线程假死
需通过其他工具如jconsole,jstack来分析线程假死原因。