Java多线程入门

3 篇文章 0 订阅
1 篇文章 0 订阅

概述

多线程在多核处理器上的并发能力往往比单线程程序更好,但是编写稳定可靠的多线程程序往往并非易事。所幸JDK提供了很多并发包,同时也有很多开箱即用第三方工具可供选择。作为开发人员,我们需要掌握一些多线程的核心知识。在这篇博客中我们主要了解多线程的基本概念和使用方法。

使用场景

当需要运行不希望阻塞主线程的任务时,我们可以考虑另起一个线程。比如Android开发中的消息发送,Web后端开发中的定时任务等。此外在Web请求中,每一次请求就对应一个线程。可以说多线程无处不在。

使用方法

使用方法很简单,我们可以直接通过new一个Thread对象,来运行我们的任务。

Java示例:

public static void main(String[] args) {        
    Runnable task1 = () -> {
        System.out.println("hellotask1");           
        System.out.println(Thread.currentThread().getName());       
    };        
    Runnable task2 = () -> {            
        System.out.println("hellotask2");            
        System.out.println(Thread.currentThread().getName());        
    };        
    new Thread(task1, "thread-1").start();        
    new Thread(task2, "thread-2").start();        
    // hello task1        // thread-1        // hello task2        // thread-2    
}


当然在实际项目中我们更推荐线程池的方式来创建和管理线程。我们采用阿里Java手册推荐的方式,即创建ThreadPoolExecutor对象来实现线程池。上面task1和task2是我们采用函数式编程自定义的Runnable类型的函数表达式,用来代表两个需要在其它线程中执行的任务,然后通过new一个Thread对象来指定实现Runnable接口的任务以及线程名字,最后调用start方法执行任务。

ThreadPoolExecutor构造函数的主要参数:

  • corePoolSize 线程池核心线程数,也就是一旦创建了ThreadPoolExecutor,就会有corePoolSize个内驻线程。
  • maximumPoolSize 线程池允许的最大线程数,表示当需要的线程超过了corePoolSize,且workQueue已满,线程池会新建线程,直到线程数达到了maximumPoolSize。
  • keepAliveTime 表示超过了 corePoolSize的线程的最大空闲时长。如果超过线程的空闲时长超过keepAliveTime,就会被终结。
  • unit (TimeUnit) 空闲时长的单位。
  • workQueue(BlockingQueue<Runnable>) 被submit的任务的存放队列。
  • threadFactory (ThreadFactory) 线程的生产工厂,我们可以在这里定义线程的创建过程。
  • handler(RejectedExecutionHandler) 拒绝策略,比如workQueue放不下新的任务了,且没有空闲线程,就会触发拒绝策略。

当然我们也可以结合一张图来理解ThreadPoolExecutor线程池的工作方式

 

 

Java示例代码:

public class ThreadExample {
    public static void main(String[] args) {
        Runnable task1 = () -> {
            System.out.println("hello task1");
            System.out.println(Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        Runnable task2 = () -> {
            System.out.println("hello task2");
            System.out.println(Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        // 由于是LinkedBlockingQueue,工作队列永远不会满,所以maximumPoolSize应该设置的和corePoolSize一样大
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                2,
                2,
                0,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(),
                r -> new Thread(r, "my-pool-thread"));
        threadPoolExecutor.submit(task1);
        threadPoolExecutor.submit(task2);
        threadPoolExecutor.submit(task1);
        threadPoolExecutor.submit(task2);
        threadPoolExecutor.submit(task1);
        threadPoolExecutor.submit(task2);
        threadPoolExecutor.submit(task1);
        threadPoolExecutor.submit(task2);
        //        hello task1
        //        my-pool-thread
        //        hello task2
        //        my-pool-thread
        //        hello task1
        //        my-pool-thread
        //        hello task2
        //        my-pool-thread
        //        hello task1
        //        my-pool-thread
        //        hello task2
        //        my-pool-thread
        //        hello task1
        //        my-pool-thread
        //        hello task2
        //        my-pool-thread
    }
}


注意点

上述代码使用了默认的拒绝策略(由于是链表阻塞任务队列,拒绝策略永远不会执行),并且自定义了线程创建工厂,为线程命名,方便日后追踪问题。从运行结果可以看出,每隔一秒会同时输出task1和task2的运行结果。结果顺序可能不一样。

一般情况下,能不用多线程就尽量不要使用,或者在使用的时候尽量使用Java提供的并发工具类,此外在使用线程池的时候,要根据业务需要合理设置corePoolSize,另外,线程池的shutdown方法只会拒绝新提交的任务,在任务队列的任务还是会正常消费。

结语

多线程的创建和使用很简单,但是一定要正确区分使用场景,本篇博客只是简单介绍了入门的使用方法,供大家参考。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值