• 【并发编程】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 是否有界需要经过配置。

  • 相关阅读:
    codeforces 540D Bad Luck Island (概率DP)
    Codevs 1205 单词反转(Vector以及如何输出string)
    Codeforces 977D Divide by three, multiply by two(拓扑排序)
    Codeforces 977B Two-gram(stl之string掉进坑)
    HDU 6186 CS Course (连续位运算)
    HDU 1005 Number Sequence(矩阵快速幂,快速幂模板)
    HDU 1004 Let the Balloon Rise(STL初体验之map)
    2018天梯赛、蓝桥杯、(CCPC省赛、邀请赛、ICPC邀请赛)校内选拔赛反思总结!
    Newcoder Wannafly13 B Jxy军训(费马小定理、分数在模意义下的值)
    TCP的可靠传输(依赖流量控制、拥塞控制、连续ARQ)
  • 原文地址:https://www.cnblogs.com/shinyrou/p/13298914.html
Copyright © 2020-2023  润新知