多线程的创建和使用
多线程的创建方式主要两大类,一种是 继承Thread类,另一种是实现Runnab接口。其本质上都是用来创建线程任务,然后启动线程来执行任务。
继承Thread类
继承类Thread是实现线程最简单的一种方式,通过JDK提供的Thread类,我们只需要重写run方法即可。那么当线程启动的时候,就会执行run方法中的内容。
public class MyThread extends Thread{
@Override
public void run(){
while(true) {
log.info("使用继承Thread类实现多线程场景...");
try {
Thread.sleep(1000);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
log.info("使用继承Thread");
MyThread myThread1=new MyThread();
myThread1.start();
MyThread myThread2=new MyThread();
myThread2.start();
实现Runnable接口
实现Runnable也是一种创建线程的常见方式,使用接口的方式可以让我们的程序降低耦合度,Runnable接口中仅仅定义了一个方法(run)。
public class MyRunable implements Runnable{
@Override
public void run(){
while(true){
log.info("实现Runnable来创建线程...");
try{
Thread.sleep(1000);
}catch(Exception e){
throw new RuntimeException(e);
}
}
}
}
log.info("实现Runable接口");
MyRunable myRunable=new MyRunable();
Thread thread=new Thread(myRunable);
thread.start();
使用内部类的方式
这并不是一种新的实现线程的方式,只是另外的一种写法。比如有些情况我们的线程就想执行一次,以后就用不到了。那么像上面两种方式(继承Thread类和实现Runnable接口)都还要再定义一个类,显得比较麻烦,我们就可以通过匿名内部类的方式来实现。使用内部类实现依然有两种,分别是继承Thread类和实现Runnable接口。
log.info("使用内部类");
Thread thread1=new Thread(){
@Override
public void run(){
while(true){
log.info("使用内部类实现多线程(Thread)");
}
}
};
thread1.start();
Thread thread2=new Thread(new Runnable() {
@Override
public void run() {
while(true){
log.info("使用内部类实现多线程(Runnable)");
}
}
});
thread2.start();
定时器
定时器可以说是一种基于线程的一个工具类,还可以定时地来执行某个任务。在java中实现定时任务有跟多种方式,JDK提供了Timer类来帮助开发者创建定时任务,另外也有很多第三方框架提供了对定时任务的支持。
public class TimerSchedule {
private static final SimpleDateFormat format =new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
public void runThread() throws ParseException {
//创建定时器
Timer timer=new Timer();
//提交计划任务
timer.schedule(new TimerTask() {
@Override
public void run() {
log.info("定时任务执行了....");
}
},format.parse("2024-3-11 9:00:00"));
}
}
TimerSchedule timerSchedule=new TimerSchedule();
timerSchedule.runThread();
实现Callable接口
不管是继承Thread类还是实现Runnable接口,都存在两个问题,第一个是无法抛出更多的异常,第二个是线程执行完毕之后无法获得线程的返回值。那么实现Callable接口就可以通过泛型参数来指定线程的返回值类型,通过FutureTask的geu方法拿到线程的返回值。
其步骤如下:
- 创建一个类实现Callable接口,实现Call方法,这个接口类似于Runnable接口,但比Runnable接口更加强大,增加里异常和返回值;
- 创建一个FutureTask,指定Callable对象,作为线程任务;
- 创建线程,指定线程任务;
- 启动线程。
public class MyCallable {
public void createThread() throws ExecutionException, InterruptedException {
Callable<Integer> call=()->{
log.info("线程执行任务开始了...");
Thread.sleep(2000);
return 1;
};
//将任务封装为FutureTask
FutureTask<Integer> task=new FutureTask<>(call);
//开启线程,执行线程任务
new Thread(task).start();
//这里实现线程执行之后,结果返回之前
log.info("线程开始执行,但是还未返回结果");
Integer result=task.get();
log.info("主线程执行之后返回的结果为:{}"+result);
}
}
//使用Callable创建多线程
MyCallable myCallable=new MyCallable();
myCallable.createThread();
基于线程池
线程和数据库连接这些资源都是非常宝贵的资源,如果等每次需要的时候创建,不需要的时候销毁,是非常浪费资源的,那么我们就可以使用缓存的策略,也就是使用线程池。
public class MyThreadPool {
//创建固定大小的线程池
public void createThreadPool() {
ExecutorService threadPool = Executors.newFixedThreadPool(10);
while (true) {
//提交多个线程任务并执行
threadPool.execute(new Runnable() {
@Override
public void run() {
log.info("通过线程池来执行线程任务");
}
});
}
}
}
//使用线程池执行多线程任务
MyThreadPool myThreadPool=new MyThreadPool();
myThreadPool.createThreadPool();