线程池总结

run() 和start()方法区别

run()方法是普通方法的调用,通过idea断点调试,从始至终都是一个线程

在这里插入图片描述

start()调用会另开一个线程去执行

在这里插入图片描述

具体代码如下

package T1.threaddemo;

public class ThreadDemo extends Thread{

    private String name;
    public ThreadDemo(String name) {
        this.name=name;
    }

    @Override
    public void run() {
        System.out.println(name);
    }

    public static void main(String[] args) {
        //new ThreadDemo("monkey老师").run();
        new ThreadDemo("monkey老师").start();
    }
}

线程池为什么快 一个一个建线程为什么慢

测试:

一个一个创建线程,为什么慢?

因为每次创建都会底层调用cpu去分配资源,每次都调用cpu会导致慢

下面这个代码创建了多少个线程?

100001个

join方法去调用会怎么样?

得到的size大小要小于100000,join() 会让调用线程等待被调用线程结束后,才会继续执行。使用的场景为我们需要等待某个线程执行完成后才可继续执行的场景。

再调用了join方法会,调用的线程会一直等待被调用的线程执行完才会执行

join底层是调用Object的wait进行等待

package T1.threaddemo;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        long start=System.currentTimeMillis();//开始时间
        final Random random =new Random();
        final List<Integer> list=new ArrayList<>();
        for (int i = 0; i < 100000; i++) {
            Thread thread = new Thread(){
                @Override
                public void run() {
                    list.add(random.nextInt());
                }
            };
            thread.start();
            thread.join();
        }
        System.out.println("时间:"+(System.currentTimeMillis()-start));
        System.out.println("大小:"+list.size());

    }


}
时间:18962
大小:100000

线程池

package T1.threaddemo;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ThreadPoolTest {
    public static void main(String[] args) throws InterruptedException {
        long start=System.currentTimeMillis();//开始时间
        final Random random =new Random();
        final List<Integer> list=new ArrayList<>();
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 100000; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    list.add(random.nextInt());
                }
            });
        }
        executorService.shutdown();
        executorService.awaitTermination(1, TimeUnit.DAYS);
        System.out.println("时间:"+(System.currentTimeMillis()-start));
        System.out.println("大小:"+list.size());

    }
}
时间:59
大小:100000

线程池的7大参数

不同线程池执行MyTask任务快慢,这种快慢是针对MyTask任务,当任务量变大 这快慢都不一定了

package T1.threaddemo;

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

public class ThreadPoolDemo {

    public static void main(String[] args) {
        ExecutorService executorService1 = Executors.newCachedThreadPool();//快
        ExecutorService executorService2 = Executors.newFixedThreadPool(10);//慢
        ExecutorService executorService3 = Executors.newSingleThreadExecutor(); //最慢
        for (int i = 1; i <= 100; i++) {
            executorService1.execute(new MyTask(i));
        }
    }
}
class MyTask implements Runnable{

    int i=0;

    public MyTask(int i) {
        this.i = i;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"--"+i);
        try{
            Thread.sleep(1000L);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

上面创建线程池的方法都是调用ThreadPoolExecutor()

在这里插入图片描述

七大参数:

第二个参数应该为:线程池最大线程数量,不应该是非核心线程数

在这里插入图片描述

newCachedThreadPool()

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

0:核心线程数为0

Integer.MAX_VALUE :最大线程数为0x7fffffff

60L :时间

TimeUnit.SECONDS :单位

new SynchronousQueue()); 队列:这里是个同步队列,同步队列只能放一个,有人取了才可以放下一个

package T1.threaddemo;

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

public class ThreadPoolDemo {

    public static void main(String[] args) {
        ExecutorService executorService1 = Executors.newCachedThreadPool();//快
        ExecutorService executorService2 = Executors.newFixedThreadPool(10);//慢
        ExecutorService executorService3 = Executors.newSingleThreadExecutor(); //最慢
        for (int i = 1; i <= 100; i++) {
            executorService1.execute(new MyTask(i));
        }
    }
}
class MyTask implements Runnable{

    int i=0;

