JUC--指的是 java.util.concurrent 包
Runnable、Callable是定义执行任务的接口
Future定义了线程执行任务时扩展功能的一个接口
jdk封装好了一个FutureTask类来实现了Future和Runnable接口
一.接口源码
Future
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
Callable
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
Runnable
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
二.开启线程的三种方式
实现接口:
通过定义一个类实现Runnable接口,再将实现类作为参数创建一个Thread对象,通过调用Thread对象的strat()方法开启。
或者定义一个类实现Callbale接口,将实现类作为参数构建一个FutureTask对象,将FutureTask作为一个Runnable参数再创建Thread对象,调用start()方法开启
继承父类:
定义一个类继承Thread类,那这个类就相当于是一个线程类了,再通过重写run()方法,创建线程对象,调用start()方法开启新线程执行你重写的run()方法。
线程池:
创建一个线程池,通过线程池来创建和开启线程,线程池内容后续可参考这篇文章XXX。
优点和区别:
1.实现接口方式和继承父类方式,因为java是单继承模式,通过继承父类就没办法继承其他类了,对于后续重构代码会有一定的影响。并且继承类里面的属性资源是线程私有的,而接口上面定义的属性资源是资源共享的。
2.线程池可以更好管理多个线程的生命周期
3.线程池的应用场景:需要开启多个线程异步工作的时候
三.扩展:
new Thread(futureTask1).start()和new Thread(futureTask1).run()的区别?
用一个测试程序作为入口来分析
@SpringBootTest
class SpringStudyApplicationTests {
@Test
void contextLoads() throws ExecutionException, InterruptedException {
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
FutureTask<String> futureTask1 = new FutureTask<>(mt1);
FutureTask<String> futureTask2 = new FutureTask<>(mt2);
// new Thread(futureTask1).run();
// new Thread(futureTask2).run();
new Thread(futureTask1).start();
new Thread(futureTask2).start();
// 获取运算结果是同步过程,即 call 方法执行完成,才能获取结果
String r1 = futureTask1.get();
String r2 = futureTask2.get();
System.out.println(r1);
System.out.println(r2);
}
class MyThread implements Callable<String> {//实现Runnable也可以
@Override
public String call() throws Exception {
System.out.println("call"+Thread.currentThread().getName());
return "call"+Thread.currentThread().getName();
}
}
}
start()
new Thread(futureTask1).start();
new Thread(futureTask2).start();
测试结果:
callThread-2
callThread-1
callThread-1
callThread-2
run()
new Thread(futureTask1).run();
new Thread(futureTask2).run();
测试结果:
callmain
callmain
callmain
callmain
结论:
start()方法是开启了多线程(异步,可以通过结果1得出)调用了run()方法,run()仅仅只是主线程调用了它定义的方法。
分析源码:
可以看到start()方法里面调用了start0()方法,
而这个方法有native关键字,表示是jvm自定义的,不需要关注,我们只需要知道jvm去调度了资源,开启了一个新的线程来执行你的run()方法就好了
private native void start0();
public
class Thread implements Runnable {
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();
/**
* If this thread was constructed using a separate
* {@code Runnable} run object, then that
* {@code Runnable} object's {@code run} method is called;
* otherwise, this method does nothing and returns.
* <p>
* Subclasses of {@code Thread} should override this method.
*
* @see #start()
* @see #stop()
* @see #Thread(ThreadGroup, Runnable, String)
*/
@Override
public void run() {
if (target != null) {
target.run();
}
}
}
守护线程和本地线程(用户线程)的区别?
守护线程:一种服务型线程,专门为本地线程(用户线程)服务的。
本地线程:平时我们使用和新建的线程,就称为本地线程,也可以叫做用户线程,为用户服务的。
守护线程可以通过Thread.setDaemon()来配置。
区别:它们在JVM中的销毁时机不同,本地线程当执行完毕就会进行出栈销毁(终止状态),而守护线程会在所有的本地线程都销毁之后,无论是否还在工作,都会进行退出。
典型的守护线程就是:垃圾回收线程,当所有本地线程都结束之后,就不会产生垃圾对象了,垃圾回收线程就没有存在的意义了,所以它就会自动退出。
线程和进程的区别?
进程包含了线程,一个进程至少有一个线程。
程序计数器、虚拟机栈和本地方法栈对于线程是私有的,所以切换线程比进程负担要小。
进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位。
什么是多线程的上下文切换?
多线程cpu通过分配时间片来进行不断的切换线程,而线程的切换会保留上一个线程的状态(包括cpu的寄存器和程序计数器在某一时间点的内容等,称为上下文信息),便于切换回来时可加载这个线程的状态,保证了一个安全性和资源的开销,而切换的过程保存线程状态和加载回线程状态就叫做上下文的切换。