Thread
一般而言,线程是CPU资源调度的基本单位。在java中,线程通过系统内核线程实现,每个Java线程对应着一个内核线程。
以HotSpot JVM为例,它的每一个Java线程都是直接映射到一个操作系统原生线程来实现的,中间没有额外的结构,HotSpot不会干涉线程调度。线程调度全由操作系统去处理,包括何时冻结或唤醒线程、该给线程分配多少处理器执行时间、该把线程安排给哪个处理器核心去执行等。
新建/运行
创建一个可执行任务,然后再创建一个线程,创建线程时需要绑定一个可执行任务。调用thread.start()方法后,线程就会执行这个任务。
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
};
// 创建一个Thread执行runnable任务
Thread thread = new Thread(runnable);
thread.start();
线程状态
在java.lang.Thread类的内部类State中,定义了线程所拥有的六种状态。
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
状态含义
- NEW:新建状态,一个线程创建后,但还未调用start()方法时的状态
- RUNNABLE:就绪状态,调用start()方法后的状态,可能在执行状态,也可能在等待CPU时间,对应操作系统中的Running和Ready
- WAITING:无限期等待状态,不会被分配CPU时间,需要等待被其它线程显式唤醒,转换为RUNNABLE。例如,在没有设置timeout调用Object.wait()、Thread.join()、LockSupport.park()
- TIMED_WAITING:有限期等待状态,不会被分配CPU时间,但一定时间后会被自动唤醒,转换为RUNNABLE。例如,在设置timeout之后调用Object.wait()、Thread.join()、LockSupport.parkUntil()
- BLOCKED:阻塞状态,不会被分配CPU时间,在资源可获取时被唤醒,转换为RUNNABLE。例如,在竞争锁对象时,若获取失败,则阻塞等待,在可获得时转换为RUNNABLE;在发出IO请求后,阻塞等待,在IO处理完成后,转换为RUNNABLE。
- TERMINATED:终止状态,线程中的任务执行完成之后处于此状态。
状态转换
找了一张状态流程图
常用方法
动态方法
序号 | 方法描述 |
---|---|
1 | public void start() 启动线程,由New状态转换为Runnable状态 |
2 | public final void setName(String name) 改变线程名称,使之与参数 name 相同 |
3 | public final void join() 等待线程执行完成,转换为TERMINATED状态,可指定等待时间 |
4 | public void interrupt() 中断线程执行 |
静态方法
序号 | 方法描述 |
---|---|
1 | public static void yield() 当前线程让出CPU时间,由Running转换为Ready状态 |
2 | public static void sleep(long millisec) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响 |
需要注意的是,不管是yield还是sleep,都不会释放当前线程持有的锁(如果持有的话)。
任务
线程是用于执行任务的,那么,如何定义一个任务,又如何执行一个任务?
定义任务
java中主要有两种方式,其余均为相应扩展。
Runnbale
Runnable是一个接口,表示一个可运行的任务。要创建一个可执行任务,实现Runnable接口,重写run()方法即可。
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
Callable
Callable是一个接口,表示一个有返回结果的任务,但任务不可直接运行。实现Callable接口,重写call方法,即可创建一个Callable任务。
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
执行任务
任务的执行有多种方式,但本质上都是通过一个子线程来执行的。
executor
- 对于Runnable任务,调用executor.execute()方法执行即可。
// 定义一个单线程任务处理器
ThreadPoolExecutor executor = Executors.newSingleThreadExecutor();
// 执行runnable任务
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
};
executor.execute(runnable);
- 对于Callable任务,调用executor.submit()方法提交,无法直接执行。executor将其转换为一个FutureTask后再执行任务。
Callable callable = new Callable() {
@Override
public Object call() throws Exception {
return "hello";
}
};
Future future = executor.submit(callable);
Objects result = future.get();
thread
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
};
// 创建一个Thread执行runnable任务
Thread thread = new Thread(runnable);
thread.start();
处理返回结果
在提交任务后,如果任务有返回值,应该如何获取返回值呢?例如,向executor中提交一个callable任务后,获取返回值。
Future
这就不得不说到Future了,在executor.submit(Callable callable)执行之后,返回的就是一个Future对象。通过Future对象你可以获取任务的执行状态和执行结果。
Future:表示异步计算结果的接口,提供了一系列方法来管理异步任务的执行状态和结果。
public interface Future<V> {
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
executor.submit(Callable callable)返回的Future对象其实是一个RunnableFuture,最终实现是一个FutureTask。
RunnableFuture:继承自 Runnable 和 Future 接口的接口,继承了两个的特性,表示一个既可运行,又有返回结果的任务
public interface RunnableFRecursiveuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
FutureTask: FutureTask类是RunnableFuture接口的实现类,由一个runnable对象或者callable对象创建。
public class FutureTask<V> implements RunnableFuture<V> {
// 忽略其余方法和变量
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
}
处理异常
子线程中执行的任务,如果在执行过程中,抛出运行时异常,应该怎么处理?
任务中捕获
在任务代码中捕获Exception,然后做一些日志记录的操作。但这种方式侵入性较大,也不优雅。
Runnable runnable = new Runnable() {
@Override
public void run() {
try{
System.out.println("hello");
} catch (Exception ex) {
// 处理异常,例如记录日志或其他操作
}
}
};
线程中捕获
为Thread设置ExceptionHandler,针对任务抛出的异常进行捕获,然后做一些基础的日志记录操作。这种方式无侵入性,简单易用,但是不灵活,无法为不同的任务设置不同的异常处理策略。
Thread thread = new Thread(runnable);
thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
public void uncaughtException(Thread t, Throwable e) {
// 处理异常,例如记录日志或其他操作
System.out.println("Thread " + t.getName() + " threw an exception: " + e);
}
});
针对ThreadPoolExecutor,ExceptionHandler在自定义ThreadFactory时设置。
new ThreadFactory() {
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
public void uncaughtException(Thread t, Throwable e) {
// 处理异常,例如记录日志或其他操作
System.out.println("Thread " + t.getName() + " threw an exception: " + e);
}
});
return t;
}
}
使用Future
前面两种方式都有自己的缺陷,而使用Future的方式,正好可以结合它们的优点又规避缺陷。使用JDK 8中提供的CompletableFuture类,可以以更加灵活的方式对执行过程中抛出的异常进行处理,示例如下
- 无返回值
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
};
CompletableFuture.runAsync(runnable, executor).whenComplete((useless, throwable) -> {
if (Objects.nonNull(throwable)) {
// 日志记录
log.error(throwable.getMessage(), throwable);
// 设置一些特殊逻辑
}
});
- 有返回值
Supplier是一个函数式接口,可提供返回结果,在CompletableFuture中,Supplier实现会被转换为一个AsyncSupply,其实现了Runnable、Future接口,所以可以执行,也可以获取返回结果。
Supplier<String> supplier = () -> "hello";
CompletableFuture.supplyAsync(supplier, executor).whenComplete((result, throwable) -> {
if (Objects.nonNull(throwable)) {
// 日志记录
log.error(throwable.getMessage(), throwable);
return;
}
log.info("execute result : {}", result);
});
标签:Runnable,Java,Thread,void,任务,线程,public
From: https://www.cnblogs.com/cd-along/p/18211078