提示:动态每日更新算法题,想要一起学习的小伙伴可以关注一下
文章目录
一、线程详解
每个进程都有自己的地址空间,即进程空间。一个服务器通常需要接收大量并发请求,为每一个请求都创建一个进程系统开销大、请求响应效率低,因此操作系统引进线程。
1. 线程的特点
- 进程中的一个实体
- 进程的一个执行路径
- CPU 调度和分派的基本单位
- 线程本身是不会独立存在
- 当前线程 CPU 时间片用完后,会让出 CPU 等下次轮到自己时候在执行
- 系统不会为线程分配内存,线程组之间只能共享所属进程的资源,线程只拥有在运行中必不可少的资源(如程序计数器、栈)
- 线程里的程序计数器就是为了记录该线程让出 CPU 时候的执行地址,待再次分配到时间片时候就可以从自己私有的计数器指定地址继续执行
下面实现线程的四个方法 1和2不能控制资源且没有返回值,方法3可以有返回值且可以处理异常,方法4在高并发情况下可以使用,合理控制资源不会造成资源耗尽。
2.实现线程的4个方法
- 继承Thread
- 实现Runnable接口
- 实现Callable接口+FutureTask
- 线程池
2.1 继承Thread类
public class ThreadTest02 {
public static void main(String[] args) {
Processor p = new Processor();
/*
采用 start 启动线程,不是直接调用 run,
start 不是马上执行线程,而是使线程进入就绪状态
线程的真正执行是由 Java 的线程调度机制完成的。
*/
p.start();
}
}
class Processor extends Thread {
public void run() {
for (int i=0; i<10; i++) {
System.out.println(i);
}
}
}
2.2 实现Runnable接口
public class test extends Thread{
public static void main(String[] args) {
MyThread thread1=new MyThread("test1");
MyThread thread2=new MyThread("test2");
MyThread thread3=new MyThread("test3");
new Thread(thread1).start();
new Thread(thread2).start();
new Thread(thread3).start();
}
}
class MyThread implements Runnable{
private String name;
public MyThread(String name){
this.name=name;
}
@Override
public void run(){
for(int i=0;i<200;i++){
System.out.println(this.name+"--->"+i);
}
}
}
2.3 实现Callable接口+FutureTask
特地说一下此方法可以拿到返回值且可以处理异常
public class CallableTest {
public static void main(String[] args) throws Exception, {
MyThread myThread = new MyThread();
FutureTask<String> futureTask = new FutureTask(myThread);
new Thread(futureTask, "A").start();
String result = (String) futureTask.get();//拿到返回结果
log.info("result:{}", result);
}
}
class MyThread implements Callable<String> {
@Override
public String call() throws Exception {
log.info("test callable()");
TimeUnit.SECONDS.sleep(5);
return "callable";
}
}
2.4 线程池
public static void fixedThreadPool() {
// 创建 2 个数据级的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(2);
// 创建任务
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("任务被执行,线程:" + Thread.currentThread().getName());
}
};
// 线程池执行任务(一次添加 4 个任务)
// 执行任务的方法有两种:submit 和 execute
threadPool.submit(runnable); // 执行方式 1:submit
threadPool.execute(runnable); // 执行方式 2:execute
threadPool.execute(runnable);
threadPool.execute(runnable);
}
二、线程池详解
1.七大参数详解
public ThreadPoolExecutor(
int corePoolSize,//线程池中的常驻核心线程数
int maximumPoolSize,//线程池能够容纳同时执行的最大线程数,此值必须大于等于1
long keepAliveTime,//多余的空闲线程的存活时间。
TimeUnit unit,//keepAliveTime的单位
BlockingQueue<Runnable> workQueue,//任务队列,被提交但尚未被执行的任务。
ThreadFactory threadFactory,//生成线程池中工作线程的线程工厂,用于创建线程一般用默认的
RejectedExecutionHandler handler)//拒绝策略,表示当队列满了并且工作线程大于等于线程池的最大线程数
}
2. 线程池创建线程流程
- 当线程数小于核心线程数时,创建线程。
- 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
- 当线程数大于等于核心线程数,且任务队列已满有两种选择:
若线程数小于最大线程数,创建线程;
若线程数等于最大线程数,抛出异常,拒绝任务。
3.七大不同线程池创建
线程池的创建方式总共包含7 种(其中 6 种是通过 Executors 创建的,1 种是通过 ThreadPoolExecutor创建的)
不同的
3.1 FixedThreadPool
创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待。
public static void fixedThreadPool() {
// 创建 2 个数据级的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(2);
// 创建任务
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("任务被执行,线程:" + Thread.currentThread().getName());
}
};
// 线程池执行任务(一次添加 4 个任务)
// 执行任务的方法有两种:submit 和 execute
threadPool.submit(runnable); // 执行方式 1:submit
threadPool.execute(runnable); // 执行方式 2:execute
threadPool.execute(runnable);
threadPool.execute(runnable);
}
3.2 CachedThreadPool
创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程。
public static void cachedThreadPool() {
// 创建线程池
ExecutorService threadPool = Executors.newCachedThreadPool();
// 执行任务
for (int i = 0; i < 10; i++) {
threadPool.execute(() -> {
System.out.println("任务被执行,线程:" + Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
});
}
}
3.3 SingleThreadExecutor
创建单个线程数的线程池,它可以保证先进先出的执行顺序。
public static void singleThreadExecutor() {
// 创建线程池
ExecutorService threadPool = Executors.newSingleThreadExecutor();
// 执行任务
for (int i = 0; i < 10; i++) {
final int index = i;
threadPool.execute(() -> {
System.out.println(index + ":任务被执行");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
});
}
}
3.4 ScheduledThreadPool
创建一个可以执行延迟任务的线程池。
public static void scheduledThreadPool() {
// 创建线程池
ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);
// 添加定时执行任务(1s 后执行)
System.out.println("添加任务,时间:" + new Date());
threadPool.schedule(() -> {
System.out.println("任务被执行,时间:" + new Date());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
}, 1, TimeUnit.SECONDS);
}
3.5 SingleThreadScheduledExecutor
创建一个单线程的可以执行延迟任务的线程池。
public static void SingleThreadScheduledExecutor() {
// 创建线程池
ScheduledExecutorService threadPool = Executors.newSingleThreadScheduledExecutor();
// 添加定时执行任务(2s 后执行)
System.out.println("添加任务,时间:" + new Date());
threadPool.schedule(() -> {
System.out.println("任务被执行,时间:" + new Date());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
}, 2, TimeUnit.SECONDS);
}
3.6 newWorkStealingPool
创建一个抢占式执行的线程池(任务执行顺序不确定)
public static void workStealingPool() {
// 创建线程池
ExecutorService threadPool = Executors.newWorkStealingPool();
// 执行任务
for (int i = 0; i < 10; i++) {
final int index = i;
threadPool.execute(() -> {
System.out.println(index + " 被执行,线程名:" + Thread.currentThread().getName());
});
}
// 确保任务执行完成
while (!threadPool.isTerminated()) {
}
}
3.7 ThreadPoolExecutor
最原始的创建线程池的方式
public static void myThreadPoolExecutor() {
// 创建线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));
// 执行任务
for (int i = 0; i < 10; i++) {
final int index = i;
threadPool.execute(() -> {
System.out.println(index + " 被执行,线程名:" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}