1.继承Thread类:创建一个类,继承自 Thread 类,并重写 run() 方法来定义线程的执行逻辑。然后可以实例化这个类并调用 start() 方法来启动线程。
public class MyThread extends Thread {@Overridepublic void run() {// 线程执行逻辑for (int i = 0; i < 5; i++) {System.out.println("当前线程: " + Thread.currentThread().getName() + ",i = " + i);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}public static void main(String[] args) {MyThread thread = new MyThread();thread.start();}
}
在这个例子中,MyThread 类继承了 Thread 类,并重写了 run() 方法来定义线程的执行逻辑,这里简单地打印输出一些信息并暂停 1 秒。在 main 方法中,我们实例化了 MyThread 类并调用 start() 方法来启动线程。
继承 Thread 类的方式相对简单直接,但也存在一些局限性,因为 Java 不支持多重继承,所以继承了 Thread 类之后就无法再继承其他类。另外,如果想要共享实例变量,也不太方便。
总结起来,继承 Thread 类是一种简单直接的创建线程的方式,适合一些简单的线程逻辑,但在复杂的场景中可能需要考虑其他方式。
2.实现Runnable接口:创建一个类,实现 Runnable 接口,并实现 run() 方法来定义线程的执行逻辑。然后可以将这个类的实例传递给 Thread 类的构造函数来创建线程。
public class MyRunnable implements Runnable {@Overridepublic void run() {// 线程执行逻辑for (int i = 0; i < 5; i++) {System.out.println("当前线程: " + Thread.currentThread().getName() + ",i = " + i);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}public class Main {public static void main(String[] args) {MyRunnable myRunnable = new MyRunnable();Thread thread = new Thread(myRunnable);thread.start();}
}
在这个例子中,MyRunnable 类实现了 Runnable 接口,并重写了 run() 方法来定义线程的执行逻辑,这里简单地打印输出一些信息并暂停 1 秒。在 main 方法中,我们创建了 MyRunnable 的实例,并将其传递给 Thread 的构造函数来创建线程。最后调用 thread.start() 来启动线程。
需要注意的是,run() 方法不会返回任何结果,没有返回值。如果需要获取线程的执行结果,可以使用 Callable 接口来代替。
总结起来,实现 Runnable 接口是一种比较常见的创建线程的方式,它不仅简单易用,而且可以方便地共享实例变量。
3.实现Callable接口:它可以让线程执行完后返回一个结果或者抛出一个异常。与 Runnable 接口不同的是,Callable 接口中的 call() 方法可以声明抛出异常,并且可以有返回值。
import java.util.concurrent.Callable;public class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 1; i <= 100; i++) {sum += i;}return sum;}
}public class Main {public static void main(String[] args) throws Exception {Callable<Integer> myCallable = new MyCallable();FutureTask<Integer> futureTask = new FutureTask<>(myCallable);Thread thread = new Thread(futureTask);thread.start();System.out.println("计算结果:" + futureTask.get());}
}
在这个例子中,MyCallable 类实现了 Callable 接口,并实现了 call() 方法来定义线程的执行逻辑,这里是计算 1-100 的和并返回。在 main 方法中,我们将 MyCallable 实例传递给 FutureTask 的构造函数,再将 FutureTask 实例传递给 Thread 的构造函数来创建线程并启动执行。最后调用 futureTask.get() 方法来获取线程的返回结果。
需要注意的是,call() 方法可以声明抛出异常,并且可以有返回值。同时,在本例中,我们使用了 FutureTask 来获取线程执行结果,它是一个包装器,可以将 Callable 转换成 Future。
总之,实现 Callable 接口可以让我们更方便地同时获取线程的返回结果和异常信息,提高了代码的可靠性和可读性。
4.使用线程池的方式创建线程:使用线程池可以更好地管理和复用线程资源,从而提高程序的性能和效率。Java 提供了 Executor 框架来创建和管理线程池。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class Main {public static void main(String[] args) {// 创建线程池,指定线程数为 5ExecutorService executor = Executors.newFixedThreadPool(5);// 提交任务给线程池for (int i = 0; i < 10; i++) {Runnable worker = new WorkerThread("任务 " + (i + 1));executor.execute(worker);}// 关闭线程池executor.shutdown();}
}class WorkerThread implements Runnable {private String taskName;public WorkerThread(String taskName) {this.taskName = taskName;}@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " 开始执行任务:" + taskName);try {// 模拟任务执行时间Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + " 完成任务:" + taskName);}
}
在这个例子中,我们通过
Executors.newFixedThreadPool(5)
创建了一个固定大小为 5 的线程池。然后我们提交了 10 个任务给线程池,每个任务都是一个实现了 Runnable 接口的 WorkerThread 类。每个任务会被线程池中的一个线程执行。最后,我们调用
executor.shutdown()
关闭线程池。这会等待所有已提交的任务执行完毕,并且不再接受新的任务。使用线程池可以有效地管理线程的创建和销毁,避免了频繁创建和销毁线程的开销。此外,线程池还提供了一些额外的功能,例如线程调度、线程超时等,能更好地控制线程的执行。
总结起来,使用线程池能够更好地管理和复用线程资源,提高程序的性能和效率。