浅谈Java中的多线程

并发编程笔记之Java 线程基础

一、创建和运行线程

方法一,直接使用 Thread
        // 构造方法的参数是给线程指定名字
        Thread t1 = new Thread("t1") {
            @Override
        // run 方法内实现了要执行的任务
            public void run() {
                System.out.println("线程任务");
            }
        };
        t1.start();
使用 Runnable 配合 Thread

把【线程】和【任务】(要执行的代码)分开
Thread 代表线程
Runnable 可运行的任务(线程要执行的代码)

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("要执行的任务");
            }
        };
        // 创建线程对象
        Thread t = new Thread(runnable, "线程名");
        // 启动线程
        t.start();
        

Java 8 以后可以使用 lambda 精简代码

// 创建任务对象
Runnable task2 = () -> System.out.println("要执行的任务"); ;
// 参数1 是任务对象; 参数2 是线程名字,推荐
Thread t2 = new Thread(task2, "t2");
t2.start();

Thread 与 Runnable 的关系:

方法1 是把线程和任务合并在了一起,
方法2 是把线程和任务分开了
用 Runnable 更容易与线程池等高级 API 配合
用 Runnable 让任务类脱离了 Thread 继承体系,更灵活

方法三,FutureTask 配合 Thread

FutureTask 能够接收 Callable 类型的参数,用来处理有返回结果的情况

// 创建任务对象
FutureTask<Integer> task3 = new FutureTask<>(() -> {
   System.out.println("要执行的任务");
   return 100;
});
// 参数1 是任务对象; 参数2 是线程名字,推荐
new Thread(task3, "t3").start();
// 主线程阻塞,同步等待 task 执行完毕的结果
Integer result = task3.get();
System.out.println("要执行的任务");

二、使用线程池创建和运行线程

线程池的构造方法:

public ThreadPoolExecutor(int corePoolSize,
						  int maximumPoolSize,
						  long keepAliveTime,
 						  TimeUnit unit,
 						  BlockingQueue<Runnable> workQueue,
						  ThreadFactory threadFactory,
 						  RejectedExecutionHandler handler)

corePoolSize :核心线程数目 (最多保留的线程数),
maximumPoolSize: 最大线程数目,线程池的线程数=核心线程数+救急线程数
keepAliveTime: 生存时间 - 针对救急线程(也称非核心线程),当核心线程全部都用完的时候,线程池会创建新的线程来执行任务,当核心线程有空了
unit 时间单位 :针对救急线程
workQueue :阻塞队列
threadFactory: 线程工厂 - 可以为线程创建时起个好名字
handler :拒绝策略

工作方式:
线程池中刚开始没有线程,当一个任务提交给线程池后,线程池会创建一个新线程来执行任务。
当线程数达到 corePoolSize 并没有线程空闲,这时再加入任务,新加的任务会被加入workQueue 队列排
队,直到有空闲的线程。如果队列选择了有界队列,那么任务超过了队列大小时,会创建 maximumPoolSize - corePoolSize 数目的线
程来救急。如果线程到达 maximumPoolSize 仍然有新任务这时会执行拒绝策略。拒绝策略 jdk 提供了 4 种实现

1、AbortPolicy 让调用者抛出 RejectedExecutionException 异常,这是默认策略
2、CallerRunsPolicy 让调用者运行任务
3、DiscardPolicy 放弃本次任务
4、DiscardOldestPolicy 放弃队列中最早的任务,本任务取而代之

当高峰过去后,超过corePoolSize 的救急线程如果一段时间没有任务做,需要结束节省资源,这个时间由
keepAliveTime 和 unit 来控制

根据这个构造方法,JDK Executors 类中提供了众多工厂方法来创建各种用途的线程池

第一种: newFixedThreadPool
  public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

特点: 核心线程数 == 最大线程数(没有救急线程被创建),因此也无需超时时间阻塞队列是无界的,可以放任意数量的任务,适用于任务量已知,相对耗时的任务

第二种: newFixedThreadPool
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

特点: 核心线程数是 0, 最大线程数是 Integer.MAX_VALUE,救急线程的空闲生存时间是 60s,意味着
全部都是救急线程(60s 后可以回收),救急线程可以无限创建队列采用了 SynchronousQueue 实现特点是,数据是在配对的生产者和消费者线程之间直接传递的,并不会将数据缓冲数据到队列中。可以这样来理解:生产者和消费者互相等待对方,握手,然后一起离开(一手交钱一手交货)。
整个线程池表现为线程数会根据任务量不断增长,没有上限,当任务执行完毕,空闲 1分钟后释放线
程。 适合任务数比较密集,但每个任务执行时间较短的情况

第三种: newSingleThreadPool
       public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
   

使用场景:
希望多个任务排队执行。线程数固定为 1,任务数多于 1 时,会放入无界队列排队。任务执行完毕,这唯一的线程
也不会被释放。
区别:自己创建一个单线程串行执行任务,如果任务执行失败而终止那么没有任何补救措施,而线程池还会新建一
个线程,保证池的正常工作Executors.newSingleThreadExecutor() 线程个数始终为1,不能修改
FinalizableDelegatedExecutorService 应用的是装饰器模式,只对外暴露了 ExecutorService 接口,因
此不能调用 ThreadPoolExecutor 中特有的方法Executors.newFixedThreadPool(1) 初始时为1,以后还可以修改
对外暴露的是 ThreadPoolExecutor 对象,可以强转后调用 setCorePoolSize 等方法进行修改

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值