57. 守护线程是什么?
守护线程(Daemon Thread)是一种特殊类型的线程,它的主要作用是为其他线程提供服务,当程序中只剩下守护线程时,虚拟机(JVM)将会退出。这意味着守护线程的运行状态不会影响程序的持续性,它的主要目的是辅助主线程或用户线程执行任务。
在Java中,可以通过调用线程的setDaemon(true)
方法将一个线程设置为守护线程。需要注意的是,这个操作必须在启动线程之前完成,一旦线程开始执行,就不能将其设置为守护线程。
以下是守护线程的一些特点:
- 守护线程通常用于执行一些后台任务,如垃圾回收、日志记录、监控等。
- 当所有非守护线程(用户线程)结束时,虚拟机会退出,此时守护线程将会被强制终止,即使它们可能还在运行中。
- 守护线程不应持有任何需要关闭的资源,因为它们可能会在资源释放前被终止。
- 设置为守护线程后,该线程创建的子线程默认也是守护线程。
以下是一个简单的代码示例,展示如何创建并启动一个守护线程:
public class DaemonThreadExample {
public static void main(String[] args) {
// 创建一个线程对象,并设置为守护线程
Thread daemonThread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println("Daemon thread is running...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
daemonThread.setDaemon(true); // 设置为守护线程
// 创建一个普通线程
Thread userThread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("User thread is running...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("User thread is about to finish...");
}
});
// 启动线程
daemonThread.start();
userThread.start();
// 确保守护线程能够执行一段时间
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
在这个例子中,当用户线程执行完毕后,即使守护线程还在运行,程序也会结束,因为JVM中没有活跃的非守护线程了。
58. 创建线程有哪几种方式?
在Java中创建线程主要有两种方式:通过继承Thread
类或者实现Runnable
接口。除此之外,还可以通过Callable
接口以及使用ExecutorService
来创建线程。
-
通过继承
Thread
类:
当一个类继承自Thread
类时,它就具备了线程的能力。你可以重写run
方法来定义线程的执行行为。class MyThread extends Thread { public void run() { System.out.println("This is my thread."); } } public class TestThread { public static void main(String args[]) { MyThread t1 = new MyThread(); t1.start(); } }
-
通过实现
Runnable
接口:
Runnable
是一个函数式接口,这意味着你可以使用lambda表达式来创建一个线程。使用Runnable
接口相较于继承Thread
类来说更加灵活,因为Java不支持多重继承。class MyRunnable implements Runnable { public void run() { System.out.println("This is my runnable."); } } public class TestRunnable { public static void main(String args[]) { Thread t1 = new Thread(new MyRunnable()); t1.start(); } }
-
通过
Callable
接口:
Callable
接口与Runnable
接口相似,但它允许线程返回结果,并且能抛出异常。import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; class MyCallable implements Callable<Integer> { public Integer call() { return 100; } } public class TestCallable { public static void main(String args[]) { FutureTask<Integer> futureTask = new FutureTask<>(new MyCallable()); Thread thread = new Thread(futureTask); thread.start(); } }
-
使用
ExecutorService
:
ExecutorService
是Java并发包中的一个接口,它可以更简单地管理线程。你可以使用它来执行Runnable
或Callable
任务。import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class TestExecutorService { public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(10); executorService.execute(new MyRunnable()); // For Runnable executorService.submit(new MyCallable()); // For Callable executorService.shutdown(); } }
以上就是创建线程的几种常见方式。每种方法都有其适用的场景,通常来说,实现Runnable
接口和使用ExecutorService
是最推荐的线程创建方式,因为它们更加灵活,并且可以更好地管理线程资源。
有帮助请点赞收藏呀~
领【150 道精选 Java 高频面试题】请 go 公众号:码路向前 。