java并发编程(二):java并发基础与基本应用

一、线程简介

1、什么是线程

    现代操作系统在运行一个程序时,会为其创建一个进程。例如启动一个java程序,就会创建一个java进程。线程是操作系统最小的调度单位,也叫做轻量级进程,是进程中实际的运作单位。在一个进程里可以创建多个线程,每个线程的实体都包含程序计数器、堆栈、保留局部变量、寄存器、少数状态参数等,这些线程都能访问共享的内存变量。

线程和进程的区别是:

    进程是操作系统资源分配的基本单位,线程是任务调度和执行的基本单位,进程不是一个可执行实体,线程是能独立运行的基本单位。

    进程间相互独立,进程有单独完整的虚拟地址空间,而线程间共享所属进程的资源对其他进程不可见。

    进程间通信主要是管道、消息队列、信号、共享存储、套接字,而线程可以直接读写进程数据段(如全局变量)来通信。

    线程的上下文切换比进程的上下文切换要快的多,因为线程共享所属进程的虚拟空间内存,所以线程的上下文切换不必切换虚拟空间内存,代价要小的多。

    一个java程序从main()方法开始执行,java实际上天生就是多线程程序,执行main方法的是名叫main的线程,大量的其他子线程都是从它这里产生的,mian线程不是守护线程,通常它是最后一个执行完毕的线程。下面用JMX来查看一个普通的Java程序包含哪些线程。

《java并发编程的艺术》第4章第1节例子:


public static void main(String[] args) {

    ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();

    ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);

    for (ThreadInfo threadInfo : threadInfos) {

        System.out.println("[" + threadInfo.getThreadId() + "]" + threadInfo.getThreadName());

    }

}



书中给出的输出内容是:

    [4] Signal Dispatcher //分发处理发送给JVM信号的线程

    [3] Finalizer //调用对象 finalize 方法的线程

    [2] Reference Handler //清除Refernce的线程

    [1] main //main线程,用户程序入口

据考证,在linux系统环境下会输出书中一样的结果


在windows下 jdk11会输出:

    [1]main

    [2]Reference Handler

    [3]Finalizer

    [4]Signal Dispatcher

    [5]Attach Listener // jvm接收外部命令的线程,如果JVM启动时每初始化,那第一次执行jvm命令时会启动。

    [20]Common-Cleaner


在Mac系统下:

    [9] Monitor Ctrl-Break

    [4] Singal Dispatcher

    [3] Finalizer

    [2] Refernce Handler

    [1] main


2、为什么要使用多线程

    “执行一个简单的hello world程序,却启动了那么多无关线程,是不是把简单的问题复杂化?”,《java并发编程的艺术》书中回答说:当然不是,因为正确使用多线程,总是能够给开发人员带来显著的好处....

    我认为如果只是执行helloworld这种程序,肯定不需要多线程环境执行,但JVM的存在的意义不是执行这么简单的程序,而是根据需求得出最佳的编程模型,以较高的效率执行代码。所以,使用多线程的原因主要有以下几点:

    1、从执行效率上讲,在多核CPU系统上,将要执行的任务分割成多个任务并行执行,就可以显著提高执行效率。单核单线程处理器上,只能实现并发,也就是在一个时间区间内,任务根据系统分配的时间片上下文切换先后执行,因为时间片非常短,所以感觉是同时执行的,但实际上不考虑阻塞会比单线程更慢。而并行执行可以充分利用多核CPU的优势,多个任务同时跑在CPU上,无疑显著的减少程序处理时间,提高代码的执行效率。

    2、从程序设计角度上讲,在一些适合的场合,用多线程可以避免阻塞。比如在网络编程上,IO操作会导致当前线程阻塞,但如果采用多线程,可以避免因IO操作而导致无法接收其他客户端的连接请求。在实际开发中,比如查询数据库的操作,会导致当前线程阻塞直到得到查询结果,这对服务器程序来讲无法接受,所以可以在主线程外另起一个线程来做查询操作,将一些后续的操作放在这个线程里处理,可以充分避免因查询阻塞而导致服务器卡顿的情况。    

二、线程的使用

1、线程优先级

    线程优先级在早期的java版本中可能很有用,但现在不推荐使用它。在java线程中,可以通过一个整型成员变量priority来控制优先级,范围从1到10,在线程构建时可通过setPriority来修改优先级,默认优先级是5。优先级高的获得时间片的几率会大于优先级低的。

    线程优先级不能作为程序正确性的依赖,因为操作系统可能完全不理会java线程对优先级的设定。

2、线程的状态

    java线程在运行的生命周期中可能处于6种不同的状态,在一个给定的时刻,线程只能处于其中的一个状态。

    状态名称                说明

    NEW                      初始状态,线程被构建,但还没调用start方法

    RUNNABLE           运行状态,Java线程将操作系统中的就绪和运行两种状态笼统地称作“运行中”

    BLOCKED             阻塞状态,表示线程阻塞于锁

    WAITING               等待状态,表示线程进入等待状态,进入该状态表示当前线程需要等待其他线程做出一些特定动作(通知或中断)

    TIME_WAITING    超时等待状态,该状态不同于WAITING,它是可以在指定的时间自行返回的

    TERMINATED       终止状态,表示当前线程已经执行完毕


