JAVA并发(线程池及其他并发相关内容)

前言

OOM : 内存泄露异常

线程池

概念

线程池主要是控制运行线程的数量,将待处理的任务放到等待队列,然后创建线程执行这些任务,如果超过了最大线程数,则等待。
优点:线程复用;控制最大并发数;管理线程
线程复用:不用一直new新线程,重复利用已经创建的线程来降低线程的常见和销毁开销,节省系统资源。
提高响应速度:当任务达到时,不用创建新的线程,直接利用线程池中的线程
管理线程:可以控制最大并发数,控制线程的创建等。

Executor->ExecutorService->AbstractExecutorService->ThreadPoolExecutor。
Executor有自己的工具类:Executors

ThreadPoolExector

Executors.newFixedThreadPool(int) 固定数量的线程池,用于执行长期任务,性能会很好。
Executors.newSingleThreadExecutor() 一池只有一个线程。
以上两种LinkedBlockingQueue实现,队列长度默认为Integer.MAX_VALUE,约为21亿,可能会堆积大量请求,从而导致OOM。
Executors.newCachedThreadPool() 使用SynchronousQueue实现,变长线程池。执行很多短期异步任务,线程池根据需要创建线程,但在先前勾线的线程可用时将重用他们。可自动扩容。
此方式允许创建的线程数量为Integer.MAX_VALUE,可能会创建大量线程,从而导致OOM。

除此之外,还有Executors.newScheduledThreadPool(),Executors.newWorkStealingPool()等

阿里规范中,不允许使用Executors创建,必须通过ThreadPoolExecutor方式创建。
除此之外,还有

线程池底层构造方法源码

/**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     * parameters.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @param maximumPoolSize the maximum number of threads to allow in the
     *        pool
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.
     * @param unit the time unit for the {@code keepAliveTime} argument
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.
     * @param threadFactory the factory to use when the executor
     *        creates a new thread
     * @param handler the handler to use when execution is blocked
     *        because the thread bounds and queue capacities are reached
     * @throws IllegalArgumentException if one of the following holds:<br>
     *         {@code corePoolSize < 0}<br>
     *         {@code keepAliveTime < 0}<br>
     *         {@code maximumPoolSize <= 0}<br>
     *         {@code maximumPoolSize < corePoolSize}
     * @throws NullPointerException if {@code workQueue}
     *         or {@code threadFactory} or {@code handler} is null
     */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

七个参数:
corePoolSize 线程池中的常驻核心线程数
maximumPoolSize 线程池中能容纳的同时执行的最大线程数,必须大于1
keepAliveTime 多余的空闲线程(超过corePoolSize的数量的线程)的存活时间
unit keepAliveTime的存活时间单位
workQueue 任务队列 new LinkedBlockingQueue<>(int)
threadFactory 生成线程池中的工作线程的线程工厂,用于创建线程,一般默认
handler 拒绝策略,当队列满满,且工作线程大于等于maximumPoolSize,拒绝请求执行的runnable的策略

拒绝策略(阻塞队列)

AbortPolicy,默认策略,直接抛出RejectedException。

new ThreadPoolExecutor.AbortPolicy();

CallRunnablePolicy,不会抛出异常,也不会终止任务,而是将任务返回给调用者(主线程),从而降低新任务的流量。

new ThreadPoolExecutor.CallRunnablePolicy();

DiscardOldestPolicy ,抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交任务。

new ThreadPoolExecutor.DiscardOldestPolicy();

DiscardPolicy该策略默默地丢弃无法处理的任务,不予任何处理也不抛出异常。如果允许任务丢失,只是最好的方法。

new ThreadPoolExecutor.DiscardPolicy();

参数选择

maximumPoolSize选择:
对于CPU密集型任务,最大线程数是CPU线程数+1。
对于IO密集(多文件上传下载),需要尽量多的线程数,可以是CPU线程数*2,或者CPU线程数/(1-阻塞系数)。
workQueue大小选择
maximumPoolSize相同或maximumPoolSize - corePoolSize,具体得根据实际情况

