Java并发编程之美摘要总结
第一章 并发编程基础
1.1 什么是线程
- 线程是进程中的一个实体。
- 进程是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,线程则是进程的一个执行路径,进程中多个线程共享进程的资源。
- 真正占用CPU执行的是线程,也就是说线程是CPU分配的基本单位。
- 启动main函数时,就是启动一个JVM进程,main函数所在线程(在这个进程中)叫做主线程。
进程和线程的关系
- 多个线程共享进程的堆和方法的资源
- 但是每个线程有自己的程序计数器和栈区域
- 程序计数器是一块内存区域,记录当前线程要执行的指令地址
为何要将程序计数器设计为线程私有呢?
线程是占用CPU的执行单位,而CPU 一般是使用时间片轮转的方式让线程轮询占用,所以当前的线程CPU时间片用完之后,要让出CPU,等下次轮到自己的时候再执行。
程序计数器就是为了记录该线程让出CPU时的执行地址的,待再次分配到时间片时线程就可以从自己的私有的计数器指定地址继续执行。
需要注意的是,如果执行的是native方法,那么pc计数器记录的是undefined地址,只有执行的是java代码时,pc计数器记录的才是下一条指令的地址。
- 每个线程都有自己的栈资源,用于存储该线程的局部变量,栈还用来存放线程的调用栈帧
- 堆是进程中最大的一块内存,存放使用new操作创建的对象实例
- 方法区则是用来存放JVM加载的类,常量,静态变量等信息
1.2 线程的创建与运行
- 三种方式创建
- 继承Thread类并重写run方法
- 实现Runnable接口的run方法
使用FuntureTask方法(着重分析一下这种)
public class CallerTask implements Callable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> futureTask = new FutureTask<>(new CallerTask());
new Thread(futureTask).start();
String result = futureTask.get();
System.out.println(result);
}
@Override
public Object call() throws Exception {
return "hello";
}
}
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
@FunctionalInterface
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
CallTask实现了Callable接口的call方法,在main函数里创建了一个FutureTask对象,(构造函数为CallTask实例,也就是接口Callable实现类CallTask的实例),然后使用创建的FutureTask对象作为任务创建啦一个线程并且启动它,最后通过futureTask.get等待任务执行完毕后返回结果。
个人理解:
class Thread implements Runnable {
...
@Override
public void run() {
if (target != null) {
target.run();
}
}
}
Thread是实现了Runnable接口并重写了run方法,如果实现Runnable接口的run方法创建线程,那么会执行Runnable接口的run方法,否则要是继承Thread类并重写run方法,则执行后者。
而FatureTask又是Runnable的实现类,自然重写了run方法,而FatureTask构造函数需要Callable接口作为参数
那就创建一个Callable实例就好啦。
而且因为Java不支持多继承,所以继承的方法弊端是不能继承其他类啦,而通过Runnable接口实例为构造器参数,被称为组合,组合相对于继承来说,耦合度更低,所以优先使用。
可是前两者都不能拿到任务返回结果,但是FutureTask可以。
1.3 线程的等待和通知
1.wait() 函数
当一个线程调用一个共享变量wait() 方法时,改调用线程会被阻塞挂起
满足以下条件才返回
(1)该线程调用了该共享对象的notify()方法或者notifyAll() 方法
(2)其他线程调用了该线程的interrupt()方法,该线程抛出InterruptedException异常返回
虚假唤醒:一个线程 挂起- - > 可运行状态(被唤醒) 即使没满足以上条件。
需要不断用**wait()**方法去测试被唤醒条件是否满足,不满足继续等待。
synchronized (obj){
while(条件不满足){
obj.wait;
}
}