Java并发3:线程

本文是根据《Java并发编程的艺术》以及以前的文章 https://juejin.im/post/5c09dbed51882539c60cfd63 和 https://juejin.im/post/5c0cff456fb9a049de6d30d6 共同整理而来。java

进程和线程

进程

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

线程

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

多线程

多线程就是指多个线程同时运行或交替运行。单核CPU是顺序执行,也即交替运行。多核CPU,由于每一个CPU有本身的运算器,因此在多个CPU能够同时运行。多线程

为何要使用多线程

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

相关概念

线程优先级

Java线程中,经过整形变量priority来控制优先级,范围从1-10,建立线程时能够设置,默认为5。在不一样的JVM和操做系统上,线程规划存在差别,有些操做系统甚至会忽略对线程优先级的设定。并发

同步和异步

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

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

并发和并行

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

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

多线程在单核CPU的话是顺序执行,也就是交替运行(并发)。多核CPU的话,由于每一个CPU有本身的运算器,因此在多个CPU中能够同时运行(并行)。post

阻塞与非阻塞

阻塞是指调用结果返回以前,当前线程会被挂起,只有在获得结果后才返回。非阻塞指不能马上获得结果以前,该函数不会阻塞当前线程,而会马上返回。spa

守护线程

Java中有两种线程,一种是用户线程,另一种是守护线程。当进程中不存在非守护线程了,守护线程自动销毁,java虚拟机会退出。典型的守护线程就是垃圾回收线程。只要当前JVM实例中存在任何一个非守护线程没有结束,守护线程就在工做,只有当最后一个非守护线程结束,守护线程才随着JVM一同结束工做。当JVM退出时,守护线程中的finally块不必定会执行,不能依靠finally块中的内容确保执行关闭或者清理资源的逻辑。

线程状态转换

  • 新建(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() 方法的线程。

示例代码参考:https://juejin.im/post/5c09dbed51882539c60cfd63

线程中断

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

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

参考:https://juejin.im/post/5c1c51476fb9a049b221d97c

线程通讯

等待/通知机制

参考:https://juejin.im/post/5c0cff456fb9a049de6d30d6

等待通知的经典范式:

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

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

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

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

管道流

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

示例代码: https://juejin.im/post/5c0e16b16fb9a049df23e62a

join()方法

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

实例代码: https://juejin.im/post/5c0e1de6e51d451dd71e2a74

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
复制代码
相关文章
相关标签/搜索