    public MyTask(int i) {
        this.i = i;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"--"+i);
        try{
            Thread.sleep(1000L);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

运用newCachedThreadPool();时候没线程里面有休眠时候运行结果为

...
pool-1-thread-86--86
pool-1-thread-87--87
pool-1-thread-88--88
pool-1-thread-89--89
pool-1-thread-90--90
....

当把休眠时间注释后会发现

​```java
pool-1-thread-16--86
pool-1-thread-27--87
pool-1-thread-15--56
pool-1-thread-28--88
pool-1-thread-19--89  不一定一一对应

因为线程复用

在这里插入图片描述

因为有些线程干完工作后会重新被用

newFixedThreadPool()

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

nThreads 核心线程数

nThreads 最大线程数 (相当于非核心员工为0,都是核心员工)

0L : 时间

TimeUnit.MILLISECONDS

当以该方法创建的线程池运行的结果:总共10个线程在运行

pool-2-thread-1--88
pool-2-thread-6--87
pool-2-thread-9--86
pool-2-thread-7--100
pool-2-thread-2--99
pool-2-thread-4--97
pool-2-thread-3--96

看下LinkedBlockingQueue())他有多长

 public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }

在这里插入图片描述

这里如果把初始调用构造器时候传参为100,那么第一种和第二种方式谁快?

可以认为一样的,除非在业务逻辑上可能才有点差距

newSingleThreadExecutor()

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

1 核心线程数

1 最大线程数

0L : 时间

TimeUnit.MILLISECONDS

在这里插入图片描述

pool-3-thread-1--86
pool-3-thread-1--87
pool-3-thread-1--88
pool-3-thread-1--89
pool-3-thread-1--90
pool-3-thread-1--91

只有一个线程在工作

keepAliveTime

是指当线程池中线程数量大于corePollSize时,此时存在非核心线程,keepAliveTime指非核心线程空闲时间达到的阈值会被回收。 java核心线程池的回收由allowCoreThreadTimeOut参数控制,默认为false,若开启为true,则此时线程池中不论核心线程还是非核心线程,只要其空闲时间达到keepAliveTime都会被回收。 但如果这样就违背了线程池的初衷(减少线程创建和开销),所以默认该参数为false

阿里巴巴为什么不推荐使用自带的线程池工具类?

上面三种Excutors创建线程池的方法中不会出现OOM的是哪个?

是:newCachedThreadPool

创建线程的本质就是CPU,如果线程创建很多只会导致CPU利用百分百,不会导致内存溢出

队列是同步队列,每次最多只存一个

其他两中方法会出现OOM是为什么?

因为他们的队列是无限长的,线程多的时候用队列存起来,可能导致队列会非常长,出现OOM

推荐我们使用自定义线程

下面代码会出现什么问题呢?

执行第31个时候会先拒绝策略异常

package T1.threaddemo;

import java.util.concurrent.*;

public class ThreadPoolDemo {

    public static void main(String[] args) {
        ExecutorService executorService1 = Executors.newCachedThreadPool();//快
        ExecutorService executorService2 = Executors.newFixedThreadPool(10);//慢
        ExecutorService executorService3 = Executors.newSingleThreadExecutor(); //最慢
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                10,
                20,
                0l,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(10));//自定义线程池
        for (int i = 1; i <= 100; i++) {
            threadPoolExecutor.execute(new MyTask(i));
        }
    }
}
class MyTask implements Runnable{

    int i=0;

    public MyTask(int i) {
        this.i = i;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"--"+i);
        try{
            Thread.sleep(1000L);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
pool-4-thread-17--27
pool-4-thread-13--23
pool-4-thread-10--10
pool-4-thread-15--25
pool-4-thread-16--26
pool-4-thread-18--28
pool-4-thread-12--22
pool-4-thread-19--29
pool-4-thread-20--30
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task T1.threaddemo.MyTask@2b193f2d rejected from java.util.concurrent.ThreadPoolExecutor@355da254[Running, pool size = 20, active threads = 20, queued tasks = 10, completed tasks = 0]
	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
	at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
	at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
	at T1.threaddemo.ThreadPoolDemo.main(ThreadPoolDemo.java:18)
pool-4-thread-12--11
pool-4-thread-17--12
pool-4-thread-7--17
pool-4-thread-10--20
pool-4-thread-9--19
pool-4-thread-1--18
pool-4-thread-8--16
pool-4-thread-16--13
pool-4-thread-13--14

为什么11-20会出现在21-30后面呢?

执行优先级

提交优先级

红色1 2 3为提交优先级

蓝色 1 2 3为执行优先级

在这里插入图片描述

提交优先级的源码

在这里插入图片描述
在这里插入图片描述

public void execute(Runnable command) {
    if (command == null) //空直接返回空指针异常
        throw new NullPointerException();
    /*
     * Proceed in 3 steps:
     *
     * 1. If fewer than corePoolSize threads are running, try to
     * start a new thread with the given command as its first
     * task.  The call to addWorker atomically checks runState and
     * workerCount, and so prevents false alarms that would add
     * threads when it shouldn't, by returning false.
     *
     * 2. If a task can be successfully queued, then we still need
     * to double-check whether we should have added a thread
     * (because existing ones died since last checking) or that
     * the pool shut down since entry into this method. So we
     * recheck state and if necessary roll back the enqueuing if
     * stopped, or start a new thread if there are none.
     *
     * 3. If we cannot queue task, then we try to add a new
     * thread.  If it fails, we know we are shut down or saturated
     * and so reject the task.
     */
    int c = ctl.get();
    if (workerCountOf(c) < corePoolSize) { //前面的10个先用核心的去处理
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    if (isRunning(c) && workQueue.offer(command)) { //中间11-20用队列存
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    } //21-30用非核心来处理,当非核心处理不了就调拒绝策略
    else if (!addWorker(command, false))
        reject(command);
}

调用拒绝策略:有四种,默认调用第一种

final void reject(Runnable command) {
    handler.rejectedExecution(command, this);
}

在这里插入图片描述
在这里插入图片描述

执行优先级

在这里插入图片描述

public class ThreadPoolTest {
    public static void main(String[] args) throws InterruptedException {
        long start=System.currentTimeMillis();//开始时间
        final Random random =new Random();
        final List<Integer> list=new ArrayList<>();
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 100000; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    list.add(random.nextInt());
                }
            });
        }
        executorService.shutdown();
        executorService.awaitTermination(1, TimeUnit.DAYS);
        System.out.println("时间:"+(System.currentTimeMillis()-start));
        System.out.println("大小:"+list.size());

    }
}
只创建了两个线程,里面是run方法的调用后进入runwork
public void run() {
            runWorker(this);
        }

runworker里面调的是task.run调用的普通方法不是start方法
final void runWorker(Worker w) {    
     wt.interrupt();
                try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        task.run();
                    }

submit和execute方法的区别?

  1. submit有返回值 execute没有返回值
  2. submit里面调用了execute方法
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值