IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 一文快速理解java多线程的4种实现方式 -> 正文阅读

[Java知识库]一文快速理解java多线程的4种实现方式

继承Thread

1,定义Thread的子类,并重写该类的run方法,该run的方法的方法体就代表了线程要完成的任务。
2,创建Thread子类的实例,即创建了线程的对象。
3,调用线程对象的start()方法来启动该线程

package com.evan.springboot.study.thread;

/**
 * @author evanYang
 * @version 1.0
 * @date 2022/04/23 09:55
 */
public class MyThread extends Thread {

    public void run(){

        System.out.println(this.getName()+" this is my thread");
    }

    public static void main(String[] args) {
        System.out.println("this is main");
        for (int i = 0; i < 50; i++) {
            MyThread myThread = new MyThread();
            myThread.run();
        }

    }
}

实现Runnable

1,定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
2,创建runnable实例类的实例,并依赖此实例作为Thread的target来创建Thread对象,该Thread才是真正的线程对象
3,使用线程对象的start()方法来启动该线程

package com.evan.springboot.study.thread;

/**
 * @author evanYang
 * @version 1.0
 * @date 2022/04/23 10:01
 */
public class MyRunable implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+" 实现runable");
    }

    public static void main(String[] args) {
        System.out.println("main is starting");
        for (int i = 0; i < 10; i++) {
            MyRunable myRunable = new MyRunable();
            Thread thread = new Thread(myRunable);
            thread.start();
        }
    }
}

main is starting
Thread-0 实现runable
Thread-1 实现runable
Thread-2 实现runable
Thread-4 实现runable
Thread-5 实现runable
Thread-6 实现runable
Thread-7 实现runable
Thread-9 实现runable
Thread-8 实现runable
Thread-3 实现runable

Process finished with exit code 0

实现Callable接口通过FutureTask包装器来创建Thread线程

1,创建Callable接口实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
2,创建Callable实现类的实例,使用FutureTask类来包装Callable对象。该FutureTask对象封装了该Callable对象的call()方法的返回值。FutureTask是一个包装器,它通过接受Callable来创建,它同时实现了Future和Runnable接口。
3,使用FutureTash对象作为Thread对象的target创建并启动新线程。
4,调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。

package com.evan.springboot.study.thread;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * @author evanYang
 * @version 1.0
 * @date 2022/04/23 10:08
 */
public class MyCallableThreadTest implements Callable<String> {
    @Override
    public String call() throws Exception {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+ i);
        }
        return "return";
    }

    public static void main(String[] args) {
        MyCallableThreadTest myCallableThreadTest = new MyCallableThreadTest();
        FutureTask futureTask = new FutureTask<>(myCallableThreadTest);
        new Thread(futureTask,"this is mycallable").start();
        try {
            Object o = futureTask.get();
            System.out.println(o);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

创建线程的三种方式对比

  • 1,采用实现Runnable、Callable接口的方式创建多线程时,

优势是:
1,线程只是实现了Runnable接口或Callable接口,还可以继承其他类。
2,在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个线程来处理同一份资源的情况,从而可以将cpu,代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
劣势是:
1,编程会稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法。

  • 2,使用继承Threda类的方式创建多线程优势是:
    2.1编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。

劣势是:

2.2 线程类已经继承了Thread类,所以不能再继承其他父类。

通过线程池创建线程,使用线程池接口ExecutorService结合Callable、Future实现有返回结果的多线程

一个线程池包括以下四个基本组成:
1,线程池管理器ThreadPool:用于创建并管理线程包括 创建线程池,销毁线程池,添加新任务;
2,工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务。
3,任务接口(task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
4,任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制、

参数解释

  • corePoolSize:核心线程数,
    如果等于0,则任务执行完后,没有任务请求进入时销毁线程池中的线程,如果大于0,即使本地任务执行完毕,核心线程也不会销毁。设置过大会浪费资源,设置过小导致线程频繁创建。
  • maximumPoolSize:最大线程数

必须大于等于1,且大于核心线程数。如果与corePoolSize相等,则线程池大小固定。如果大于corePoolSize,则最多创建maximumPoolSize执行任务。

  • keepAliveTime:线程空闲时间
    线程池中线程空闲时间达到keepAliveTime值时,线程会被销毁,直到只剩下corePoolSize个线程为止。默认情况下,线程池的最大线程数大于corePoolSize时,keepAliveTime才会起作用。如果allowCoreThreadTimeOut被设置为true,即使线程池的最大线程数等于corePoolSize,keepAliveTime才会起作用(回收超时的核心线程)。

  • unit:

TimeUnit表示时间单位。

  • workQueue:缓存队列

当请求线程数大于corePoolSize时,线程进入BlockingQueue阻塞队列

拒绝策略

public class ThreadPoolExecutor extends AbstractExecutorService {
}
public static class AbortPolicy implements RejectedExecutionHandler {
        /**
         * Creates an {@code AbortPolicy}.
         */
        public AbortPolicy() { }
}
 public static class DiscardPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code DiscardPolicy}.
         */
        public DiscardPolicy() { }
}

public static class DiscardOldestPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code DiscardOldestPolicy} for the given executor.
         */
        public DiscardOldestPolicy() { }
}
 public static class CallerRunsPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code CallerRunsPolicy}.
         */
        public CallerRunsPolicy() { }
}

