Java并发编程一基础必知

并发基础必知

  • Java并发编程一
    • 进程与线程
    • 并发与并行
    • 为什么使用多线程
    • 线程的生命周期
    • Java内存模型
      • 重排序
      • 内存模型
      • Happens-before
      • as-if-serial语义
      • happens-before与JMM的关系
    • 线程安全
  • 三种线程创建

Java并发编程一

最近一直在学习Java中的多线程编程,面试的时候好多手写多线程题,当时就GG了,趁现在工作稳定下来,把这部分记录一下,分享一下自己的理解,如果有不当的地方还请指出。正式开始前有几个概念需要了解下,其实包括为什么要编写多线程、Java中内存模型等。

进程与线程

在我们运行一个Java程序或者浏览器等这些应用的时候都是一个个进程,来完成里面的一个个功能比如360中扫描垃圾就是一个线程。
进程:一段程序代码执行的过程,是系统进行资源分配的基本单位。
线程:是CPU进行运算和调度的基本单位。

两者区别:一个进程中可以有多个线程,线程之间共享进程,进程与进程之间隔离。
CPU使用时间片轮转来调度线程,当线程时间片用完,就会让出CPU,等到下一次的获取时间片继续运行,在上一篇中讲解程序计数器,它用来记录当前字节码的行号,CPU就会知道你上一次执行到哪里,从而往下继续执行。

并发与并行

并发:一个CPU把时间片分配给各个线程,在一个时间段内的线程代码运行时,其它线程处于阻塞状态(赛跑争取)。
并行:多个CPU上,线程互不抢占CPU资源,同时运行(并排行走)。

为什么使用多线程

因为现在的硬件水平不断提高,处理器的核心数量也越来越多,如果线程只使用一个处理器核心,其他处理器闲置,就太浪费资源了,让多线程在多个处理器核心上跑,会显著减少程序的处理时间,有更快的响应速度,执行效率显著提高。

线程的生命周期

新建:初始化线程,进入新建状态。如:Thread t=new Thread();
就绪:线程获取到CPU时间片,进去就绪状态,等待执行。如:t.start();
运行:就绪的线程获取CPU使用权,进入运行状态。进入运行状态只有一个入口就绪。
阻塞:运行的线程由于某些原因放弃CPU,进入到阻塞状态,等待CPU重新调度执行。
死亡:线程运行完毕或者发生异常。
Java并发编程一基础必知_第1张图片

Java内存模型

重排序

在执行代码时,为了提高性能,编译器和处理器会对代码进行重排序。
编译器重排序在不改变单线程结果前提下,可以重新安排执行语句的顺序。
处理器重排序(指令与内存)如果数据不存在依赖性,可以改成语句对应的机器指令顺序。
从源代码到最终的执行指令:重排序

内存模型

Java中线程之间通信采用的是共享内存模型,Java线程是由Java内存模型(Java Memory Model)JMM控制,JMM决定了一个线程对共享变量的写入何时对另外一个线程可见:线程之间的共享变量存储在主内容,每个线程有一个私有的工作内存,他们会把共享变量备份至自己的私有内存,操作后再放到共享内存,不同线程之间无法直接访问对方工作内存之中的变量。因为直接操作主内存太慢,操作本地内存就很快,类似内存和高速缓存。
Java并发编程一基础必知_第2张图片

Happens-before

在JMM中,如果一个操作执行的结果需要对另外一个操作可见,那么这两个操作就必须存在Happens-before(先行发生)关系,与程序员相关的happens-before有四个规则:

  • 程序顺序规则:一个线程中的每个操作,happens-before与该线程中后续操作。
  • 监视器规则:对一个锁的解锁,happens-before与随后的加锁。
  • volatile变量规则:对一个volatile域的写,happens-before对这个volatile域的读。
  • 传递性规则:如果A happens-before B,B happens-before C,那么A happens-before C。

as-if-serial语义

as-if-serial意思是不管怎么重排序,单线程的情况下程序结果不能改变。编译器和处理器都必须遵守as-if-serial语义,这样我们就不用担心编译器和处理器重排序,也不用担心内存可见性问题。

happens-before与JMM的关系

Java并发编程一基础必知_第3张图片

线程安全

当多个线程访问一个对象时,如果不用考虑这些线程在运行环境下的调度和交替执行,也不需要进行额外的同步,调用这个对象的行为都可以获得正确的结果,那这个对象就是线程安全的。线程安全满足以下三个条件:

  1. 原子性:操作不可中断要么成功要么失败。
  2. 可见性:当一个线程修改完一个变量,另一个线程立即得到最新的值。
  3. 有序性:按照代码顺序执行,禁止重排序。

三种线程创建

Java中提供了三种线程的创建方法,继承Thread并重写run方法,实现Runnable接口重写run方法,实现Callable接口重写call方法。

  1. 继承Thread类

Java并发编程一基础必知_第4张图片
运行结果:
thread

run方法是不会新建线程运行,只是调用当前线程的方法,调用普通方法一样。
start会新建一个线程,等得到CPU的使用权后则会执行所对应的run方法体的代码。

Java并发编程一基础必知_第5张图片
run方法

  1. 实现Runnable接口

Java并发编程一基础必知_第6张图片

  1. 实现Callable方法

Java并发编程一基础必知_第7张图片

 1.Thread是类,需要被继承,重写run方法,操作简单。但是Java是单继承,有一定局限性,所以这种方式基本不用。
 2.Runnable是接口,实现run方法且无返回值,不能抛异常。
 3.Callable也是接口,实现call方法有返回值,可以抛异常,可拿到一个Future对象。
 当然Java中基本不用手动去创建这三种线程,出于资源问题、响应效率和方便管理线程会用线程池来创建线程。

你可能感兴趣的:(Java,并发)