Java线程之间的通信由Java内存模型(简称JMM)控制,从抽象的角度来说,JMM定义了线程和主内存之间的抽象关系。
JMM的抽象示意图:
那么怎么知道这个共享变量的被其他线程更新了呢?这就是JMM的功劳了,也是JMM存在的必要性之一。JMM通过控制主内存与每个线程的本地内存之间的交互,来提供内存可见性保证。
Java中的volatile关键字可以保证多线程操作共享变量的可见性以及禁止指令重排序,synchronized关键字不仅保证可见性,同时也保证了原子性(互斥性)。在更底层,JMM通过内存屏障来实现内存的可见性以及禁止重排序。为了程序员的方便理解,提出了happens-before,它更加的简单易懂,从而避免了程序员为了理解内存可见性而去学习复杂的重排序规则以及这些规则的具体实现方法。
内存模型的三大特性(如何保证?待补充)
synchronized能够保证三大特性,volatile能够保证可见性和有序性
Java线程生命周期与操作系统中的进程生命周期定义有所不同。
状态名称 | 说明 |
---|---|
NEW | 初始状态,Thread对象被创建,但是还没有调用start()方法 |
RUNNABLE | 运行状态,Java中将运行和就绪态统称为运行态 |
BLOCKED | 阻塞状态,线程获取不到锁资源而进入阻塞状态 |
WAITING | 等待状态,进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断) |
TIME_WAITING | 超时等待状态,不同于等待状态,可以在指定的时间自行返回 |
TERMINATED | 终止状态,表示当前线程已经执行完毕 |
纠错:左上角Object.join()应为Thread.join()
方法一:继承Thread,覆写run()方法
方法二:实现Runnable接口,然后交给Thread执行
例子:
@Test
public void test1(){
Thread t1 = new Thread("t1"){
@Override
public void run(){
System.out.println(1);
}
};
t1.start();
}
@Test
public void test2(){
// 使用Lambda接口简化类的创建
Runnable task = () -> System.out.println(2);
Thread t2 = new Thread(task, "t2");
t2.start();
}
区别
Runnable
更容易与线程池等高级 API 配合Runnable
让任务类脱离了Thread
继承体系,更灵活方法三:FutureTask配合Thread,FutureTask 能够接收 Callable 类型的参数,Callable也是一个函数式接口,只有一个call()方法,创建好FutureTask任务交给Thread执行,它用来处理有返回结果的情况
使用例子:
// 创建任务对象,指定返回结果类型
FutureTask<String> task = new FutureTask<>(()->{
Thread.sleep(1000);
return "sss";
});
// 新建线程去执行任务
new Thread(task, "thread1").start();
// 调用者线程阻塞,直到task任务执行结束返回结果
String result = task.get();
System.out.println(result);
Thread类
常用方法
// 启动一个新线程运行run方法
start();
// 线程运行时的代码
run();
// 等待 调用 该方法的线程结束,当前线程才继续执行
join();
// 最多等待n毫秒
join(long n);
// 获取当前正在 执行 的线程
currentThread();
// 让当前执行的线程休眠n毫秒
sleep(n);
// 提示线程调度器让出当前线程对CPU的使用
yield();
// 打断线程,调用sleep、wait、join的线程会进入阻塞状态,可用该方法打断阻塞状态的线程,并抛出异常和清除打断标记
// 如果线程正在运行,打断标记为真
interrupt();
关于start()的两个引申问题
要分析这两个问题,我们先来看看start()的源码:
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
看不到对threadStatus的修改,通过端点调试,两个问题的答案都是不可行,在调用一次start()之后,threadStatus的值会改变(threadStatus !=0),此时再次调用start()方法会抛出IllegalThreadStateException异常。
变量的线程安全分析
成员变量和静态变量是否线程安全?
局部变量是否线程安全?
参考链接
[1].http://concurrent.redspider.group/article/02/6.html