当前位置: 首页 > news >正文

Java并发3:线程

本文是根据《Java并发编程的艺术》以及之前的文章 juejin.im/post/5c09db… 和 juejin.im/post/5c0cff… 共同整理而来。

进程和线程

进程

进程是操作系统结构的基础;是一次程序的执行;是一个程序及其数据在处理机上顺序执行时所发生的活动;是程序在一个数据集合上运行的过程,它是从系统进行资源分配和调度的一个独立单位。

线程

线程是一个比进程更小的执行单位,可以理解成是在进程中独立运行的子任务。一个进程在执行过程中可以产生多个线程,同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间做切换时,负担比进程小得多。

多线程

多线程就是指多个线程同时运行或交替运行。单核CPU是顺序执行,也即交替运行。多核CPU,因为每个CPU有自己的运算器,所以在多个CPU可以同时运行。

为什么要使用多线程

  1. 更多的处理器核心,使得多线程能分配到多个处理器核心,减少了程序的处理时间,提升了效率
  2. 更快的响应时间:将数据一致性不强的操作派发给不同的线程,从而缩短了响应时间
  3. 更好的编程模型

相关概念

线程优先级

Java线程中,通过整形变量priority来控制优先级,范围从1-10,创建线程时可以设置,默认为5。在不同的JVM和操作系统上,线程规划存在差异,有些操作系统甚至会忽略对线程优先级的设定。

同步和异步

同步方法调用开始后,调用者必须等待,直到方法调用返回后,才能继续后序的行为。可以理解为,排队执行。

异步方法调用像是一个消息传递,当一个异步过程调用发出后,调用者可以继续后序的操作,但是调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。多线程是异步的,其调用时机是随机的。

并发和并行

并发是指一个处理器同时处理多个任务。并行是指多个处理器或者是多核的处理器同时处理多个不同的任务。

如上图所示,并发是逻辑上是同时发生。并行是物理上的同时发生。

多线程在单核CPU的话是顺序执行,也就是交替运行(并发)。多核CPU的话,因为每个CPU有自己的运算器,所以在多个CPU中可以同时运行(并行)。

阻塞与非阻塞

阻塞是指调用结果返回之前,当前线程会被挂起,只有在得到结果后才返回。非阻塞指不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。

守护线程

Java中有两种线程,一种是用户线程,另外一种是守护线程。当进程中不存在非守护线程了,守护线程自动销毁。典型的守护线程就是垃圾回收线程。只要当前JVM实例中存在任何一个非守护线程没有结束,守护线程就在工作,只有当最后一个非守护线程结束,守护线程才随着JVM一同结束工作。

线程状态转换

  • 新建(New):创建后尚未启动。

  • 可运行(Runnable):线程对象创建后,在调用它的start()方法,系统为此线程分配CPU资源,使其处于可运行状态,这是一个准备运行的状态。

    • 调用sleep()方法后经过的时间超过了指定的休眠时间。
    • 线程调用的阻塞IO返回,阻塞方法执行完毕。
    • 线程获得了试图同步的监视器
    • 线程正在等待通知,且其他线程发出了通知
    • 处于挂起状态的线程调用了resume恢复方法。
  • 运行(Running):在Runnable状态下的线程获得了CPU时间片,执行程序代码。

  • 阻塞(Blocking):线程因为某种原因放弃了CPU使用权,让出了CPU时间片,暂时停止运行。包括如下五种情况:

    • 线程调用sleep()方法
    • 线程调用了阻塞IO方法,在该方法返回前,被阻塞
    • 线程试图获得同步锁,该锁被其他线程持有(同步阻塞)
    • 线程等待通知(等待阻塞)
    • 程序调用了suspend方法挂起(避免该方法,易死锁)
  • 死亡(Dead):线程run()、main()方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。

线程创建及启动

在Java中,实现多线程编程的方式主要有两种,一种是继承Thread类,一种是实现Runnable接口。

在Java源码中,Thread类实现了Runnable接口,它们之间具有多态关系。使用这两种方式创建的线程在工作时性质是一样的,没有本质区别。

线程对象在初始化完成后,调用 start() 方法启动这个线程。其含义是当前线程(启动子线程的线程)告知JVM,只要线程规划器空闲,立即启动调用 start() 方法的线程。

示例代码参考:juejin.im/post/5c09db…

线程中断

中断可以理解为线程的一个标志位属性,表示一个运行中的线程是否被其他线程进行了中断操作。线程中断是一种用于停止线程的协作机制,线程可以通过这种机制来通知另一个线程,告诉它在合适的或者可能的情况下停止当前工作。

使用interrupt()方法将中断状态设置为ture;使用isInterrupted()判断线程的中断状态;使用Thread.interrupted()判断当前线程的中断状态,并将当前线程的中断状态设置为false。

参考:juejin.im/post/5c1c51…

线程通信

等待/通知机制

参考:juejin.im/post/5c0cff…

等待通知的经典范式:

等待方:1.获取对象锁;2.如果条件不满足,调用对象的 wait() 方法;3.被通知后检查条件,条件满足执行对应的逻辑。