默认AbortPolicy()

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。

DiscardPolicy

ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。如果线程队列已满,则后续提交的任务都会被丢弃,且是静默丢弃。

DiscardOldestPolicy

ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务。

CallerRunsPolicy

ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

submit()和execute()区别

execute

public interface Executor {

    /**
     * Executes the given command at some time in the future.  The command
     * may execute in a new thread, in a pooled thread, or in the calling
     * thread, at the discretion of the {@code Executor} implementation.
     *
     * @param command the runnable task
     * @throws RejectedExecutionException if this task cannot be
     * accepted for execution
     * @throws NullPointerException if command is null
     */
    void execute(Runnable command);
}

execute()方法的入参为一个Runnable,返回值为void,这时候我们已经知道了execute()方法的来源以及其定义

submit()

submit()是ExecutorService 接口中的,入参可以为Callable,也可以为Runnable,而且方法有返回值Future;

public interface ExecutorService extends Executor {
 /**
     * Submits a value-returning task for execution and returns a
     * Future representing the pending results of the task. The
     * Future's {@code get} method will return the task's result upon
     * successful completion.
     *
     * <p>
     * If you would like to immediately block waiting
     * for a task, you can use constructions of the form
     * {@code result = exec.submit(aCallable).get();}
     *
     * <p>Note: The {@link Executors} class includes a set of methods
     * that can convert some other common closure-like objects,
     * for example, {@link java.security.PrivilegedAction} to
     * {@link Callable} form so they can be submitted.
     *
     * @param task the task to submit
     * @param <T> the type of the task's result
     * @return a Future representing pending completion of the task
     * @throws RejectedExecutionException if the task cannot be
     *         scheduled for execution
     * @throws NullPointerException if the task is null
     */
    <T> Future<T> submit(Callable<T> task);
}

两者的区别

1,接收的参数不一样
2,submit()有返回值,而execute()没有
例如,有个validation的task,希望该task执行完后告诉我它的执行结果,是成功还是失败,然后继续下面的操作。
3,submit()可以进行Exception处理;
例如,如果task里会抛出checked或者unchecked exception,而你又希望外面的调用者能够感知这些exception并做出及时的处理,那么就需要用到submit,通过对Future.get()进行抛出异常的捕获,然后对其进行处理。

定长线程池 FixedThreadPool

固定数量的线程池,该线程池中的线程数量始终不变。
当有一个新任务提交时,线程池中若有空闲线程,则立即执行,若没有,则新的任务会被暂存到一个任务队列中,待有空闲线程在执行任务。
若任务队列满了,线程没有达到max线程数量,则会创建新的线程执行,直到达到最大线程。
若最大线程也创建完毕,则会走拒绝策略。

package com.evan.springboot.study.thread;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

/**
 * @author evanYang
 * @version 1.0
 * @date 2022/04/23 15:27
 */
