线程池的概述和使用
为什么要使用线程池
在java中,如果每个请求到达就创建一个新线程,开销是相当大的。在实际使用中,服务器在创建和销毁线程上花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请求的时间和资源要多的多。除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。为了防止资源不足,服务器应用程序需要采取一些办法来限制任何给定时刻处理的请求数目,尽可能减少创建和销毁线程的次数,特别是一些资源耗费比较大的线程的创建和销毁,尽量利用已有对象来进行服务,这就是“池化资源”技术产生的原因。
线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应更快。另外,通过适当的调整线程中的线程数目可以防止出现资源不足的情况。
线程池的组成部分
一个比较简单的线程池至少应包含线程池管理器、工作线程、任务列队、任务接口等部分。其中线程池管理器的作用是创建、销毁并管理线程池,将工作线程放入线程池中;工作线程是一个可以循环执行任务的线程,在没有任务是进行等待;任务列队的作用是提供一种缓冲机制,将没有处理的任务放在任务列队中;任务接口是每个任务必须实现的接口,主要用来规定任务的入口、任务执行完后的收尾工作、任务的执行状态等,工作线程通过该接口调度任务的执行。
线程池管理器至少有下列功能:创建线程池,销毁线程池,添加新任务。
工作线程是一个可以循环执行任务的线程,在没有任务时将等待。
任务接口是为所有任务提供统一的接口,以便工作线程处理。任务接口主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等。
线程池适合应用的场合
当一个服务器接受到大量短小线程的请求时,使用线程池技术是非常合适的,它可以大大减少线程的创建和销毁次数,提高服务器的工作效率。但是线程要求的运动时间比较长,即线程的运行时间比
线程池概述
程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。
而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。
线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池
内置线程池的使用概述
JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法
public static ExecutorService newCachedThreadPool(): 根据任务的数量来创建线程对应的线程个数
public static ExecutorService newFixedThreadPool(int nThreads): 固定初始化几个线程
public static ExecutorService newSingleThreadExecutor(): 初始化一个线程的线程池
这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。它提供了如下方法
Future<?> submit(Runnable task)
Future submit(Callable task)
使用步骤:
创建线程池对象
创建Runnable实例
提交Runnable实例
关闭线程池
案例演示
线程池的使用
public class MyTest3 {
public static void main(String[] args) {
// public static ExecutorService newSingleThreadExecutor ():初始化一个线程的线程池
ExecutorService executorService = Executors.newSingleThreadExecutor();
//给线程池中提交任务
executorService.submit(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "执行了111111");
}
}
});
executorService.submit(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "执行了111111");
}
}
});
executorService.submit(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "执行了111111");
}
}
});
}
}
匿名内部类的方式实现多线程程序
案例演示
匿名内部类的方式实现多线程程序
new Thread(){代码…}.start();
new Thread(new Runnable(){代码…}).start();
public class MyTest {
public static void main(String[] args) {
// MyThread myThread = new MyThread();
// myThread.start();
//我们使用匿名内部类的方式来开启一个线程
Thread th1 = new Thread() {
@Override
public void run() {
System.out.println("线程开启了");
}
};
th1.start();
new Thread() {
@Override
public void run() {
System.out.println("线程开启了");
}
}.start();
new Thread("线程A") {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程开启了8888");
}
}.start();
}
}
public class MyTest2 {
public static void main(String[] args) {
/* MyRunable myRunable = new MyRunable();
Thread thread = new Thread(myRunable);
thread.start();*/
//匿名内部类的方式来开启线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("任务执行了");
}
}, "线程A").start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("任务执行了");
}
}).start();
}
}
public class MyRunable implements Runnable {
@Override
public void run() {
}
}
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程执行了");
}
}
定时器的概述和使用
定时器概述
定时器是一个应用十分广泛的线程工具,可用于调度多个定时任务以后台线程的方式执行。
在Java中,可以通过Timer和TimerTask类来实现定义调度的功能。
Timer和TimerTask
Timer:
public Timer()
public void schedule(TimerTask task, long delay):
public void schedule(TimerTask task,long delay,long period);
public void schedule(TimerTask task, Date time):
public void schedule(TimerTask task, Date firstTime, long period):
TimerTask:定时任务
public abstract void run()
public boolean cancel()
开发中
Quartz是一个完全由java编写的开源调度框架。
案例演示
定时器的使用
public class MyTest4 {
public static void main(String[] args) throws ParseException {
/* void schedule (TimerTask task, Date time)
安排在指定的时间执行指定的任务。*/
Timer timer = new Timer();
String dateStr = "2021-01-20 11:20:00";
Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(dateStr);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("发送邮件");
}
}, date, 1000);
}
}
定时器的练习
案例演示
定时任务的多次执行代码体现
定时删除指定的带内容目录
多线程常见的面试题
A:多线程有几种实现方案,分别是哪几种? 三种
B:同步有几种方式,分别是什么? 三种 同步代码块 同步方法,Lock
C:启动一个线程是run()还是start()?它们的区别?
3222222222 王伟