(1)具体步骤如下:
MyThread
来继承 Thread 类,重写 run 方法;public class Demo {
public static class MyThread extends Thread {
@Override
public void run() {
System.out.println("MyThread");
}
}
public static void main(String[] args) {
Thread myThread = new MyThread();
myThread.start();
}
}
(2)注意要调用 start() 方法后,该线程才算启动!我们在程序里面调用了start() 方法后,虚拟机会先为我们创建⼀个线程,然后等到这个线程第⼀次得到时间片时再调用 run() 方法。 注意不可多次调用 start() 方法。在第一次调用start() 方法后,再次调用start() 方法会抛出异常。
(3)也可以直接使用 Thread
类来创建线程:
@Slf4j
public class Demo {
public static void main(String[] args) {
//创建线程对象
Thread t = new Thread(() -> {
//要执行的任务
log.debug("running");
});
//为线程设置名称,也可以在创建线程时直接指定,例如 Thread t1 = new Thread("t1") {...};
t.setName("t1");
//启动线程
t.start();
log.debug("running");
//注:上面两条日志打印语句的顺序不固定
}
}
(1)具体步骤如下:
MyThread
实现 Runnable 接口,并实现 run 方法;Runnable 接口 (JDK 1.8 +) 代码如下:
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
可以看到 Runnable 是⼀个函数式接口,这意味着我们可以使用 Java 8 的函数式编程来简化代码。示例代码如下:
public class Demo {
public static class MyThread implements Runnable {
@Override
public void run() {
System.out.println("MyThread");
}
}
public static void main(String[] args) {
new Thread(new MyThread()).start();
//或者使用 Java 8 函数式编程,可以省略 MyThread 类
new Thread(() -> {
System.out.println("Java 8 匿名内部类");
}).start();
}
}
(2)也可以通过如下方式创建线程:
@Slf4j
public class Demo {
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
log.debug("running");
}
};
Thread t = new Thread(r, "t1");
t.start();
log.debug("running");
}
}
Java 8 以后可以使用 lambda 精简代码(本代码中的前提是接口中只有一个抽象方法):
public class Demo {
public static void main(String[] args) {
Runnable r = () -> {
log.debug("running");
};
Thread t = new Thread(r, "t1");
t.start();
log.debug("running");
}
}
(1)具体步骤如下:
Task
,并实现 call() 方法(有返回值);class Task implements Callable<Integer> {
@Override
public Integer call() throws Exception {
// 模拟计算需要3秒
Thread.sleep(3000);
return 2;
}
public static void main(String args[]) throws ExecutionException, InterruptedException {
FutureTask<Integer> task = new FutureTask<>(new Task());
new Thread(task).start();
Integer res = task.get();
//注意调⽤ get ⽅法会阻塞当前线程,直到得到结果,所以实际编码中建议使⽤可以设置超时时间的重载 get ⽅法。
System.out.println(res);
System.out.println("end...");
}
}
输出结果如下:
2
end...
(2)此外,Callable⼀般也可配合线程池工具 ExecutorService 来使用。这里只介绍 ExecutorService 可以使用 submit 方法来让⼀个 Callable 接口执行。它会返回⼀个 Future,我们后续的程序可以通过这个 Future 的 get 方法得到结果。这里可以看⼀个简单的使用案例:
import java.util.concurrent.*;
class Task implements Callable<Integer> {
@Override
public Integer call() throws Exception {
// 模拟计算需要⼀秒
Thread.sleep(1000);
return 2;
}
public static void main(String args[]) throws ExecutionException, InterruptedException {
ExecutorService executor = Executors.newCachedThreadPool();
Task task = new Task();
Future<Integer> result = executor.submit(task);
//注意调⽤ get ⽅法会阻塞当前线程,直到得到结果,所以实际编码中建议使⽤可以设置超时时间的重载 get ⽅法。
System.out.println(result.get());
}
}
输出结果同上。
使用 ThreadPoolExecutor
类的构造函数可以自定义线程池,下面使用参数最全的构造函数来举例:
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.concurrent.*;
class Solution {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//自定义线程池
int corePoolSize = 2;
int maximumPoolSize = 5;
long keepAliveTime = 50;
// keepAliveTime 的单位
TimeUnit unit = TimeUnit.MICROSECONDS;
//工作队列 workQueue
BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>(3);
//使用开源框架 guava 提供的 ThreadFactoryBuilder 可以给线程池里的线程自定义名字
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("demo-task-%d").build();
//饱和策略
RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
ThreadPoolExecutor threadsPool = new ThreadPoolExecutor(
corePoolSize, maximumPoolSize,
keepAliveTime, unit,
blockingQueue, threadFactory,
handler);
//执行无返回值的任务
Runnable taskWithoutRet = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " is running");
}
};
threadsPool.execute(taskWithoutRet);
//执行有返回值的任务
FutureTask<Integer> taskWithRet = new FutureTask<>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName() + " is running");
//线程睡眠 1000 ms
Thread.sleep(1000);
return 100;
}
});
threadsPool.submit(taskWithRet);
System.out.println("有返回值的任务的结果为: " + taskWithRet.get());
//关闭线程池
threadsPool.shutdown();
}
}
① 有关
Futrue
的相关知识可以参考Java 并发编程面试题——Future这篇文章。
② 有关线程池的相关知识可以参考Java并发编程面试题——线程池这篇文章。
(1)使用继承 Thread 类的方式创建线程:
(2)采用实现 Runnable、Callable 接口的方式创建线程:
(3)通过线程池来创建线程:
Runnable 和 Callable 都是 Java 并发编程中用于创建线程的接口,它们的主要区别如下: