Java线程基础回顾及内存模型,看你还记得多少?

Java基础回顾 接口不是类,而是另外一种引用数据类型接口实现String对象是不可变的, 一旦创建,值不能修改。常量池: 和其他对象一样,位于堆内存中。线程池7个参数corePoolSize 核心线程数maximumPoolSize 最大线程数keepAliveTime 空闲线程存活时间unit 时间单位workQueue工作队列threadFactory线程工厂handler 拒绝策略线程池执行流程。 阅读详情

点击上方“Github爱好者社区”,选择星标

回复“资料”,获取小编整理的一份资料

本文作者:啦啦哇哈哈

原文链接:https://www.jianshu.com/p/0ecca8ab9cb4

线程创建的两种方式

继承Thread类

class MyThread extends Thread{
    ......
    @Override
    public void run(){
        ......
    }
}

MyThread mt = new MyThread();  //创建线程
mt.start();                    //启动线程

实现Runnable接口

class MyThread implements Runnable{
    ......
    @Override
    public void run(){
        ......
    } 
}
MyThread mt = new MyThread();    //创建Runnable对象
Thread td = new Thread(mt);      //创建线程
td.start();                      //启动线程

两种方式的区别:
1.继承Thread类受限于JAVA单继承的特性,而实现Runnable接口则没有这种限制;
2.Runnable的代码可以被多个Thread共享,适用于多个线程处理同一资源(同一个Runnable对象)的情形。

线程的生命周期

创建

  • new一个线程对象。

就绪

  • 创建线程之后,调用了start()方法,此时线程进入就绪队列,等待CPU调度。或者阻塞状态的线程被唤醒。

运行

  • 获取到了CPU资源,执行run()方法。

终止

  • 线程的run()方法执行完毕,或者线程调用了相关的终止方法。

阻塞

  • 运行状态下,由于某种原因让出CPU资源,暂停自己的执行,就进入了阻塞状态,如调用了sleep()方法。

守护线程

JAVA线程有两类

  • 用户线程
    运行在前台,执行具体的任务,程序的主线程、连接网络的子线程等等都是用户线程

  • 守护线程
    运行在后台,为其他前台线程服务,一旦所有用户线程都结束运行,守护线程会随JVM一起结束工作。数据库连接池中的监测线程,JVM虚拟机启动后的检测线程还有最常见的垃圾回收线程等等都是守护线程。

  • 设置守护线程
    通过调用Thread类的setDaemon(true)方法来设置当前线程为守护线程。

注意事项

  • setDaemon(true)必须在start()方法之前调用,否则会抛出异常。

  • 守护线程中产生的新线程也是守护线程。

  • 不是所有的任务都可以分配给守护线程来执行,比如读写操作或计算逻辑。

  • 前面提到用户线程结束之后,守护线程就没有守护对象了,会随JVM一起结束工作,所以一旦在守护线程里面读写操作,那如果用户线程都结束了,可能会读写异常。

下面就是一个主线程结束,打断了守护线程的写操作的实例,我们本来是想写到word1到word999的,主线程scanner读取键盘输入,阻塞等待,一旦键盘输入完成,即scanner结束阻塞解除之后,主线程运行下去就结束了,守护线程即便没有写到999,也还是结束了。

class DaemonThread implements Runnable{

    @Override
    public void run() {
        // TODO Auto-generated method stub
        File file = new File("D:" + File.separator+"word.txt");
        try {
            writeFile(file);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }


    private void writeFile(File file) throws Exception {
        OutputStream oStream =new FileOutputStream(file);
        int i = 1;
        while(i < 1000) {
            Thread.sleep(1000);
            oStream.write(("\r\nword" + i).getBytes());
            System.out.println(Thread.currentThread().getName() + ": word" + i++);
        }
        oStream.close();
    }
}
public class ThreadTest {
    public static void main(String[] args) {
        System.out.println("进入主线程" + Thread.currentThread().getName());
        Thread thread = new Thread(new DaemonThread());
        thread.setDaemon(true);
        thread.start();

        Scanner scanner = new Scanner(System.in);
        scanner.nextLine();
        System.out.println("退出主线程");

    }
}

在word6时候,控制台输入结束:

文件内容是这样的:

可见性

基本概念

