精华内容
下载资源
问答
  • 写在前面相信很多小伙伴都知道局部变量线程安全的,那你知道为什么局部变量线程安全的吗?前言多个线程同时访问共享变量时,会导致并发问题。那么,如果将变量放在方法内部,是不是还会存在并发问题呢?如果不...

    写在前面

    相信很多小伙伴都知道局部变量是线程安全的,那你知道为什么局部变量是线程安全的吗?

    前言

    多个线程同时访问共享变量时,会导致并发问题。那么,如果将变量放在方法内部,是不是还会存在并发问题呢?如果不存在并发问题,那么为什么不会存在并发问题呢?

    著名的斐波那契数列

    记得上学的时候,我们都会遇到这样一种题目,打印斐波那契数列。斐波那契数列是这样的一个数列:1、1、2、3、5、8、13、21、34…,也就是说第1项和第2项是1,从第3项开始,每一项都等于前2项之和。我们可以使用下面的代码来生成斐波那契数列。

    //生成斐波那契数列

    public int[] fibonacci(int n){

    //存放结果的数组

    int[] result = new int[n];

    //数组的第1项和第2项为1

    result[0] = result[1] = 1;

    //计算第3项到第n项

    for(int i = 2; i 

    result[i] = result[i-2] + result[i-1];

    }

    return result;

    }

    假设此时有很多个线程同时调用fibonacci()方法来生成斐波那契数列,对于方法中的局部变量result,会不会存在线程安全的问题呢?答案是:不会!!

    接下来,我们就深入分析下为什么局部变量不会存在线程安全的问题!

    方法是如何被执行的?

    我们以下面的三行代码为例。

    int x = 5;

    int[] y = fibonacci(x);

    int[] z = y;

    当我们调用fibonacci(x)时,CPU要先找到fibonacci()方法的地址,然后跳转到这个地址去执行代码,执行完毕后,需要返回并找到调用方法的下一条语句的地址,也就是int[] z = y的地址,再跳到这个地址去执行。我们可以将这个过程简化成下图所示。

    07dd7dbbb825d7e063e4fa3ad11c8ab0.png

    这里需要注意的是:CPU会通过堆栈寄存器找到调用方法的参数和返回地址。

    例如,有三个方法A、B、C,调用关系为A调用B,B调用C。在运行时,会构建出相应的调用栈,我们可以用下图简单的表示这个调用栈。

    ba89993d118f9e64f151124952b102d8.png

    每个方法在调用栈里都会有自己独立的栈帧,每个栈帧里都有对应方法需要的参数和返回地址。当调用方法时,会创建新的栈帧,并压入调用栈;当方法返回时,对应的栈帧就会被自动弹出。

    我们可以这样说:栈帧是在调用方法时创建,方法返回时“消亡”。

    局部变量存放在哪里?

    局部变量的作用域在方法内部,当方法执行完,局部变量也就没用了。可以这么说,方法返回时,局部变量也就“消亡”了。此时,我们会联想到调用栈的栈帧。没错,局部变量就是存放在调用栈里的。此时,我们可以将方法的调用栈用下图表示。

    722f0891ec9c212147bf4626ed455678.png

    很多人都知道,局部变量会存放在栈里。如果一个变量需要跨越方法的边界,就必须创建在堆里。

    调用栈与线程

    两个线程就可以同时用不同的参数调用相同的方法。那么问题来了,调用栈和线程之间是什么关系呢?答案是:每个线程都有自己独立的调用栈。我们可以使用下图来简单的表示这种关系。

    aff0eeea3a3a92e22034aa86f02ea9b8.png

    此时,我们在看下文中开头的问题:Java方法内部的局部变量是否存在并发问题?答案是不存在并发问题!因为每个线程都有自己的调用栈,局部变量保存在线程各自的调用栈里,不会共享,自然也就不存在并发问题。

    线程封闭

    方法里的局部变量,因为不会和其他线程共享,所以不会存在并发问题。这种解决问题的技术也叫做线程封闭。官方的解释为:仅在单线程内访问数据。由于不存在共享,所以即使不设置同步,也不会出现并发问题!

    写在最后

    如果觉得文章对你有点帮助,请微信搜索并关注「 冰河技术 」微信公众号,跟冰河学习高并发编程技术。

    最后,附上并发编程需要掌握的核心技能知识图,祝大家在学习并发编程时,少走弯路。

    cdb8e01ae9f38b42f709a1188011c974.png

    展开全文
  • 在工作中,我发现很多同学在设计之初都是直接按照单线程的思路来写程序的,而忽略了本应该重视的并发问题;等上线后的某天,突然发现诡异的Bug,再历经千辛万苦终于定位到问题所在,却发现对于如何解决已经没有了...

    在工作中,我发现很多同学在设计之初都是直接按照单线程的思路来写程序的,而忽略了本应该重视的并发问题;等上线后的某天,突然发现诡异的Bug,再历经千辛万苦终于定位到问题所在,却发现对于如何解决已经没有了思路。

    关于这个问题,我觉得咱们今天很有必要好好聊聊“如何用面向对象思想写好并发程序”这个话题。

    面向对象思想与并发编程有关系吗?本来是没关系的,它们分属两个不同的领域,但是在Java语言里,这两个领域被无情地融合在一起了,好在融合的效果还是不错的:在Java语言里,面向对象思想能够让并发编程变得更简单

    那如何才能用面向对象思想写好并发程序呢?结合我自己的工作经验来看,我觉得你可以从封装共享变量、识别共享变量间的约束条件和制定并发访问策略这三个方面下手。

    一、封装共享变量

    并发程序,我们关注的一个核心问题,不过是解决多线程同时访问共享变量的问题。在《03 | 互斥锁(上):解决原子性问题》中,我们类比过球场门票的管理,现实世界里门票管理的一个核心问题是:所有观众只能通过规定的入口进入,否则检票就形同虚设。在编程世界这个问题也很重要,编程领域里面对于共享变量的访问路径就类似于球场的入口,必须严格控制。好在有了面向对象思想,对共享变量的访问路径可以轻松把控。

    面向对象思想里面有一个很重要的特性是封装,封装的通俗解释就是将属性和实现细节封装在对象内部,外界对象只能通过目标对象提供的公共方法来间接访问这些内部属性,这和门票管理模型匹配度相当的高,球场里的座位就是对象属性,球场入口就是对象的公共方法。我们把共享变量作为对象的属性,那对于共享变量的访问路径就是对象的公共方法,所有入口都要安排检票程序就相当于我们前面提到的并发访问策略。

    利用面向对象思想写并发程序的思路,其实就这么简单:将共享变量作为对象属性封装在内部,对所有公共方法制定并发访问策略。就拿很多统计程序都要用到计数器来说,下面的计数器程序共享变量只有一个,就是value,我们把它作为Counter类的属性,并且将两个公共方法get()和addOne()声明为同步方法,这样Counter类就成为一个线程安全的类了。

    public class Counter {
      private long value;
      synchronized long get(){
        return value;
      }
      synchronized long addOne(){
        return ++value;
      }
    }
    

    当然,实际工作中,很多的场景都不会像计数器这么简单,经常要面临的情况往往是有很多的共享变量,例如,信用卡账户有卡号、姓名、身份证、信用额度、已出账单、未出账单等很多共享变量。这么多的共享变量,如果每一个都考虑它的并发安全问题,那我们就累死了。但其实仔细观察,你会发现,很多共享变量的值是不会变的,例如信用卡账户的卡号、姓名、身份证。对于这些不会发生变化的共享变量,建议你用final关键字来修饰。这样既能避免并发问题,也能很明了地表明你的设计意图,让后面接手你程序的兄弟知道,你已经考虑过这些共享变量的并发安全问题了。

    二、识别共享变量间的约束条件

    识别共享变量间的约束条件非常重要。因为这些约束条件,决定了并发访问策略。例如,库存管理里面有个合理库存的概念,库存量不能太高,也不能太低,它有一个上限和一个下限。关于这些约束条件,我们可以用下面的程序来模拟一下。在类SafeWM中,声明了两个成员变量upper和lower,分别代表库存上限和库存下限,这两个变量用了AtomicLong这个原子类,原子类是线程安全的,所以这两个成员变量的set方法就不需要同步了。

    public class SafeWM {
      // 库存上限
      private final AtomicLong upper =
            new AtomicLong(0);
      // 库存下限
      private final AtomicLong lower =
            new AtomicLong(0);
      // 设置库存上限
      void setUpper(long v){
        upper.set(v);
      }
      // 设置库存下限
      void setLower(long v){
        lower.set(v);
      }
      // 省略其他业务代码
    }
    

    虽说上面的代码是没有问题的,但是忽视了一个约束条件,就是库存下限要小于库存上限,这个约束条件能够直接加到上面的set方法上吗?我们先直接加一下看看效果(如下面代码所示)。我们在setUpper()和setLower()中增加了参数校验,这乍看上去好像是对的,但其实存在并发问题,问题在于存在竞态条件。这里我顺便插一句,其实当你看到代码里出现if语句的时候,就应该立刻意识到可能存在竞态条件。

    我们假设库存的下限和上限分别是(2,10),线程A调用setUpper(5)将上限设置为5,线程B调用setLower(7)将下限设置为7,如果线程A和线程B完全同时执行,你会发现线程A能够通过参数校验,因为这个时候,下限还没有被线程B设置,还是2,而5>2;线程B也能够通过参数校验,因为这个时候,上限还没有被线程A设置,还是10,而7<10。当线程A和线程B都通过参数校验后,就把库存的下限和上限设置成(7, 5)了,显然此时的结果是不符合库存下限要小于库存上限这个约束条件的。

    public class SafeWM {
      // 库存上限
      private final AtomicLong upper =
            new AtomicLong(0);
      // 库存下限
      private final AtomicLong lower =
            new AtomicLong(0);
      // 设置库存上限
      void setUpper(long v){
        // 检查参数合法性
        if (v < lower.get()) {
          throw new IllegalArgumentException();
        }
        upper.set(v);
      }
      // 设置库存下限
      void setLower(long v){
        // 检查参数合法性
        if (v > upper.get()) {
          throw new IllegalArgumentException();
        }
        lower.set(v);
      }
      // 省略其他业务代码
    }
    

    在没有识别出库存下限要小于库存上限这个约束条件之前,我们制定的并发访问策略是利用原子类,但是这个策略,完全不能保证库存下限要小于库存上限这个约束条件。所以说,在设计阶段,我们一定要识别出所有共享变量之间的约束条件,如果约束条件识别不足,很可能导致制定的并发访问策略南辕北辙

    共享变量之间的约束条件,反映在代码里,基本上都会有if语句,所以,一定要特别注意竞态条件。

    三、制定并发访问策略

    制定并发访问策略,是一个非常复杂的事情。应该说整个专栏都是在尝试搞定它。不过从方案上来看,无外乎就是以下“三件事”。

    1. 避免共享:避免共享的技术主要是利于线程本地存储以及为每个任务分配独立的线程。
    2. 不变模式:这个在Java领域应用的很少,但在其他领域却有着广泛的应用,例如Actor模式、CSP模式以及函数式编程的基础都是不变模式。
    3. 管程及其他同步工具:Java领域万能的解决方案是管程,但是对于很多特定场景,使用Java并发包提供的读写锁、并发容器等同步工具会更好。

    接下来在咱们专栏的第二模块我会仔细讲解Java并发工具类以及他们的应用场景,在第三模块我还会讲解并发编程的设计模式,这些都是和制定并发访问策略有关的。

    除了这些方案之外,还有一些宏观的原则需要你了解。这些宏观原则,有助于你写出“健壮”的并发程序。这些原则主要有以下三条。

    1. 优先使用成熟的工具类:Java SDK并发包里提供了丰富的工具类,基本上能满足你日常的需要,建议你熟悉它们,用好它们,而不是自己再“发明轮子”,毕竟并发工具类不是随随便便就能发明成功的。
    2. 迫不得已时才使用低级的同步原语:低级的同步原语主要指的是synchronized、Lock、Semaphore等,这些虽然感觉简单,但实际上并没那么简单,一定要小心使用。
    3. 避免过早优化:安全第一,并发程序首先要保证安全,出现性能瓶颈后再优化。在设计期和开发期,很多人经常会情不自禁地预估性能的瓶颈,并对此实施优化,但残酷的现实却是:性能瓶颈不是你想预估就能预估的。

    总结

    利用面向对象思想编写并发程序,一个关键点就是利用面向对象里的封装特性,由于篇幅原因,这里我只做了简单介绍,详细的你可以借助相关资料定向学习。而对共享变量进行封装,要避免“逸出”,所谓“逸出”简单讲就是共享变量逃逸到对象的外面,比如在《02 | Java内存模型:看Java如何解决可见性和有序性问题》那一篇我们已经讲过构造函数里的this“逸出”。这些都是必须要避免的。

    这是我们专栏并发理论基础的最后一部分内容,这一部分内容主要是让你对并发编程有一个全面的认识,让你了解并发编程里的各种概念,以及它们之间的关系,当然终极目标是让你知道遇到并发问题该怎么思考。这部分的内容还是有点烧脑的,但专栏后面几个模块的内容都是具体的实践部分,相对来说就容易多了。我们一起坚持吧!

    课后思考

    本期示例代码中,类SafeWM不满足库存下限要小于库存上限这个约束条件,那你来试试修改一下,让它能够在并发条件下满足库存下限要小于库存上限这个约束条件。

    欢迎在留言区与我分享你的想法,也欢迎你在留言区记录你的思考过程。感谢阅读,如果你觉得这篇文章对你有帮助的话,也欢迎把它分享给更多的朋友。

    延伸阅读

    关于这部分的内容,如果你觉得还不“过瘾”,这里我再给你推荐一本书吧——《Java并发编程实战》,这本书的第三章《对象的共享》、第四章《对象的组合》全面地介绍了如何构建线程安全的对象,你可以拿来深入地学习。

    展开全文
  • 多个线程同时访问共享变量时,会导致并发问题。那么,如果将变量放在方法内部,是不是还会存在并发问题呢?如果不存在并发问题,那么为什么不会存在并发问题呢? 需要更多Java知识点和面试题的朋友可以点一点下方...

    前言

    多个线程同时访问共享变量时,会导致并发问题。那么,如果将变量放在方法内部,是不是还会存在并发问题呢?如果不存在并发问题,那么为什么不会存在并发问题呢?

    需要更多Java知识点和面试题的朋友可以点一点下方链接免费领取

    链接:1103806531暗号:CSDN

    在这里插入图片描述

    著名的斐波那契数列

    记得上学的时候,我们都会遇到这样一种题目,打印斐波那契数列。斐波那契数列是这样的一个数列:1、1、2、3、5、8、13、21、34…,也就是说第1项和第2项是1,从第3项开始,每一项都等于前2项之和。我们可以使用下面的代码来生成斐波那契数列。

    //生成斐波那契数列
    public int[] fibonacci(int n){
        //存放结果的数组
        int[] result = new int[n];
        //数组的第1项和第2项为1
        result[0] = result[1] = 1;
        //计算第3项到第n项
        for(int i = 2; i < n; i++){
            result[i] = result[i-2] + result[i-1];
        }
        return result;
    }
    
    

    假设此时有很多个线程同时调用fibonacci()方法来生成斐波那契数列,对于方法中的局部变量result,会不会存在线程安全的问题呢?答案是:不会!!

    接下来,我们就深入分析下为什么局部变量不会存在线程安全的问题!

    方法是如何被执行的?

    我们以下面的三行代码为例。

    int x = 5;
    int[] y = fibonacci(x);
    int[] z = y;
    
    

    当我们调用fibonacci(x)时,CPU要先找到fibonacci()方法的地址,然后跳转到这个地址去执行代码,执行完毕后,需要返回并找到调用方法的下一条语句的地址,也就是int[] z = y的地址,再跳到这个地址去执行。我们可以将这个过程简化成下图所示。

    在这里插入图片描述

    这里需要注意的是:CPU会通过堆栈寄存器找到调用方法的参数和返回地址。

    例如,有三个方法A、B、C,调用关系为A调用B,B调用C。在运行时,会构建出相应的调用栈,我们可以用下图简单的表示这个调用栈。

    在这里插入图片描述
    每个方法在调用栈里都会有自己独立的栈帧,每个栈帧里都有对应方法需要的参数和返回地址。当调用方法时,会创建新的栈帧,并压入调用栈;当方法返回时,对应的栈帧就会被自动弹出。

    我们可以这样说:栈帧是在调用方法时创建,方法返回时“消亡”。

    局部变量存放在哪里?

    局部变量的作用域在方法内部,当方法执行完,局部变量也就没用了。可以这么说,方法返回时,局部变量也就“消亡”了。此时,我们会联想到调用栈的栈帧。没错,局部变量就是存放在调用栈里的。此时,我们可以将方法的调用栈用下图表示。

    在这里插入图片描述

    很多人都知道,局部变量会存放在栈里。如果一个变量需要跨越方法的边界,就必须创建在堆里。

    调用栈与线程

    两个线程就可以同时用不同的参数调用相同的方法。那么问题来了,调用栈和线程之间是什么关系呢?答案是:每个线程都有自己独立的调用栈。我们可以使用下图来简单的表示这种关系。

    在这里插入图片描述
    此时,我们在看下文中开头的问题:Java方法内部的局部变量是否存在并发问题?答案是不存在并发问题!因为每个线程都有自己的调用栈,局部变量保存在线程各自的调用栈里,不会共享,自然也就不存在并发问题。

    线程封闭

    方法里的局部变量,因为不会和其他线程共享,所以不会存在并发问题。这种解决问题的技术也叫做线程封闭。官方的解释为:仅在单线程内访问数据。由于不存在共享,所以即使不设置同步,也不会出现并发问题!

    最后

    我这边也整理了一份 架构师全套视频教程和关于java的系统化资料,包括java核心知识点、面试专题和20年最新的互联网真题、电子书等都有。有需要的朋友可以点一点下方链接免费领取!

    链接:1103806531暗号:CSDN

    在这里插入图片描述

    在这里插入图片描述

    展开全文
  • 今天在看深入理解java虚拟机时看到一段话,大意是说JMM模型不会对局部变量及方法参数实施内存一致性协议的约定,方法参数好说,因为是线程栈内部的数据,是线程私有的,但是局部变量如果是对象呢,线程持有的是对象...

    今天在看深入理解java虚拟机时看到一段话,大意是说JMM模型不会对局部变量及方法参数实施内存一致性协议的约定,方法参数好说,因为是线程栈内部的数据,是线程私有的,但是局部变量如果是对象呢,线程持有的是对象的引用,如果多个线程同时对对象修改呢?为此特意做了个实验:

      Thread hello = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Student student = new Student();
                    student.setName("hello");
                    System.out.println("hello:" + student);
                }
            });
    
            Thread world = new Thread(new Runnable() {
                @Override
                public void run() {
                    Student student = new Student();
                    student.setName("world");
                    System.out.println("world:" + student);
                }
            });
    
            hello.start();
            world.start();

    结果输出:

    world:oa.demo.thread.ObjectTest.Student@33c5a8a9
    hello:oa.demo.thread.ObjectTest.Student@5a6b83b0

    可以发现,其实每个线程内部的对象都在堆中创建了一份,并不共用,所以并发不存在问题

    展开全文
  • 看下面两个场景:1、当用在方法内的局部变量时,局部变量属于当前线程级别的变量,其他线程访问不了,所以这时也不存在线程安全不安全的问题了。2、当用在单例对象成员变量的时候呢?这时候多个线程过来访问的就是同...
  • 看下面两个场景:1、当用在方法内的局部变量时,局部变量属于当前线程级别的变量,其他线程访问不了,所以这时也不存在线程安全不安全的问题了。2、当用在单例对象成员变量的时候呢?这时候多个线程过来访问的就是同...
  • 看下面两个场景:1、当用在方法内的局部变量时,局部变量属于当前线程级别的变量,其他线程访问不了,所以这时也不存在线程安全不安全的问题了。2、当用在单例对象成员变量的时候呢?这时候多个线程过来访问的就是同...
  • Java中使用同步关键字synchronized需要注意的问题 彻底理解ThreadLocal ...多个线程共享同一个局部变量,会发生线程安全问题吗?不会 线程安全问题,有哪些解决办法? 解决办法: synchronized --- ...
  • 看下面两个场景:1、当用在方法内的局部变量时,局部变量属于当前线程级别的变量,其他线程访问不了,所以这时也不存在线程安全不安全的问题了。2、当用在单例对象成员变量的时候呢?这时候多个线程过来访问的就是同...
  • Java虚拟机栈是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
  • 1、当用在方法内的局部变量时,局部变量属于当前线程级别的变量,其他线程访问不了,所以这时也不存在线程安全不安全的问题了。 2、当用在单例对象成员变量的时候呢?这时候多个线程过来访问的就是同一个HashMap了,...
  • 推荐阅读:我凭借这份pdf拿下了蚂蚁金服、字节跳动、小米等大厂的offer看下面两个场景:1、当用在方法内的局部变量时,局部变量属于当前线程级别的变量,其他线程访问不了,所以这时也不存在线程安全不安全的问题了...
  • 1、当用在方法内的局部变量时,局部变量属于当前线程级别的变量,其他线程访问不了,所以这时也不存在线程安全不安全的问题了。 2、当用在单例对象成员变量的时候呢?这时候多个线程过来访问的就是同一个HashMap了,...
  • 转载自面试必问-几种...1、当用在方法内的局部变量时,局部变量属于当前线程级别的变量,其他线程访问不了,所以这时也不存在线程安全不安全的问题了。 2、当用在单例对象成员变量的时候呢?这时候多个线程过...
  • JAVA 线程 第三版 英文

    2008-11-05 10:26:51
    Thread局部变量 总结 第六章 高级同步议题 同步术语 J2SE 5.0中加入的同步Class 防止死锁 死锁检测 Lock饥饿 总结 第七章 Thread与Swing Swing Threading的限制 事件派发Thread的处理 使用invokeLater()...
  • Java线程(第三版).pdf[带书签]

    千次下载 热门讨论 2015-02-12 17:32:23
    Thread局部变量 总结 第六章高级同步议题 同步术语 J2SE 5.0中加入的同步Class 防止死锁 死锁检测 Lock饥饿 总结 第七章Thread与Swing Swing Threading的限制 事件派发Thread的处理 使用invokeLater()...
  • java基础个人总结

    2020-12-19 18:43:31
    java基础静态变量,实例变量,局部变量、静态方法线程安全吗,为什么? 静态变量,实例变量,局部变量、静态方法线程安全吗,为什么? 静态变量:我们知道静态变量是存储在方法区的(java8之后叫元空间),是线程间...
  • JavaSE基础:多线程

    2021-03-01 11:26:48
    5、实现线程的两种方式6、线程对象的生命周期二、线程的调度和数据安全1、常用的线程调度模型2、有关线程调度的方法3、数据安全(...线程安全问题8、局部变量的使用9、synchronized三种写法三、守护线程1、概念2、例子
  • Java面试要点

    2020-03-25 03:15:05
    局部变量中是线程安全的,因为方法内局部变量是线程私有的 在成员变量中是线程不安全的,因为是多个线程共享的 sql优化应该从哪些方面考虑? 1.如何合理的创建索引 2.怎么使用索引 3.索引失效 4.合理创建表字段 ...
  • 问题和Synchronized的区别存储在jvm的哪个区域真的只是当前线程可见吗会导致内存泄漏么为什么用Entry数组而不是Entry对象你学习的开源框架哪些用到了ThreadLocalThreadLocal里的对象一定是线程安全的吗笔试题一、...
  • 6_Java 虚拟机栈

    2020-11-01 18:15:26
    栈帧中的局部变量一定是线程安全的吗 ?Java 栈中是否存在垃圾回收 ? 6_Java 虚拟机栈 虚拟机栈概述 有不少 Java 开发人员一提到Java内存结构,就会非常粗粒度地将JVM中的内存区理解为仅有 Java 堆(heap)和 Java...
  • Java虚拟机栈

    2021-01-24 10:06:32
    Java虚拟机栈栈定义 每个线程运行时需要的内存,称为虚拟机栈 每个栈由多个栈帧组成,对应着每次方法调用时所占用的内存 ... 方法内的局部变量是否线程安全? 如果没有逃离线程的作用范围(没有返回变量,也不和外面变
  • Java虚拟机

    2018-01-20 13:22:29
    8.2.1 局部变量表 8.2.2 操作数栈 8.2.3 动态连接 8.2.4 方法返回地址 8.2.5 附加信息 8.3 方法调用 8.3.1 解析 8.3.2 分派 8.3.3 动态类型语言支持 8.4 基于栈的字节码解释执行引擎 8.4.1 解释执行 ...
  • 文章目录线程池的常用的参数和含义线程池的拒绝策略Java线程停止的几种方法和对比ThreadLocal原理ThreadLocal和Synchonized区别局部变量存在线程安全问题吗公平锁和非公平锁ReentrantLock和synchronized区别 ...
  • 疯狂JAVA讲义

    2014-10-17 13:35:01
    5.3.3 局部变量的初始化和内存中的运行机制 130 5.3.4 变量的使用规则 130 5.4 隐藏和封装 132 5.4.1 理解封装 132 5.4.2 使用访问控制符 132 5.4.3 package和import 135 5.4.4 Java的常用包 140 5.5 深入...

空空如也

空空如也

1 2 3
收藏数 47
精华内容 18
关键字:

java局部变量线程安全吗

java 订阅