共计 3398 个字符,预计需要花费 9 分钟才能阅读完成。
简介
线程是一种重要的概念,用于实现并发执行的多任务。
-
线程是程序执行的最小单元,它可以独立执行代码片段。
-
多线程允许在同一程序中同时执行多个任务,提高程序的并发性和响应能力。
-
线程可以共享内存空间,方便数据交换和通信。
三种方式创建线程
1. 继承Thread类
可以通过继承 Thread 类,并重写 run()
方法,在需要的地方调用 start
方法进行执行线程。
注意:需要调用 start
方法才能看见线程效果,调用 run
方法只是在当前线程同步执行 run 方法。
如果需要执行的逻辑代码只在一个地方出现,那么可以使用 Lambda 表达式的方式给
public class ThreadTest {
/**
* 继承自 Thread 的类
* 1. 需要调用 start 方法才能看见线程效果,调用 run 方法只是在当前线程同步执行 run 方法
*/
public static void main(String[] args) {
new MyThread("线程1", 5).start();
new MyThread("线程2", 2).start();
}
}
class MyThread extends Thread {
int count;
MyThread(String name, int count){
super(name);
this.count = count;
}
@Override
public void run() {
System.out.println(getName() + "开始执行,时间:" + new Date());
try {
for (int i=0; i < count; i++){
System.out.println(getName() + "第" + (i+1) + "次执行....");
Thread.sleep(1000);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(getName() + "执行完毕,时间:" + new Date());
}
}
2. 实现Runnable接口
首先需要注意的是,实现 Runnable 接口的方式,其实还是要将实现类作为构造参数传递给 Thread 类,通过 Thread 的 start 方法开辟一个线程。
另外,Runnable 接口是一个函数式接口,可以通过 Lambda 表达式创建实现类。
public class RunnableTest {
/**
* 为什么需要通过Thread执行Runnable对象
* 1. 线程管理:Thread类提供了对线程的管理和控制。它包含了一些方法,例如start()来启动线程,join()来等待线程完成,
* interrupt()来中断线程等。通过Thread类,可以更方便地控制线程的生命周期和执行。
* 2. 线程调度:Thread类提供了线程调度的功能。线程调度是指决定线程执行顺序和时间片分配的过程。
* 通过Thread类,可以设置线程的优先级、睡眠时间和调度策略等。
* 3. 线程上下文:Thread类维护了线程的上下文信息,例如线程名称、线程状态、线程组等。
* 通过Thread类,可以方便地获取和设置这些信息。
*/
public static void main(String[] args) {
MyRunnable runnable1 = new MyRunnable("线程1", 5);
Thread thread1 = new Thread(runnable1);
thread1.start();
// Lambda 表达式创建线程
new Thread(() -> {
System.out.println( "线程2开始执行,时间:" + new Date());
try {
for (int i=0; i < 2; i++){
System.out.println("线程2第" + (i+1) + "次执行....");
Thread.sleep(1000);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("线程2执行完毕,时间:" + new Date());
}).start();
}
}
class MyRunnable implements Runnable {
private String name;
private int count;
MyRunnable(String name, int count) {
this.name = name;
this.count = count;
}
@Override
public void run() {
System.out.println(name + "开始执行,时间:" + new Date());
try {
for (int i=0; i < count; i++){
System.out.println(name + "第" + (i+1) + "次执行....");
Thread.sleep(1000);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(name + "执行完毕,时间:" + new Date());
}
}
3. 实现Callable接口
前面两种方式都是通过重写 run 方法实现多线程,需要注意的是 run 方法返回值是 void,也就是说无法获取线程的执行结果,于是 Callable 出现了。
这种方式需要通过线程池的方式进行调用,首先需要创建一个线程池,然后创建一个 Callable 接口的实现类,使用线程池的 submit
或 execute
执行线程中的代码。
而这两个方法的区别是, execute
方法是无返回值的,效果和上面的方式一样。而 submit
是有返回值的,返回的是一个 Future<T>
接口类型的对象,通过该接口的 get 方法可以获取到返回值。
public class CallableTest {
/**
*
* @param args
*/
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(2);
System.out.println("实际创建的线程池对象:" + executor);
Callable<String> task1 = new MyCallable("线程1", 5);
Future<String> future1 = executor.submit(task1);
System.out.println("实际创建的Future对象:" + future1);
Callable<String> task2 = new MyCallable("线程2", 2);
Future<String> future2 = executor.submit(task2);
executor.shutdown();
try {
// 这里是阻塞的,需要等待获取结果
String result1 = future1.get();
System.out.println(result1);
// 虽然线程2比线程1早执行完,但是需要等待线程1执行完毕。如果放在线程1之前获取返回值,则先打印线程2执行完毕
String result2 = future2.get();
System.out.println(result2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
class MyCallable implements Callable<String> {
private String name;
private int count;
public MyCallable(String name, int count) {
this.name = name;
this.count = count;
}
@Override
public String call() throws Exception {
//System.out.println(name + "开始执行,时间:" + new Date());
for (int i=0; i < count; i++){
System.out.println(name + "第" + (i+1) + "次执行....");
Thread.sleep(1000);
}
//System.out.println(name + "执行完毕,时间:" + new Date());
return name + "执行完毕!!!!!!!";
}
}
提醒:本文发布于441天前,文中所关联的信息可能已发生改变,请知悉!