  • 可见性:一个线程对共享变量值的修改,能够及时地被其他线程看到。

  • 共享变量:如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量。

JAVA内存模型(JMM)

Java Memory Model描述了Java程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取出变量这样的底层细节。

JMM

所有共享变量都存储在主内存中

每个线程都有自己独立的工作内存,里面保存该线程使用到的变量副本(是主内存中该变量的一份拷贝)

工作内存和主内存交互的八种原子操作
关于一个变量如何从主内存中拷贝到工作内存、如何从工作内存同步回主内存之类的实现细节,Java内存模型定义了以下八种操作来完成,JVM实现时候必须保证下面提及的每一种操作都是原子的、不可分的。

①用于主内存变量:

锁定(lock):把变量标识为一条线程独占的状态。
解锁(unlock):把处于锁定状态的变量释放出来。
读取(read):把变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用。
载入(load):把read操作从主内存中得到的变量值放入工作内存的变量副本中。

②用于工作内存变量:

使用(use):把工作内存中一个变量的值传递给执行引擎。
赋值(assign):把从执行引擎接收到的值赋给工作内存的变量。
存储(store):把工作内存中变量的值传送到主内存中,以便随后的write操作使用。
写入(write):把store操作从工作内存中得到的变量的值放入主内存的变量中。

确保并发操作安全的原则:

1.顺序但不连续:必然是先read后load以及先store后write,有先后顺序的要求,但是并没有要求连续执行,比如read和load,store和write之间是要插入其他指令的,比如read a,read b,load a,load b就是合理的;
2.读了一定载,存了一定写,读或者存之后不允许拒绝载入和写操作,即read\load,store\write不允许他们之一单独出现;
3.线程不能没有assign,就同步回主内存,想同步回去总归要做点不一样的事情吧hhh
4.新的变量只能在主内存“诞生”,不允许在工作内存中使用未初始化的变量,即use和store这种涉及工作内存变量的操作,都要求这个变量是先执行过assign和load操作的(初始化操作)
5.一个变量只允许一个线程lock操作,但是可以同一个线程lock套Lock,如果多次Lock,自然也需要多次unlock才能解锁变量
6.加锁之前先重新读取值,Lock操作会清空工作内存中此变量的值,use之前需要重新load或者assign进行初始化
7.不能解空锁,不能解别人的锁unlock不能解锁没有lock的变量,也不能在一个线程unlock另一个线程中lock的变量
8.解锁之前先同步,unlock前,必须要先store、write,把变量同步回主内存。
我们下面所讲的所有内容,都可以在这些规则中找到对应的点。
以上8种内存操作以及8种规则限定,再加上其他volatile的一些特殊规定,完全确定了JAVA中哪些内存访问操作在并发下是安全的,由于定义相当严谨但又十分繁琐,很麻烦,后面我们会介绍一个先行发生原则来确定一个访问在并发环境下是否安全。

两条规定:

  • 1.线程不能直接与主内存交互。
    线程对共享变量的所有操作必须在自己的工作内存中进行,不能直接从主内存读写。

  • 2.各线程工作内存之间相互独立。
    不同线程之间无法直接访问其他线程工作内存中的变量,线程间变量

共享变量可见性实现的原理:
以线程1对共享变量的修改要被线程2及时看到为例,要经过以下步骤:

  • 1.把工作内存1中更新过的共享变量刷新到主内存中;

  • 2.从主内存中将刷新过的共享变量读取工作内存2中。

那么要实现可见性,必须保证:

  • 1.线程修改后的共享变量值及时从工作内存刷新到主内存(store and write);

  • 2.其他线程能及时把共享变量新值从主内存更新到自己的工作内存(load and read)。

可见性实现方式:
语言层面:

  • synchronized

