当前位置:   article > 正文

理解Java并发编程_java 并发编程 真正理解的

java 并发编程 真正理解的

计算机基础

  1. 要想理解Java多线程,一定离不开计算机组成原理和操作系统,因为,java的多线程是JVM虚拟机调用操作系统的线程来实现的
/*
	Thread.start() 方法中调用了原生的start0()方法
*/
public synchronized void start() {
        if (threadStatus != 0)
            throw new IllegalThreadStateException();
        group.add(this);
        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
            }
        }
    }
		
		// 原生方法
    private native void start0();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  1. 首先,先来理解CPU(处理器)的结构

在这里插入图片描述

解析每一个部件的功能,具体的细节不展开

  • ALU(算数逻辑单元):顾名思义是用来计算的,首先要知道一点,所有的信息在计算机看来都是0和1,通过加法运算,补运算, 异或运算等各种运算,完成数据的计算
  • CU (控制单元) : 可以理解为控制ALU运算(比如运算什么,怎么运算,以及运算的次数)
  • Register (寄存器): 用来存储ALU计算的中间数值
  • pc(程序计数器):用来记录某个线程运行到那个指令了
  1. 程序的加载过程以及是如何运行的

在这里插入图片描述

理解:程序,进程,线程

举例:比如我现在点击了WX.exe的程序,计算机会将WX程序加载到内存,这是在内存中的就是一个WX进程,每一个进程都有一个main线程,ALU找到main线程开始执行

程序:就是一段待执行的代码

进程:将程序这段代码以及所需要的数据加载到内存

线程:CPU,调度的基本单位

  1. 再来理解一个常识性的问题:我们买的电脑几核几线程的概念

    我们可以通过cmd的命令查看一下自己的电脑(华为matebookpro)

    以我自己的电脑为例 四核八线程

wmic

在这里插入图片描述

获取cpu的名称 cpu get name

在这里插入图片描述

获取cpu的核心数 cpu get numberofcores

在这里插入图片描述

也可以通过更加直观的方式, 打开任务管理器
在这里插入图片描述

可以很直观的看出有一个插槽,四个内核,八个逻辑处理器

解释

在这里插入图片描述

一个插槽说明只有一个物理cpu

四个内核说明有四个ALU

八个逻辑处理器说明每个ALU可以在Register中切换,可以减少线程的挂起

以前的cpu没有使用多核和超线程技术,也就意味着一个物理cpu对应一个内核对应一个线程,效率是极其低下的

线程的安全性

  1. 什么是线程安全?

    • 当多个线程访问某个类时,不管运行的环境(linux/windows)采用何种的调度方式或者这些线程将如何交替的运行,在主程序中不采用任何额外的同步或协同,这个类都可以表现出正确的行为。
    • 无状态的对象一定是线程安全的(也就是说该对象中的属性被多个线程共享)
  2. 原子性

    要想理解原子性首先要理解几个概念

    竞态条件:

    1. 专业术语:由于不恰当的执行顺序而出现不正确的结果的这种情况

    2. 个人理解:线程由于cpu的调度,多个线程交替执行,要得到正确的结果需要运气成分

      // 比如在单例模式中
      if (instance == null) {
      	instance = new Singletion();
      }
      
      //这个过程是  先检查再执行,这个过程是可以被cpu打断的
      
      先检查再执行就是一个竞态条件
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      //比如  count++
      
      //count++ 不是一步执行完成的
      
      需要先从内存中读取,在做修改,在写回内存
      
      读取 - 修改 - 写入  也是一个竞态条件
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

    复合条件:

    1. 专业术语:一组需要以原子的方式执行(不可分割)的操作
    2. 个人理解:就是不可以被cpu的调度所打断,必须执行完毕

    数据竞争:

    1. 这个很好理解,就是多个线程可以同时去写入或读出一个变量
  3. 加锁机制

    加锁机制可以保证原子性,也就是把一系列的操作变为原子的

    • 内置锁

      内置锁其实很简单,就是改变了,对象里的markword

      			// 使用这个依赖可以查看对象的结构				
      			<dependency>
                  <groupId>org.openjdk.jol</groupId>
                  <artifactId>jol-core</artifactId>
                  <version>0.10</version>
              </dependency>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      public class JustTest {
          private static class T {
              int i;
          }
      
      		// 不加synchronized
          public static void main(String[] args) {
              T t = new T();
      
      			// 打印对象的结构
            System.out.println(ClassLayout.parseInstance(t).toPrintable());
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13

      在这里插入图片描述

      public class JustTest {
          private static class T {
              int i;
          }
      
          public static void main(String[] args) {
              T t = new T();
              synchronized (t) {
                  System.out.println(ClassLayout.parseInstance(t).toPrintable());
              }
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12

      在这里插入图片描述

    • 重入锁

      当一个线程请求一个未被持有锁的对象,JVM会记下所得持有者,并将计数器加一,若果同一线程再次获取锁,则计数器再加一,直到计数器的个数为0时被释放

      如果不可重入,下面的代码可能就会死锁

      // 父类持有锁,不能释放,而子类需要锁,二者僵持
      public class Widget {
          public synchronized void doSomething() {
              
          }
      }
      
      class LoggingWidget extends Widget {
          
          @Override
          public synchronized void doSomething() {
              System.out.println(toString());
              super.doSomething();
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15

      注:不是所有的数据都需要锁来保护,只有被多个线程同时访问的可变数据才需要通过锁来保护。

  4. 活跃性和性能

    活跃性:安全性的含义:永远不要发生糟糕的事情,而活跃性则关注于:某个事情最终会发生,但由于如此,就可能出现没有得到锁而死等的现象,或者无意中造成的无限循环

    性能:性能方面有很多问题例如,服务时间过长,响应不及时等,要在性能和安全编码之间相互的权衡,不要一味为了性能而去修改简单的并发程序

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号