死锁

各自持有一把锁,又想获取对方的锁
示例

class HoldLockThread implements Runnable {
    private String lockA;
    private String lockB;

    public HoldLockThread(String lockA, String lockB) {
        this.lockA = lockA;
        this.lockB = lockB;
    }

    @Override
    public void run() {
        synchronized (lockA) {
            System.out.println(Thread.currentThread().getName() + "\t自己持有:" + lockA + "\t尝试获取:" + lockB);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (Exception e) {
                e.printStackTrace();
            }
            synchronized (lockB) {
                System.out.println(Thread.currentThread().getName() + "\t自己持有:" + lockB + "\t尝试获取:" + lockA);
            }
        }
    }
}

public class DeadLockDemo {
    public static void main(String[] args) {
        String lockA = "lockA";
        String lockB = "lockB";
        new Thread(new HoldLockThread(lockA, lockB), "ThreadA").start();
        new Thread(new HoldLockThread(lockB, lockA), "ThreadB").start();
    }
}

如何找出死锁

先用jps -l命令获取进程号
再通过jstack [进程号]查看

LockSupport

等待唤醒方式:
1、Object的wait()方法让线程等待,Object的notify()方法唤醒
2、JUC包中Contidion的await()方法让线程等待,使用signal()、signalAll()方法唤醒
以上两种方法必须在同步块或同步方法里且成对出现使用。

3、LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程

通过park()和unpark(thread)方法来实现阻塞和唤醒线程的操作
LockSupport是一个线程阻塞工具类,所有的方法都是静态方法,可以让线程在任意位置阻塞,阻塞之后也有对于的唤醒方法。(调用的是Unsafe里的native方法)

LockSupport类使用了一种名为Permit(许可)的概念做到阻塞和唤醒线程的功能,每个线程都有一个permit,permit只有两个值,0和1,默认是0。(类似Semaphore,但是许可的累加上限是1)

Thread t1 = new Thread(()->{
	//开始等待,当许可证不为0时-1后继续执行
	LockSupport.park();
});
t1.start();
new Thread(()->{
	//唤醒t1,许可证变为1
	LockSupport.unpark(t1);
}).start();

连续调用unpark只相当于调用一次,连续调用unpark再连续调用park时只有第一次会通过,之后的会等待。

AQS(AbstractQueuedSynchronizer抽象同步队列)

通过内置的CLH(FIFO)队列的变种来完成资源获取线程的排队工作,将每条将要去抢占资源的线程封装成一个Node节点来实现锁的分配,有一个int类变量表示持有锁的状态,通过CAS完成对status值的修改(0表示没有,1表示阻塞)

双向链表,将每一个线程封装成为一个Node对象,在Node节点中包含节点状态()和上一个节点(pre),下一个节点(next)。
非公平锁模式:当资源释放时,先唤醒第一个节点,若节点已取消,则将后续节点一起唤醒,谁抢到则谁占用。
公平锁模式:当资源释放时,先唤醒对位节点,让队尾节点开始递归判断前置节点是否为待唤醒状态,找到最靠前的待唤醒节点,将其唤醒,使其使用资源。

Callable接口

Runnable方法的run()无法带返回值

1、Callable带返回值
2、会抛异常
3、覆写call()方法,而不是run()方法

Runnable和Future的子接口RunnableFuture,实现类为FutureTask
创建时需要传入实现Callable接口的类。
通过get方法获取返回值
用法

//实现Callable接口
class MyCallnable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        try { 
         	//todo
         } catch (InterruptedException e) {e.printStackTrace(); }
         //返回结果
        return 0;
    }
}

public class CallableDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建FutureTask类,接收MyCallnable 。
        FutureTask<Integer> futureTask = new FutureTask<>(new MyCallnable ());
        //将FutureTask对象放到Thread类的构造器里面。
        new Thread(futureTask).start();
        //用FutureTask的get方法得到返回值。
        int result = futureTask.get();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值