  • volatile

先了解两个小的基本概念之后我们再来介绍synchronized和volatile。
指令重排序
代码书写的顺序与实际执行的顺序不同,是编译器或者处理器为了提高程序性能而做的优化。原因是重排序之后的指令可能更加符合CPU的执行特点,最大限度发挥CPU性能。
int num1 = 1;
int num2 = 2;

可能实际的执行顺序是相反的:
int num2 = 2;
int num1 = 1;

as-if-serial
无论如何重排序,程序执行的结果应该与代码顺序执行的结果一致。
一个例子:
int num1 = 1;
int num2 = 2;
int sum = num1 + num2;

在单线程中前两句随便重排序(数据依赖关系不允许重排序),但是第三句绝对是在前两句之后,这样不会影响sum的最终结果是3。指令重排序不会影响单线程中变量可见性。
而多线程中,指令重排序很有可能导致变量可见性问题。后面详细叙述。

下面详细叙述synchronized和volatile。
synchronized
synchronized实现互斥锁,能够实现原子性和可见性。

实现可见性原理

JMM关于synchronized的两条规定:

线程解锁unlock前(退出sychronized代码块时候),必须把共享变量的最新值刷新到主内存中store and write

线程加锁lock时(进入sychronized代码块时候),将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存重新读取最新的值load and read(加锁和解锁需要同一把锁)

这两条规定也就保证了synchronized能够实现共享变量的可见性。

过程
1.获得互斥锁
2.清空工作内存
3.从主内存拷贝变量的最新副本到工作内存
4.执行代码
5.更改后的共享变量值刷新到主内存
6.释放互斥锁
synchronized可见性分析代码示例
下面是一个线程不安全的代码:

public class Test {
    private boolean ready = false;
    private int result = 0;
    private int number = 1;

    public void write() {
        ready = true;//1.1
        number = 2;//1.2
    }

    public void read() {
        if (ready) {
            result = number * 3;//2.1
        }
        System.out.println("result的值为:" + result);//2.2
    }

    private class ReadWriteThread extends Thread{
        private boolean flag;

        public ReadWriteThread(boolean flag) {
            this.flag = flag;
        }

        @Override
        public void run() {
            if (flag) {//构造方法传入true执行写操作
                write();
            }else {//构造方法传入false执行读操作
                read();
            }
        }
    }

    public static void main(String[] args) {
        Test fuck = new Test();

        fuck.new ReadWriteThread(true).start();//启动写线程
        fuck.new ReadWriteThread(false).start();//启动读线程
    }
}

以上程序的逻辑很简单,就是一个Test类里面,有个result和number,有write和read方法。它内部有个线程类,通过传给线程类的构造器的boolean值来控制这个线程是读线程还是写线程。
然后在main里面分别启动一个读和写线程,运行后结果的输出可能有很多种情形。
result的值可能为:0,6,3
导致这种情况(共享变量不可见)出现的原因:

线程的交叉执行
比如最朴素的情况,先写后读,则输出结果6.写线程先启动1.1执行完ready=true,number此时是1,然后读线程得到CPU,进入if语句,最终打印result是3,而如果读线程直接执行完毕,result就是0。

重排序结合线程交叉执行
比如写线程先启动,重排序先执行1.2,然后读线程就执行了,这时候即使number值变了,result的结果也是0。
共享变量更新后的值没有在工作内存与主内存间及时更新

而安全的代码则是在write和read方法都加上synchronized关键字。

  public synchronized void write() {
        ready = true;//1.1
        number = 2;//1.2
  }
  public synchronized void read() {
        if (ready) {
            result = number * 3;//2.1
        }
        System.out.println("result的值为:" + result);//2.2
  }

加了一把锁,锁内的代码在一段时间内只能有一个线程可以执行,只有当前线程释放锁,其他线程才能进入这块代码执行。

synchronized解决可见性的解决方案:
  • 原子性
    避免了线程交叉执行锁内代码,同样由于保证锁内只有一个线程执行,那么重排序对单个线程来讲as-if-serial,始终结果是不变的,也不存在由于指令重排序导致的不可见问题了。

