一个进程中有多个线程,多个线程共享进程的堆和方法区资源,每个线程都有自己的程序计数器和栈。
程序计数器是一块内存区域,记录线程当前要执行的指令地址。
为什么要将程序计数器设为线程私有?
线程是占用CPU的基本单位,CPU一般是使用时间片轮转方式让线程轮询占用的,当前CPU时间片用完后,要让出CPU,等下次轮到自己的时候再执行。程序计数器就是为了记录该线程让出CPU时的 执行地址,待再次分配到时间片时,线程就可以从私有的计数器指定的地址继续执行。
栈资源用于存储该线程的局部变量,这些局部变量是该线程私有的,其他线程无法访问,除此之外栈还用来存放线程的调用栈帧。
堆是进程中最大的一块内存,堆是被进程中大的所有线程共享的,是进程创建时分配的,堆里面主要存放的是使用new操作创建的对象实例。
方法区则用来存放JVM加载的类,常量及静态变量等,也是线程共享。
线程的三种创建方式:
- 继承Thread 类并重写run
- 实现Runnable接口的方法
- 使用FutureTask方法
1. 继承Thread 类并重写run
public class Threadtest {
public static class Mythread extends Thread{
@Override
public void run() {
System.out.println("I am a child thread");
}
public static void main(String[] args) {
Mythread mt = new Mythread();
mt.start();
}
}
}
注:创建了thread对象之后该线程并没有被启动执行,直到调用了start方法之后才启动。
调用了start之后,线程并没有马上执行,而是处于就绪状态(获取了除CPU之外的其他资源,等待获取了CPU资源之后才会真正处于运行状态)run方法执行完毕之后,该线程就处于终止状态。
使用继承的好处:在run()方法内获取当前线程直接使用this就好,无须使用Thread.currentThread();
使用继承不好的地方:java 不支持多继承,如果继承了thread类,就不能再继承其他类。
2. 实现runnable接口
public class RunableTask implements Runnable{
@Override
public void run() {
System.out.println("I am a child thread");
}
public static void main(String[] args) throws InterruptedException {
RunableTask task = new RunableTask();
new Thread(task).start();
new Thread(task).start();
}
}
两个线程共用一个task代码逻辑,如果需要,可以给RunableTask 添加参数进行任务区分。RunableTask可以继承其他类。
但是以上方法都没有返回值
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CallerTask implements Callable<String> {
@Override
public String call() throws Exception {
return "hello";
}
public static void main(String[] args) throws InterruptedException {
FutureTask<String> futureTask= new FutureTask<>(new CallerTask());
new Thread(futureTask).start();
try{
String result = futureTask.get();
System.out.println(result);
}catch (ExecutionException e){
e.printStackTrace();
}
}
}