public class MyThreadPool {
    public static void main(String[] args) {
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 4; i++) {
            int temp=i;
            fixedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("execute;"+Thread.currentThread().getName()+",i"+temp);
                }
            });
        }


        List<Future<String>> list = new ArrayList<Future<String>>();
        for (int i = 0; i < 3; i++) {
            Future<String> submit = fixedThreadPool.submit(new CallableWithResult(i));
            list.add(submit);
        }
        for (Future<String> future : list) {
            try {
                System.out.println("future result is "+future.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }


}
class CallableWithResult implements Callable<String>{
    private int id;
    public CallableWithResult(int id){
        id=this.id;
    }
    @Override
    public String call() throws Exception {
        System.out.println("call()方法被自动调用,干活!!!             " + Thread.currentThread().getName());
        // 下面的判读是模拟一个抛出异常的操作,随机得到一个true
        if (new Random().nextBoolean())
            throw new TaskException("Meet error in task." + Thread.currentThread().getName());
        // 一个模拟耗时的操作
        for (int i = 999999; i > 0; i--)
            ;
        return "call()方法被自动调用,任务的结果是:" + id + "    " + Thread.currentThread().getName();
    }
}
class TaskException extends Exception{
    public TaskException(String message){
        super(message);
    }
}

CachedThreadPool缓存线程池

如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程

该方法返回一个可根据实际情况调整线程数量的线程池。线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复用。

package com.evan.springboot.study.thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author evanYang
 * @version 1.0
 * @date 2022/04/23 16:32
 */
public class MyCacheThreadPool {
    public static void main(String[] args) {
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        for (int i = 0; i < 20; i++) {
            int tem=i;
            cachedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("threadName;"+Thread.currentThread().getName()+",i"+tem);
                }
            });
        }
    }
}

threadName;pool-1-thread-1,i0
threadName;pool-1-thread-6,i5
threadName;pool-1-thread-7,i6
threadName;pool-1-thread-4,i3
threadName;pool-1-thread-3,i2
threadName;pool-1-thread-2,i1
threadName;pool-1-thread-8,i7
threadName;pool-1-thread-5,i4
threadName;pool-1-thread-9,i8
threadName;pool-1-thread-10,i9
threadName;pool-1-thread-12,i11
threadName;pool-1-thread-11,i10
threadName;pool-1-thread-7,i12
threadName;pool-1-thread-2,i19
threadName;pool-1-thread-9,i16
threadName;pool-1-thread-8,i18
threadName;pool-1-thread-11,i13
threadName;pool-1-thread-10,i15
threadName;pool-1-thread-12,i14
threadName;pool-1-thread-5,i17

可以看到本来创建了20个线程池,这里只用了12个,因为newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程

newSingleThreadExecutor

创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

package com.evan.springboot.study.thread;

import lombok.SneakyThrows;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

/**
 * @author evanYang
 * @version 1.0
 * @date 2022/04/23 16:37
 */
public class MySingleThreadPool {
    public static void main(String[] args) {
        ExecutorService sing = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            final int index=i;
            sing.execute(new Runnable() {
                @SneakyThrows
                @Override
                public void run() {
                    System.out.println("index: "+index);
                    Thread.sleep(2000);
                }
            });
        }
    }
}

index: 0
index: 1
index: 2
index: 3
index: 4
index: 5
index: 6
index: 7
index: 8
index: 9

newScheduledThreadPool

支持定时及周期性的任务执行。

package com.evan.springboot.study.thread;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @author evanYang
 * @version 1.0
 * @date 2022/04/23 16:42
 */
public class MyScheduledThredaPool {
    public static void main(String[] args) {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
        for (int i = 0; i < 10; i++) {
            final int tem=i;
            scheduledExecutorService.schedule(new Runnable() {
                @Override
                public void run() {
                    System.out.println("i:" + tem);
                }
            },3, TimeUnit.SECONDS);
        }

    }
}

i:0
i:4
i:2
i:3
i:1
i:7
i:9
i:6
i:5
i:8

禁止直接使用Executors创建线程池原因:

Executors.newCachedThreadPool和Executors.newScheduledThreadPool两个方法最大线程数为Integer.MAX_VALUE,如果到达上线,没有任务服务器可以继续工作,肯定会抛出OOM异常。

Executors.newSingleThreadExecutor和Executors.newFixedThreadPool两个方法的workQueue参数newLinkedBlockingQueue(),容量为Integer.MAX_VALUE,如果瞬间请求非常大,会有OOM风险。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-04-24 09:15:04  更:2022-04-24 09:17:34 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 2:59:29-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码