synchronized(对象){
    while(条件不满足){
        对象.wait();
    }
    处理逻辑
}
复制代码

通知方:1.获取对象锁;2.改变条件;3.通知等待该对象的进程

synchronized(对象){
    改变条件
    对象.notifyAll();
}
复制代码

管道流

管道流用于线程之间的数据传输,其媒介为内存。 一个线程发送数据到输出管道,另一个线程从输入管道中读取数据。通过使用管道,实现不同线程间的通信,而无须借助类似临时文件之类的东西。 JDK中提供了四个类:PipedInputStream,PipedOutputStream;PipedReader,PipedWriter。

示例代码: juejin.im/post/5c0e16…

join()方法

在线程A的代码中执行了线程B threadB.join(),含义是线程A等待线程B终止之后才继续进行线程该代码之后的工作。join() 方法还可以传入参数如join(long mills)以及join(long mills,int nanos)的超时方法,表示线程在给定时间内没有终止,将从中返回。

实例代码: juejin.im/post/5c0e1d…

ThreadLocal

ThreadLocal 是存放线程变量的变量,是一个以ThreadLocal对象为key,任意对象为value的存储结构。

public class Profiler {
    private static final ThreadLocal<Long> TIME_TL=new ThreadLocal<Long>(){
        @Override
        protected Long initialValue() {
            return System.currentTimeMillis();
        }
    };
    private static final ThreadLocal<String> STR_TL=new ThreadLocal<String>(){
        @Override
        protected String initialValue() {
            return "hello";
        }
    };

    public static final void begin(){
        TIME_TL.set(System.currentTimeMillis());
    }

    public static final long end(){
        return System.currentTimeMillis()-TIME_TL.get();
    }

    public static void main(String[] args) throws InterruptedException {
        Profiler.begin();
        TimeUnit.SECONDS.sleep(1);
        System.out.println("Cost "+Profiler.end()+" mills");
        System.out.println(STR_TL.get());
    }
}
复制代码

运行结果:

Cost 1001 mills
hello
复制代码

相关文章:

  • 技术部门怎么年终考核最合理?
  • python机器学习实战(二)
  • 生成验证码
  • 【Spring Boot】19.集成消息
  • Visio2010建立ER图并直接导出为SQL语句
  • tomcat架构分析和源码解读
  • 2015博客升级记(五):CentOS 7.1编译安装PHP7
  • Git的一些常用操作
  • 正确配置jstl的maven依赖,jar包冲突的问题终于解决啦
  • Java开发集合定义及案例详解
  • nginx请求转发
  • csv文件的格式
  • tomcat环境变量的配置
  • 人工智能技术会逐渐淘汰掉哪些职业?
  • 多线程编程(四)--线程同步
  • [笔记] php常见简单功能及函数
  • AngularJS指令开发(1)——参数详解
  • C++类中的特殊成员函数
  • crontab执行失败的多种原因
  • Github访问慢解决办法
  • HomeBrew常规使用教程
  • JavaScript服务器推送技术之 WebSocket
  • php的插入排序,通过双层for循环
  • SpringBoot几种定时任务的实现方式
  • yii2中session跨域名的问题
  • 对JS继承的一点思考
  • 服务器之间,相同帐号,实现免密钥登录
  • 爬虫进阶 -- 神级程序员:让你的爬虫就像人类的用户行为!
  • 设计模式 开闭原则
  • 要让cordova项目适配iphoneX + ios11.4,总共要几步?三步
  • 一些基于React、Vue、Node.js、MongoDB技术栈的实践项目
  • 正则与JS中的正则
  • ​【已解决】npm install​卡主不动的情况
  • ​Kaggle X光肺炎检测比赛第二名方案解析 | CVPR 2020 Workshop
  • ​LeetCode解法汇总1410. HTML 实体解析器
  • #Linux杂记--将Python3的源码编译为.so文件方法与Linux环境下的交叉编译方法
  • $.extend({},旧的,新的);合并对象,后面的覆盖前面的
  • (zt)最盛行的警世狂言(爆笑)
  • (编译到47%失败)to be deleted
  • (博弈 sg入门)kiki's game -- hdu -- 2147
  • (翻译)terry crowley: 写给程序员
  • (力扣)循环队列的实现与详解(C语言)
  • (六) ES6 新特性 —— 迭代器(iterator)
  • (免费领源码)python#django#mysql公交线路查询系统85021- 计算机毕业设计项目选题推荐
  • (三)Hyperledger Fabric 1.1安装部署-chaincode测试
  • (三)mysql_MYSQL(三)
  • (学习日记)2024.04.10:UCOSIII第三十八节:事件实验
  • (一一四)第九章编程练习
  • (译)2019年前端性能优化清单 — 下篇
  • (轉貼) UML中文FAQ (OO) (UML)
  • .NET 药厂业务系统 CPU爆高分析
  • .NET的数据绑定
  • @angular/cli项目构建--http(2)
  • @EnableWebMvc介绍和使用详细demo
  • @property括号内属性讲解