  • 可见性
    上面提到了synchronized实现可见性的原理,在加锁和释放锁时候都是要刷内存的。也就保证了共享变量能够及时更新。不加synchronized也能更新,但是并不能保证是及时更新,那么高并发情况下就可能出现意外。

加了synchronized之后(这里是对象锁),就只有两种结果,0或者6了~
在写读之间加个sleep,让主线程休眠上1000ms,那么基本可以保证先写后读,最终结果只有6。
volatile
synchronized保证原子性和可见性,volatile保证了可见性。

volatile实现内存可见性的原理:

从编译器和处理器角度来讲,通过加入内存屏障和禁止重排序优化实现

对volatile变量执行write操作时候,处理器会在写操作前加入一条store屏障指令,会把工作内存中共享变量副本的值强制刷新到主内存共享变量中去,以便进行后续的write,主内存中就是写的最新的值。还能防止处理器,把volatile前面的变量重排序到volatile写操作之后。
对volatile变量执行read操作时候,处理器会在读操作前加入一条load屏障指令,会把主内存中共享变量的值放入工作内存的共享变量副本中去,以便进行后续的read,工作内存中就是读的最新的值。还能防止重排序。

volatile没有lock,不能实现原子性。最简单的例子就是count++,这个实在不想说了…算了还是写个代码看一下,这段代码是保证了race的可见性的,我们预期的race的结果应该是200000:

public class Test {
    public static volatile int race = 0;
    public static void increase() {
        race ++;
    }

    public static void main(String[] args) {
        Thread[] threads = new Thread[20];
        for(int i = 0; i < 20; i++) {
            threads[i] = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int j = 0; j < 10000; j++) {
                        Test.increase();
                    }
                }
            });
            threads[i].start();
        }

        while(Thread.activeCount() > 1) {
            Thread.yield();
        }

        System.out.println(race);
    }
}

不过最终的运行结果小于200000,就是由于volatile并不能保证原子性,race++的底层机器指令是分几步完成的,不同的线程就会进行交叉操作。

自增操作原子性的解决方案

使用synchronized关键字
使用ReentrantLock(java.until.concurrent.locks包下)
使用AtomicInteger(vava.util.concurrent.atomic包下)

使用synchronized示例:

public synchronized static void increase() {
    race ++;
}

使用ReentrantLock示例:
首先需要一个ReentrantLock对象,调用这个对象的lock方法,就加上了锁,这里推荐用try…finally写法,finally中调用unlock释放锁,因为锁中的操作可能抛出异常,当然这里race++显然没有异常,我们只是在这里演示一下推荐写法。

    public static ReentrantLock lock = new ReentrantLock();

    public static void increase() {
        lock.lock();
        try {
            race ++;
        }finally {
            lock.unlock();
        }

    }

volatile使用场合
  • ①对变量的写入操作不依赖当前值或者能够确保只有单一线程修改变量的值

不满足:number ++、 count = count *5等等
满足:boolean变量等

  • ②该变量没有包含在具有其它变量的不变式中。

不满足:程序中有两个volatile变量low和up,还有个不变式low<up

不满足上面两种情形的还是要通过加锁来保证原子性。第一种情形不再讨论了,第二个其实没有看懂。。网上搜了个例子。

public class A {
   private volatile int low=0;

   private volatile int up=100;

   public int getLow() { return low; }
   public int getUp() { return up; }

   public void setLow(int value) { 
       if (value > upper) 
            system.out.print(".......");
       low = value;
   }

   public void setUp(int value) { 
       if (value < low) 
           system.out.print(".......");

       up = value;
   }
}

这个例子里面是没有a++之类的东西的,这里面有两个VOLATILE变量,它包含了一个不变式就是LOWupper) 和   if (value < low) )初始值是0和100,如果有两个线程同时操作set函数,一比如A线程操作setlow(4),B线程setup(3),最后结果是4和3。所以这里我们是需要加锁的。volatile并没有卵用。

