【并发编程】3.线程与线程池

一、线程

1.线程与进程

参考深入理解计算机系统中的概念
为了解决CPU与存储器之间的速度差异,来最大化利用CPU的性能而提出的概念

进程操作系统对正在运行的程序的一种抽象,感觉同时可以运行多个进程,而每个程序都好像在独占的使用处理器。
单核CPU 同一个时刻只能运行一个进程,同时运行多个是通过进程切换来交错运行来实行(上下文切换),因为CPU计算速度远超与文件读取、网络传输等任务。
多核CPU 则在同一时刻可以运行多个进程
上下文包含许多信息,包括PC、寄存器文件和主存中的内容,上下文切换就要报错当前进程的状态,加载新进程的上下文,由操作系统内核(kernel)进行,内核不是一个独立的进程,是系统全部进程所用代码和数据结构的集合

线程操作系统可识别的最小执行单元
现代操作系统中一个进程分为多个线程,这些线程共享该进程的上下文——>引发线程安全问题
多处理器时,多线程可以使得程序运行的更快

多核CPU与线程
早期单核CPU只能同时运行一个线程,多核处理器将多个CPU集成到一个集成电路芯片上。
超线程技术:模拟出的两个逻辑内核共享同一个CPU资源,所以同一时刻可以有两个线程都占用CPU资源,因此这两个线程都可以得到执行,这就是实现同一时间执行两个线程的并行操作。

所以现在的8核处理器在同一时间可以处理16个线程,称之为经典8核16线程

在Linux系统中可以通过以下指令查看CPU相关信息
查看CPU信息(型号)

[root@zhujun ~]# cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c
24 Intel(R) Xeon(R) CPU E5-2630 0 @ 2.30GHz

查看物理CPU个数

[root@zhujun ~]# cat /proc/cpuinfo| grep "physical id"| sort| uniq| wc -l
2

查看每个物理CPU中core的个数(即核数)

[root@zhujun ~]# cat /proc/cpuinfo| grep "cpu cores"| uniq
cpu cores : 6

查看逻辑CPU的个数

[root@zhujun ~]# cat /proc/cpuinfo| grep "processor"| wc -l
2.线程的生命周期


创建:一个新的线程被创建,等待该线程被调用执行;
就绪:线程已经启动等待被CPU分配时间片,也就是排队等待被CPU执行。
运行:此线程正在执行,正在占用时间片;
阻塞:也叫等待状态,等待某一事件(如IO或另一个线程)执行完;
退出:一个线程完成任务或者其他终止条件发生,该线程终止进入退出状态,退出状态释放该线程所分配的资源。

3.Java中的线程使用

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

public class MyThread extends  Thread{
    @Override
    public void run(){
        System.out.println(this.getName()+" run");
    }


    public static void main(String[] args) {
        MyThread th1 = new MyThread();
        th1.start();
    }
}

2.实现Runnable接口,通过该对象创建线程

public class MyRunnable implements Runnable{
    @Override
    public void run(){
        System.out.println("thread run");
    }

    public static void main(String[] args) {
        Thread th1 = new Thread(new MyRunnable());
        th1.start();
    }
}

二、线程池

1.为什么要使用线程池

Java中线程的创建并不只是在堆区分配内存并且创建对象,需要调用操作系统内核API,操作系统进行一系列资源的分配。
所以线程的频繁创建与销毁会增加应用的消耗。

所以需要使用线程池,线程池中维护了的工作线程,和一个存储工作的队列。

2.Java中的线程池

继承关系
Executor——>ExecutorService——>AbstractExecutorService——>ThreadPoolExecutor

ThreadPoolExecutor的最完整的构造中具有以下参数

public ThreadPoolExecutor(
int corePoolSize, //最小线程数
int maximumPoolSize, //最大线程数
long keepAliveTime, //空闲时间
TimeUnit unit,//空闲时间
BlockingQueue<Runnable> workQueue, //工作队列
ThreadFactory threadFactory, //线程工厂
RejectedExecutionHandler handler) //拒绝策略

ThreadPoolExecutor 已经提供了以下4 种策略:

  • CallerRunsPolicy:提交任务的线程自己去执行该任务。
  • AbortPolicy:默认的拒绝策略,会 throws RejectedExecutionException。
  • DiscardPolicy:直接丢弃任务,没有任何异常抛出。
  • DiscardOldestPolicy:丢弃最老的任务,其实就是把最早进入工作队列的任务丢弃,然后把 新任务加入到工作队列。

ThreadPoolExecutor中的重要方法
* execute()核心的方法,可以给线程池一个任务,交给线程池去执行。
* submit()方法 也是交给线程池一个任务并执行,区别是可以有返回值

3.实际开发中如何使用线程池

1.避免使用 Executors 线程工程类来创建线程池
提供的很多方法中工作队列默认使用的都是无界的LinkedBlockingQueue队列(没有指定容量),高负载情境下,无界队列很容易导致OOM。
2.工作队列使用有界队列
ArrayBlockingQueue 和 LinkedBlockingQueue 是支持有界,LinkedBlockingQueue 是否有界需要经过配置。

posted @ 2020-07-14 15:19  ShinyRou  阅读(187)  评论(0编辑  收藏  举报