本文已参与「新人创作礼」活动,一起开启掘金创作之路。
1.1什么是线程
在了解线程之前,有必要了解一下什么是进程,因为线程是进程中的一个实体,进程是由多个线程组成的。
多个线程共享进程的**堆和方法区资源**,但是每个线程有自己的**程序计数器和栈区域**。
多个进程之间是相互独立的。
在java中,main函数就是JVM进程的一个主线程。
上面讲到的程序计数器是什么呢?就是用来记录线程当前要执行的指令地址
线程独自的栈资源是用来记录自己的局部变量,其他的线程是访问不了的。
堆是一个进程中最大的一块内存,堆是进程中的所有线程所共有的。堆里面大多都是通过new创建的对象实例。
方法区是用来放JVM加载的类、常量、静态变量等信息,也是线程共享的。
1.2线程的创建与运行
线程的创建有三种。
一:继承Thread类并重写run方法
二:实现Runnable接口的run方法
三:使用FutureTask方式
class MyThread extends Thread{
@Override
public void run() {
System.out.println("hello Thread");
}
}
public class ThreadTest {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
}
}
为什么我们重写run但是没有用,反而用了一个我们没有写过的start呢?
其实这个我们可以看源代码就能了解到,创建线程就是靠start,run只是一个普通的方法。
直到我们用start才真正的创建了一线程。
当然,我们用了start也不是真的让线程运行起来,线程的运行还是要靠CPU的资源,start之后只是一个就绪的状态。
那我们来看看这个创建线程的好处和坏处
优点:
1.在run方法内获取当前线程直接使用this就可以了,无须使用Thread.currentThread()方法。
缺点
1.JAVA的单继承导致无法继承其他的类。
2.没有返回值
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("hello Runnable");
}
}
public class RunnableTest {
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable());
thread.start();
}
}
实现Runnable接口的run方法可以解决单继承的问题,但是还是无法解决没有返回值的问题。
那么我们就要用到了第三种的方法了。
public class FutureTaskTest {
public static class CallerTask implements Callable<String>{
@Override
public String call() throws Exception {
return "hello FutureTask";
}
}
public static void main(String[] args) {
FutureTask<String> futureTask = new FutureTask<>(new CallerTask());
new Thread(futureTask).start();
try {
String ret = futureTask.get();
System.out.println(ret);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
}
小结
每个方法都有自己的坏处和好处,我们要在实战当中有所取舍。第三种方法的使用还是比较少的。