volatile和synchronized的比较

补充两点:

  • ①即使没有保证可见性的操作,如加锁或者volatile,很多时候共享变量依然能够得到主内存和工作内存之间的及时更新,出现不可见问题一般都是高并发情况下。

  • ②之前的那八个操作都必须是原子操作,而对64位的数据类型(long和double),模型中特别定义了宽松的规定,允许虚拟机将没有被volatile修饰的64位数据的读写划分为两次32位的操作分开进行,这叫long和double的非原子性协定(Noatomic Treatment of double and long Variables)这样就有可能导致线程交叉操作,出现某些线程修改“半个变量”的情形。但现在这样实现的JVM还是很罕见的。

好啦,今天的分享就到这儿啦,我们下次见啦~



推荐阅读:
Github上这几个沙雕项目,够我玩三天!
百度副总裁被抓!
这10道大厂Java面试题,我敢打赌90%的人都不会!!!
关注「Github爱好者社区」加星标,每天带你逛Github好玩的项目
spark3.x 写入hudi报错 【代码】spark3.x 写入hudi报错。 阅读详情

相关推荐

从零开始学华为设备:手把手教你用ENSP搭建OSPF实验环境(附拓扑文件)

本文详细介绍了如何使用华为ENSP模拟器从零开始搭建OSPF多区域网络实验环境,包括拓扑规划、设备配置、OSPF参数优化及故障排查技巧。通过实战案例和配置示例,帮助网络工程师快速掌握华为设备的OSPF协议配置,提升企业网络环境中的实战能力。

weixin_29305313的博客 287

volatile的适用场合

1对变量的写入操作不依赖其当前值 不满足:number++/count = count*5等 满足Boolean变量、记录温度变化的变量等 2.该变量没有包含在具有其他变量的不变式中 不满足:不变式low<up 转载于:https://www.cnblogs.com/CCCrunner/p/6444550.html...

SCC19760109的博客 431

Pytorch transformers tokenizer 分词器词汇表添加新的词语和embedding

NLP添加新词汇

7325

一招修复内存不能read_Java线程基础回顾及内存模型,看你还记得多少?

点击上方“何俊林”,马上关注,每天早上8:50准时推送真爱,请置顶或星标本文作者:啦啦哇哈哈,原文链接:https://www.jianshu.com/p/0ecca8ab9cb4线程创建的两种方式继承Thread类classMyThreadextendsThread{......@Overridepublicvoidrun(){......}}...

weixin_39611308的博客 99

Android车载应用开发:Kotlin与Automotive OS深度实践

Android Automotive OS(AAOS)是Google专为车辆设计的操作系统,直接运行于车机硬件,提供完整的车载信息娱乐系统(IVI)。与Android Auto不同,AAOS允许开发者深度集成车辆硬件功能,如空调控制和传感器数据读取,同时遵循严格的驾驶安全规范。Kotlin作为开发语言,具备空安全设计、扩展函数和协程支持等优势,简化了开发流程。开发环境配置包括Android Studio、AAOS模拟器和Gradle配置,核心组件如CarAppService、Screen与模板系统,以及车辆

时小雨的专栏 3445

【HarmonyOS 5】鸿蒙APP使用【团结引擎Unity】开发的案例教程

以下是基于团结引擎开发鸿蒙Unity应用的详细案例教程,整合环境配置、工程适配、跨语言通信等核心环节。

2201_75709314的博客 2485

ubuntu卡在载入初始化内存盘_Java线程基础回顾及内存模型,看你还记得多少?...

线程创建的两种方式继承Thread类class MyThread extends Thread{ ...... @Override public void run(){ ...... } } MyThread mt = new MyThread(); //创建线程 mt.start(); //启动线程实现Ru...

weixin_39602560的博客 482

WIFI基础入门--802.11g--增强速率物理层ERP--15

