今天聊聊多线程

简介: 今天聊聊多线程

多线程在Java开发领域算是比较常见, 工作中的业务代码都会使用多线程来提高执行效率, 充分的利用cpu多核的特性, 把电脑的性能发挥到极致。如何创建多线程呢?其实很简单, Java 提供了4种方式。

1、继承 Thread 类,重写run方法

2、实现Runnable接口,重写run方法,实现Runnable接口的实现类的实例对象作为Thread 构造函数的target

3、通过Callable和futureTask 创建线程

4、通过线程池创建线程


前面两种可以归结为一类,没有返回值, 原因很简单,通过重写run方法, run方法的返回值都是void, 所以没有返回结果。


方式1:继承Thread 类的线程实现方式如下

public class ThreadR extends Thread {
    public ThreadR() {
        //编写子类的构造方法,可缺省
        System.out.println("编写子类的构造方法");
    }
    @Override
    public void run() {
        //编写自己的线程代码
        System.out.println(Thread.currentThread().getName());
    }
    public static void main(String[] args) {
        ThreadR threadDemo01 = new ThreadR();
        threadDemo01.setName("我是自定义的线程1");
        threadDemo01.start();
        System.out.println(Thread.currentThread().toString());
    }
}

执行结果:

编写子类的构造方法

Thread[main,5,main]

我是自定义的线程1


方式2:通过实现Runnable 接口,实现run方法,接口的实现类的实例对象作为Thread的target作为参数传入带参的Thread构造函数,通过调用start()方法启动线程。

实现Runnable接口的线程实现方式如下

public class ThreadRun implements Runnable {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName());
        Thread thread = new Thread(new ThreadRun());
        thread.start();
    }
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " 通过接口的方式实现的 ");
    }
}

运行的结果为:

main

Thread-0 通过接口的方式实现的


线程实现方式3:通过Callable和futureTask 创建线程

a:创建Callable 接口的实现类, 并实现Call 方法

b:创建Callable实现类的实现, 使用futureTask 类包装Callable 对象, 该FutureTask 对象封装了Callable对象的Call 方法的返回值

c:使用FutureTask对象作为Thread对象的target创建并启动线程

d:调用FutureTask对象的get()来获取子线程执行结束的返回值

public class CallFutureV {
    public static void main(String[] args) {
        Callable<Object> callable = new CallFutureVK<Object>();
        FutureTask<Object> futureTask = new FutureTask<Object>(callable);
        Thread thread = new Thread(futureTask);
        System.out.println(" 通过Callable 的方式创建 " + Thread.currentThread().getName());
        thread.start();
    }
    static class CallFutureVK<Object> implements Callable<Object>{
        /**
         * 重写call 方法
         * @return
         * @throws Exception
         */
        @Override
        public Object call() throws Exception {
            System.out.println(Thread.currentThread().getName()+"------ 通过实现Callable接口 通过 futureTask 包装器实现的线程");
            return null;
        }
    }
}

程序运行结果:

通过Callable 的方式创建main

Thread-0------ 通过实现Callable接口 通过 futureTask 包装器实现的线程


方式4:通过线程池来创建


         

执行结果如下:

线程池参数定义的说明

ThreadPoolExecutor 参数介绍

ThreadPoolExecutor 最多可以设置7个参数, 如下代码显示

public ThreadPoolExecutor(int corePoolSize,
                           int maximumPoolSize,
                           long keepAliveTime,
                           TimeUnit unit,
                           BlockingQueue<Runnable> workQueue,
                           ThreadFactory threadFactory,
                           RejectedExecutionHandler handler) {
     // 省略...
 }

7个参数代表的含义如下:

参数1:corePoolSize

核心线程数, 线程池中始终存活的线程数。

参数2:maximumPoolSize

最大线程数,线程池中允许的最大线程数, 当线程池的任务队列满了之后可以创建的最大线程数。

参数3:keepAliveTime

最大线程数可以存活的时间,当线程中没有任务执行时, 最大线程就会销毁一部分, 最终保持核心线程数量的线程。

参数4:unit:

单位是和参数3存活时间配合使用的,合在一起用于设定线程的存活时间, 参数keepAliveTime 的时间单位有以下7种可选:

TimeUnit.DAYS:天
TimeUnit.HOURS:小时
TimeUnit.MINUTES:分
TimeUnit.SECONDS:秒
TimeUnit.MILLISECONDS:毫秒
TimeUnit.MICROSECONDS:微妙
TimeUnit.NANOSECONDS:纳秒

参数5:workQueue

一个阻塞队列,用来存储线程池等待执行的任务,均为线程安全, 包含7种类型

ArraryBlockingQueue:一个由数组结构组成的有界阻塞队列。
LinkedBlockingQueue: 一个由链表结构组成的有界阻塞队列。
SynchronousQueue:一个不存储元素的阻塞队列,即直接提交给线程不保存它们。
PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
DelayQueue:一个使用优先级队列实现的无界阻塞队列,只有在延迟期满时才能
从中提取元素。
LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。与SynchronousQueue类似
还含有非阻塞方法。
LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。

较常用的是 LinkedBlockingQueue 和 Synchronous , 线程池的排队策略与 BlockingQueue 有关


参数6:threadFactory

线程工厂, 用来创建线程,默认为正常优先级, 非守护线程。

参数7:handler

拒绝策略, 拒绝处理任务时的策略, 系统提供了4种可选

AbortPolicy:拒绝并抛出异常。
CallerRunsPolicy:使用当前调用的线程来执行此任务。
DiscardOldestPolicy:抛弃队列头部(最旧)的一个任务, 并执行当前任务。
DiscardPolicy:忽略并抛弃当前任务。

默认策略为 AbortPolicy


总结:

创建线程的4种方式也都介绍了, 但最主要的还是通过线程池来创建线程的方式。  这种方式的好处是能解决频繁创建线程带来的损耗。 创建线程池去管理线程,创建回收这种方式最大的解决了资源浪费的情况。

下节重点介绍线程池的方式使用和线程池创建后的执行流程。


目录
相关文章
|
5月前
|
安全 Java C#
C#多线程详解
C#多线程详解
45 0
|
10月前
|
安全 Linux C++
C++多线程
C++多线程
49 1
|
5月前
|
安全 数据库 芯片
多线程的使用
多线程的使用
54 0
|
监控 Java API
多线程专题
多线程专题
|
安全 Linux C++
C++多线程实现
C++11支持语言级别的多线程编程,可以跨平台运行,支持windows/linux/mac等。
110 0
|
Linux 调度
多线程具体实现(上)
多线程具体实现
73 0
|
安全 C++
C++多线程(二)
C++多线程
140 0
|
存储 安全 Java
多线程1
多线程1-https://www.nowcoder.com/issue/tutorial?tutorialId=94&uuid=4e79fb9392af4f90b898311e8c1efe36
60 0