使用多线程
创建线程三种方式
优先选择使用 Runnable 接口;当需要返回值时,使用 Callable 接口;仅在测试或简单实现时才选择继承 Thread 类
方式一:继承 Thread 类
通过继承 Thread 类,重写run()方法
class MyThread extends Thread {
@Override
public void run() {
// 线程执行逻辑
}
}
// 使用
MyThread thread = new MyThread();
thread.start();
1)受单继承限制,不能再继承其他类
2)线程与任务绑定,不符合单一职责原则(Thread 负责线程管理,但在其中又增加了业务逻辑)
方式二:实现 Runnable 接口
实现 Runnable 接口,重写run()方法
class MyRunnable implements Runnable {
@Override
public void run() {
// 线程执行逻辑
}
}
// 使用
Thread thread = new Thread(new MyRunnable());
thread.start();
// 或使用线程池
ExecutorService executor = Executors.newFixedThreadPool(5);
executor.execute(new MyRunnable());
1)可以多继承,使用更灵活
2)线程与任务分离,实现该接口只需关注业务逻辑,应用时一个对象可交于多个 Thread 类管理
3)支持 Lambda 表达式简化,能够配合线程池ExecutorService使用
方式三:实现 Callable 接口与 Future
Callable接口
实现 Callable 接口,重写call()方法
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
// 线程执行逻辑
return "执行结果";
}
}
// 使用
ExecutorService executor = Executors.newFixedThreadPool(5);
Future<String> future = executor.submit(new MyCallable());
String result = future.get(); // 获取返回值
1)通过Future或FutureTask调用可以获得返回值
2)能够配合线程池ExecutorService使用
Future接口
只是接口,无法直接new出对象。
应当使用FutureTask类包装实现Callable接口的对象,用于管理线程执行和参数接收
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;
}
cancel()方法用来取消任务,成功则返回 true,参数表示是否允许取消正在执行且未完成的任务
isCancelled()方法表示任务是否被取消成功
isDone()方法表示任务是否已经完成
get()方法用来获取执行结果,这个方法会产生阻塞,一直等到任务执行完毕才返回
get(long timeout, TimeUnit unit)若在指定时间内还没获取到结果,就直接返回 null
补充:Run()与start()
1.必须重写run()方法:
默认run()方法不会做任何事,必须重写
2.Run()与start()的区别:
run()封装线程执行代码,直接调用相当于普通的方法调用,start()会启动线程由 JVM 调用run()方法
线程控制方法
sleep()睡眠
设定毫秒数,使当前线程进入休眠状态
调用时会抛出InterruptedException异常,需要进行异常处理
try {
Thread.sleep(2000); // 当前线程休眠2秒
} catch (InterruptedException e) {
e.printStackTrace();
}
join()等待
等待该线程执行完,后续其他线程才能获得 CPU 执行权
Thread worker = new Thread(() -> {
System.out.println("工作线程运行");
});
worker.start();
try {
worker.join(); // 等待worker线程结束
} catch (InterruptedException e) {
e.printStackTrace();
}
setDaemon()守护
将该线程标记为守护线程,后台运行,当所有其他线程(含main方法主线程)结束后该线程才会终止
需要在start()方法执行前设定为true
Thread daemon = new Thread(() -> {
while (true) {
System.out.println("守护线程运行");
}
});
daemon.setDaemon(true); // 设置为守护线程
daemon.start();
yield()让步
向JVM提出建议,表示可以暂时让步,放弃部分 CPU 时间片
Thread.yield(); // 当前线程让出CPU
线程的六种状态
// Thread.State 源码
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
NEW 新建
线程被创建但尚未启动,指当前还没有调用start()方法
Thread thread = new Thread(() -> {
// 线程任务
});
// 此时线程状态为NEW
RUNABLE 可运行
线程此时等待 CPU 分配资源 or 位于 Java 虚拟机中运行
(包含了操作系统中就绪态和运行态两种状态)
thread.start();
// 启动后进入RUNNABLE状态
BLOCKED 阻塞
此时线程正在等待锁的释放以占用资源
// 线程1先获取锁
synchronized(lock) {
// 线程2在此处被阻塞
}
WAITING 等待
无限期等待其他线程的特定操作,只能等待其他线程的唤醒
Object lock = new Object();
Thread thread = new Thread(() -> {
synchronized(lock) {
try {
lock.wait(); // 进入WAITING状态
} catch (InterruptedException e) {}
}
});
thread.start();
TIMED_WAITING 限时等待
设置了超时时间的等待
Thread.sleep(1000); // 睡眠1秒
lock.wait(5000); // 等待5秒
TERMINATED 终止
线程执行完毕或异常终止