WIFI基础入门--802.11--增强速率物理层--191.802.11g的组件保护机制 1.802.11g的组件 802.11g其实是将好几种物理层规范合而为一。虽然统称为增强速率物理层(Extended Rate PHY简称ERP),但是实际上ERP有好几种不同的方式: ERP-DSSS与ERP-CCK 这两种模式向下兼容于原始直接序列规范(1Mbps与2Mbps),以及所提到的802.11...

香蕉一号 4619

线程的基础回顾

1.死锁/活锁(线程没有出现阻塞,一直处于等待的状态) 死锁:一组互相竞争的资源的线程之间互相等待,导致永久阻塞现象。 活锁:线程没有出现阻塞,一直处于等待的状态. 2.死锁满足的条件 这四个条件同时满足,就会产生死锁。 互斥,共享资源X和Y只能被一个线程占用。 占有且等待,线程T1已经取得共享资源X,在等待共享资源Y的时候,不释放共享资源X 不可抢占,其他线程不能强行抢占线程T1占有的资源。 循环等待,线程T1等待线程T2占有的资源,线程T2等待线程T1占有的资源,就是循环等待。、 死

weixin_48013460的博客 1117

如何用DETR(detection transformer)训练自己的数据集

DETR(detection transformer)简介 DETR是Facebook AI的研究者提出的Transformer的视觉版本,是CNN和transformer的融合,实现了端到端的预测,主要用于目标检测和全景分割。 DETR的Github地址:link

weixin_50233398的博客 3万+

ROS2中间件选型指南:FastDDS与CycloneDDS零拷贝机制对比与实战

在机器人开发中,通信中间件是连接分布式节点的核心纽带,其性能直接影响系统的实时性与可靠性。DDS作为ROS2默认的通信标准,通过发布订阅模型和QoS策略,为高频大带宽场景提供了灵活的数据分发能力。并行计算与高性能传输的需求,推动了零拷贝技术的演进——从传统序列化到共享内存乃至指针传递,旨在减少数据复制开销、降低延迟。在激光雷达点云、相机图像等场景下,中间件的选型尤为关键。FastDDS凭借与ROS2的深度集成和丰富文档成为入门首选,而CycloneDDS在低延迟和资源占用上表现更优,借助iceoryx实现了

weixin_28726391的博客 262

Java基础知识回顾

关于JavaSE的一些比较基础的内容回顾,也是开始准备秋招和实习了qwq

backordinary的博客 1112

TCN(Temporal Convolutional Networks)时序卷积网络详解及代码

TCN(Temporal Convolutional Networks)时序卷积网络详解及代码

zhulewen的博客 7884

java基础回顾

Integer、Short、Byte、Character、Long 都有一个常量池,常量池的范围是-128~127之间。如果此方法定义的包装类的值在这个范围内,则会直接返回内部缓存池中已经存在的对象的引用(new的方式jdk9后就淘汰了)

b810411的博客 851

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

贝叶斯统计 [韦来生编]

