并发编程系列之线程简介

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> 并发编程系列之线程简介

并发编程系列之线程简介

前言

前几天我们把Java内存模型介绍了下,大家对JMM也有所认识了,从今天我们就开始走进一个我们天天挂在嘴边,听在耳边的东西:线程,对于线程相信大家都不会陌生,当然也有很多小伙伴在开发中或多或少的使用到线程,即使你没有使用过,但是并不代表你的程序中不存在,相反,他是一定会存在的,虽然这有点废话,OK,那我们废话也不多说,今天我们先对线程做个简单的介绍,那么,让我们一起开始新的旅途吧。

什么是线程?

线程是现代操作系统调度的最小单元,也叫轻量级进程,在一个进程中可以创建多个线程,每个线程都拥有自己单独的计数器,堆栈和局部变量等属性,并且能够访问共享的内存变量,处理器在这些线程之间,快速的切换,使用户造成多线程并发执行的错感,其实多线程本质上还是单线程,只是处理器在快速切换线程处理。

Java程序天生就是多线程程序,下面我们以一个main方法为例,我们什么都不做,只是启动一个main方法,看看执行结果:


public static void main(String[] args) {
    // 获取线程管理bean
    ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
    ThreadInfo[] dumpAllThreads = threadMXBean.dumpAllThreads(false, false);
    for (int i = 0; i  dumpAllThreads.length; i++) {
      System.out.println("系统中线程编号:"+dumpAllThreads[i].getThreadId()+" 线程名字:"+dumpAllThreads[i].getThreadName());
    }
  }

执行结果:
系统中线程编号:5 线程名字:Attach Listener
系统中线程编号:4 线程名字:Signal Dispatcher
系统中线程编号:3 线程名字:Finalizer
系统中线程编号:2 线程名字:Reference Handler
系统中线程编号:1 线程名字:main

从上面执行结果我们就能看到main方法在执行的时候,并不是只有一个main线程在执行,还有另外4个线程也在执行(对上面几个线程分被代表什么意思感兴趣的小伙伴可以自己去查阅相关资料)。

为什么要使用多线程?

正确的使用多线程能带来下面几种好处

更多的处理器核心:线程是大多数操作系统调度的基本单元,一个程序运行过程中能够创建多个线程,而一个线程在一个时刻只能运行在一个处理器核心上,假设在单线程环境下,一个线程只能使用一个处理器核心,就算有再多的处理器核心,也还是无法利用同时处理多个线程,所以这个时候多核系统的价值就无法体现出现,而多线程环境下,我们将计算逻辑分配到多个线程上,由多个处理器核心同时处理,显著提高系统的处理效率;

更快的响应时间:我们在处理业务逻辑时,可以将数据一致性不强的操作分给其他线程异步去处理,而尽快的响应用户请求,缩短了响应时间;

更好的编程模型:Java为多线程编程提供了一套完整的,良好的编程模型,使开发人员更快速方面的开发有安全保障的业务程序;

线程的优先级

现代操作系统基本采用时分的形式调度运行的线程,操作系统会分出一个个时间片,线程会分配到若干时间片,当线程的时间片用完就会触发线程调度,并等待下次分配,线程分配到的时间片多少决定了线程使用处理器资源的多少,而线程优先级就是决定线程能分配资源的多少。

在JAVA线程中,通过一个int priority来控制优先级,范围为1-10,其中10最高,默认值为5,优先级越高的线程分配到的时间片的数量越多。

设置线程优先级时的2个规则:

  • 如果线程是频繁阻塞的线程,那么就需要设置较高的优先级,给予足够的时间来处理该线程,防止线程阻塞导致进程崩溃;
  • 对于偏重计算(使用较多的CPU时间)的线程,要设置较低的优先级,防止处理器被独占;
  • 对于偏重计算(使用较多的CPU时间)的线程,要设置较低的优先级,防止处理器被独占;

    关于优先级的相关源码如下:

    线程优先级默认分3级,1-5-10

    
       /**
         * The minimum priority that a thread can have.
         */
        public final static int MIN_PRIORITY = 1;
    
       /**
         * The default priority that is assigned to a thread.
         */
        public final static int NORM_PRIORITY = 5;
    
        /**
         * The maximum priority that a thread can have.
         */
        public final static int MAX_PRIORITY = 10;
    

    设置优先级:

    
    public final void setPriority(int newPriority) {
            ThreadGroup g;
            checkAccess();
            if (newPriority  MAX_PRIORITY || newPriority  MIN_PRIORITY) {
                throw new IllegalArgumentException();
            }
            if((g = getThreadGroup()) != null) {
                if (newPriority  g.getMaxPriority()) {
                    newPriority = g.getMaxPriority();
                }
                setPriority0(priority = newPriority);
            }
        }
    

    线程的状态

    线程在运行的生命周期中有6种状态,如下:

    
    public enum State { 
            // 初始状态,线程被创建,但是还没有调用start方法
            NEW,
            // 运行状态(运行中和就绪都属于运行状态)
            RUNNABLE,
            // 阻塞状态,锁导致的阻塞
            BLOCKED,
            // 等待状态,当前线程需要等待其他线程动作
            WAITING,
            // 超时等待状态,可以在指定时间内自行返回
            TIMED_WAITING,
            // 终止状态,表示线程执行完毕
            TERMINATED;
        }
    

    线程各个状态之间的转换过程如下图:

    并发编程系列之线程简介

    守护线程(Daemon)

    守护线程是一种支持性线程,因为它主要被用作程序中后台调度以及支持性工作,也就是说,当Java虚拟机中不存在非守护线程时,虚拟机就会执行退出。

    使用如下方法将普通线程设置成守护线程:

    
    Thread thread = new Thread();    
        thread.setDaemon(true);
    

    源码如下:

    
    public final void setDaemon(boolean on) {
            checkAccess();
            if (isAlive()) {
                throw new IllegalThreadStateException();
            }
            daemon = on;
        }
    

    以上就是今天我们对线程的简介,通过本篇文章,让我们对线程有个概念和认识,方便我们后面内容的展开,感谢阅读,感谢关注!!!

    原文始发于微信公众号(Justin的后端书架):

    本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

    本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

    原文链接:blog.ouyangsihai.cn >> 并发编程系列之线程简介


     上一篇
    并发编程系列之线程的启动终止 并发编程系列之线程的启动终止
    前言 上节我们对线程有了个基本的概念和认识,从线程状态转变过程我们也已经知道了线程通过调用start方法进行启动,直到run方法执行线程结束,今天我们就来详细的说说启动和终止线程的细节,OK,让我们开始今天的并发之旅吧。 创建线程
    2021-04-05
    下一篇 
    并发编程系列之Final域的内存语义 并发编程系列之Final域的内存语义
    前言 上节我们讲了锁的内存语义,在同步原语中我们已经讲了两个,今天再来介绍另一个同步原语Final域,了解下final域的内存语义以及重排序规则在处理器中又是如何实现的,并结合前面的volatile和锁,大家可以进行对比下,OK,开始我们今
    2021-04-05