3、Daemon守护线程

    Daemon线程是一种支持型线程,因为它主要被用作程序中后台调度以及支持性工作。这意味着,当一个java虚拟机中不存在非Daemon线程的时候,Java虚拟机将会退出。可以调用Thread.setDaemon(true)将线程设置为Daemon线程,但要在线程执行start方法之前初始状态设置,不能在启动线程之后设置。

    Daemon线程被用作完成支持性工作,但是在java虚拟机退出时Daemon线程中的finally块并不一定会执行,所以在构建Daemon线程时,不能依靠finally块中的内容来确保执行关闭或清理资源的逻辑。

4、线程的启动和终止

4.1 构造线程:

    在运行线程之前首先要构造一个线程对象,线程对象在构造的时候需要提供线程所需要的属性,如线程所属的线程组、线程优先级、是否是Daemon线程等信息。

java源码 jdk11.0.3

    在构建过程中,一个新构造的线程对象是由其parent线程来进行空间分配的,而child线程继承了parent是否为Daemon、优先级和加载资源的contextClassLoader以及可继承的ThreadLocal,同时还会分配一个唯一ID来标识这个child线程。至此,一个能够运行得线程对象就初始化好了,在堆内存中等待着运行。

    创建线程的四种方式:继承Thread类、实现runnable接口、实现callable接口、使用线程池创建

4.2 启动线程:

    线程对象初始化完成后,调用start()方法就可以启动这个线程。线程start()方法的含义是:当前线程(即parent线程)同步告知Java虚拟机,只要线程规划器空闲,应立即启动调用start()方法的线程,调用该线程的run()方法。

4.3 理解中断:

    在程序中免不得要碰到需要暂停或停止当前线程的情况,比如负责下载的程序会占用大部分带宽,用户想玩网游就要暂停下载任务,这个时候就要停止当前的线程让出带宽给用户玩游戏,而这种情况就需要用到线程的中断机制。

    中断可以理解为线程的一个标识位属性,它表示一个运行中的线程是否被其他线程进行了中断操作。中断好比其他线程对该线程打了个招呼,其他线程调用该线程的interrupt()方法对其进行中断操作。

    线程通过方法isInterrupted()来进行判断是否被中断,也可以调用静态方法Thread.interrupted()对当前线程的中断标识位进行复位。如果该线程已经处于终结状态,即使该线程被中断过,在调用该线程对象的isInterrupted()时依旧会返回false。

    从Java的API中可以看到,许多声明抛出InterruptedException的方法(例如Thread.sleep(long millis)方法)这些方法在抛出InterruptedException之前,Java虚拟机会先将该线程的中断标识位清除,然后抛出InterruptedException,此时调用isInterrupted()方法将会返回false。

    可以看一下以下例子观察两个线程的中断标识位。


    输出的结果:


    从结果可以看出,抛出InterruptedException的线程SleepThread,其中断标识位被清除了,而一直忙碌运作的线程BusyThread,中断标识位没有被清除。

4.4 过期的suspend()、resume()和stop()

    大家对于CD机肯定不会陌生,如果把它播放音乐比作一个线程的运作,那么对音乐播放做出的暂停、恢复和停止操作对应在线程Thread的API就是suspend()、resume()和stop()。

    在以下代码中,创建了一个线程PrintThread,它以1秒的频率进行打印,而主线程对其进行暂停、恢复和停止操作。


    输出如下:(输出内容中的时间与示例执行的具体时间相关)

    在执行过程中,PrintThread运行了3秒,随后被暂停,3秒后恢复,最后经过3秒被终止。通过示例的输出可以看到,suspend()、resume()和stop()方法完成了线程的暂停、恢复和终止,而且非常“人性化”。但是这些API是过期的,不建议使用。

    不建议使用的主要原因有:suspend()方法在调用后,线程不会释放已经占有的资源(比如锁),而是占有着资源进入睡眠状态,这样容易引发死锁问题。同样,stop()方法在终结一个线程时不会保证线程的资源正常释放,通常是没有给线程完成资源释放工作的机会,因此会导致程序可能工作在不确定状态下。

    暂停和恢复操作可以用等待/通知机制替代。

4.5 安全地终止线程

    可以采用对线程设置中断标识后安全的终止线程。使用interrupt()方法设置线程的中断标识位,由该线程检测进行中断。另外还可以在该线程中自主增加一个boolean类型用于判断是否终止该线程。


    输出结果:(输出内容可能不同)


三、常见面试题问题

    1、线程和进程的区别是什么

    2、线程的状态有哪几种

    3、Java中创建线程有几种方式,你喜欢哪一种,为什么

    4、如何安全的终止线程

    5、为什么不推荐使用suspend()、resume()和stop()

    摘抄出处:《java并发编程的艺术》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,128评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,316评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,737评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,283评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,384评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,458评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,467评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,251评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,688评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,980评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,155评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,818评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,492评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,142评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,382评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,020评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,044评论 2 352

推荐阅读更多精彩内容