贝叶斯(Bayes)统计是近几十年来迅速发展起来的数理统计的一个重要分支。贝叶斯方法与经典统计方法的主要不同之处是进行统计推断时除了利用样本信息外,还要利用参数的先验信息,因此可以提高统计推断或统计决策的效果。它在经济、金融、生物、医学、自然科学和社会科学等许多领域具有广泛的应用。贝叶斯方法的研究已渗透到了统计学的几乎所有领域。作者在给中国科学技术大学概率论与数理统计专业本科生开设的“数理统计”课程中有一章专门讲授“贝叶斯方法和统计决策理论”,近十年来也给中国科学技术大学概率统计专业研究生开设过几次“贝叶斯分析”课。本书是在对过去讲稿的内容作了适当的增减和调整的基础上完成的。  本书共分六章。第1章是绪论,介绍了贝叶斯统计的若干基本概念,同时对必要的数理统计的基础知识有重点地作了回顾。第二章介绍了确定先验分布的若干可供选择的方法。第三章介绍了几类常见统计模型参数的后验分布的主要结果和计算方法。第四章和第五章分别介绍了“贝叶斯统计推断”和“贝叶斯统计决策”的内容。第六章介绍了贝叶斯统计计算的若干方法,包括蒙特卡洛方法和马尔可夫链蒙特卡洛(MCMC)方法以及相关统计软件的简介。本书的每一章附右大量的例题和习题,书末附有常用的几个表格和部分习题解答供读者查用。  大约可在54学时内讲授本书的第1章至第六章的主要内容,教师可根据学时需要选讲本书第1章至第六章的部分内容。书中标“*”号的章节可略去不讲,留给读者作为阅读材料。第六章例题中的R代码和部分例题的数据文件等可从作者个人主页下载。  作者在编写本书的过程中参考了J.O.Berger教授的《统计决策论及贝叶斯分析》和茆诗松教授编写的《贝叶斯统计》等书中的一些内容,以及《贝叶斯分析》一书中由张伟平博士编写的有关贝叶斯统计计算的一些内容。在此表示衷心感谢。  本书编写准备过程中,中国科学技术大学统计与金融系的几个研究生帮助完成了本书部分中文的录入和排版,作者对他们的辛勤工作表示真诚的感谢。高等教育出版社为本书的出版给予了大力支持。在此一并致谢。

Java内存模型相关原则详解

在《Java内存模型(JMM)详解》一文中我们已经讲到了Java内存模型的基本结构以及相关操作和规则。而Java内存模型又是围绕着在并发过程中如何处理原子性、可见性以及有序性这三个特征来构建的。本篇文章就带大家了解一下相关概念、原则等内容。 原子性 原子性即一个操作或一系列是不可中断的。即使是在多个线程的情况下,操作一旦开始,就不会被其他线程干扰。 比如,对于一个静态变量int x两条线程同时对其...

程序新视界 2547

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

java内存模型基础

java内存模型基础油

qq_41820066的博客 163

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Java 内存模型与线程

硬件的效率与一致性 计算机的存储设备与处理器的运算速度有几个数量级的差距,所以现代计算机系统都不得不加入一层读写速度尽可能接近处理器运算速度的高速缓存(Cache)来作为内存与处理器之间的缓冲:将运算需要使用到的数据复制到缓存中,让运算能快速进行,当运算结束后再从缓存同步回内存之中,这样处理器就无须等待缓慢的内存读写了。 缓存一致性 对于增加缓存的设计,将会引入缓存一致性的问题,每个处理...

Yangyan200707的博客 144

Java内存模型的回顾

本篇文章主要讲述了Java内存模型中的程序计数器、虚拟机栈、本地方法栈、元空间与堆,以及堆中的常量池。前面通过javap反编译class文件得到int add(int a, int b)函数的栈帧,主要分析了栈帧中JVM指令对应的局部变量表、操作数栈、程序计数器的状态变化。以及JDK7以后出现了替代永久代的元数据区,并分析了元数据区替换了永久代有哪些好处,主要分析了给字符串...

一枚野生程序员 —— Tim 576

图解JVM内存模型和JVM线程模型

将上文的知识点进行汇总后,我们可以得到一张新图/JVM线程模型/一个Java线程的实现方式可以有三种。

w20001118的博客 653

java基础-15 内存模型

内存模型 java 内存模型 很多人将【java 内存结构】与【java 内存模型】傻傻分不清,【java 内存模型】是 Java Memory Model(JMM)的意思。 Java平台自动集成了线程以及多处理器技术,这种集成程度比Java以前诞生的计算机语言要厉害很多,该语言针对多种异构平台的平台独立性而使用的多线程技术支持也是具有开拓性的一面,有时候在开发Java同步和线程安全要求很严...

QYmufeng的博客 353
上一篇: 卧槽!还有这种事!马斯克的SpaceX-API 竟然开源了!登顶GitHub热榜!
下一篇: 终于明白 Java 为什么要加 final 关键字了!
公众号:【GitHub爱好者社区】
博客等级 码龄6年 357粉丝 48原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值