精华内容
下载资源
问答
  • 网上有很多关于ThreadLocal解决了什么问题的资料,但是很多都是不正确的。 不正确的理解 ThreadLocal为解决多线程程序的并发问题提供了一种新的思路 ThreadLocal的目的是为了解决多线程访问资源时的共享问题 ...

    一、ThreadLocal解决了什么问题

    网上有很多关于ThreadLocal解决了什么问题的资料,但是很多都是不正确的。

    不正确的理解

    • ThreadLocal为解决多线程程序的并发问题提供了一种新的思路
    • ThreadLocal的目的是为了解决多线程访问资源时的共享问题

    正确的理解

    看看JDK中的源码是怎么写的:

    【原文】This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its {@code get} or {@code set} method) has its own, independently initialized copy of the variable. {@code ThreadLocal} instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).
    【翻译】ThreadLocal类提了供线程本地变量。它与普通变量的区别在于,每个使用该变量的线程都会初始化一个完全独立的副本。ThreadLocal 变量通常被private static修饰,用于关联线程上下文。

    ThreadLoal变量,它的基本原理是,同一个 ThreadLocal 所包含的对象(对ThreadLocal<String>而言即为 String 类型变量),在不同的 Thread 中有不同的副本。
    因为每个 Thread 内有自己的实例副本,且该副本只能由当前 Thread 使用。那就不存在多线程间共享资源的问题,既无共享,何来同步问题,又何来解决同步问题一说?

    通过上面的分析,可以一句话总结ThreadLocal解决的问题:ThreadLocal提供了线程本地变量,每个线程都有一个该变量的副本,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度。

    二、ThreadLocal应用场景

    基于ThreadLocal解决的问题,我们可以将ThreadLocal应用到很多场景中。

    • 场景一:当一个变量需要在线程间隔离而在方法或类间共享时,可以使用ThreadLocal。
    • 场景二:……

    三、ThreadLocal用法

    public class ThreadLocalDemo {
    
        public static void main(String[] args) {
            new Thread(new InnerClass(),"Thread-1").start();
            new Thread(new InnerClass(),"Thread-2").start();
            new Thread(new InnerClass(),"Thread-3").start();
        }
    
        private static class InnerClass implements Runnable{
    
            @Override
            public void run() {
                Counter.count.set(Counter.count.get()+1);
                System.out.println(Thread.currentThread().getName()+ 
                ",count-hashcode="+Counter.count.hashCode()+
                ",count-value="+Counter.count.get());
            }
        }
    
        private static class Counter{
    
            public static ThreadLocal<Integer> count = new ThreadLocal<Integer>(){
                @Override
                protected Integer initialValue() {
                    return 0;
                }
            };
        }
    }
    
    //执行结果
    Thread-2,count-hashcode=742865302,count-value=1
    Thread-3,count-hashcode=742865302,count-value=1
    Thread-1,count-hashcode=742865302,count-value=1

    从上面的输出可以看出,每个线程的count对象是不一样。

    四、ThreadLocal原理

    方案一:ThreadLocal维护线程与实例之间的映射

      既然每个访问 ThreadLocal 变量的线程都有自己的一个“本地”实例副本。一个可能的方案就是 ThreadLocal 维护一个 Map,键是 Thread,值是它在该 Thread 内的实例。线程通过该 ThreadLocal 的 get() 方案获取实例时,只需要以线程为键,从 Map 中找出对应的实例即可。该方案如下图所示
    这里写图片描述

      该方案可满足上文提到的每个线程内一个独立备份的要求。每个新线程访问该 ThreadLocal 时,需要向 Map 中添加一个映射,而每个线程结束时,应该清除该映射。这里就有两个问题:

    • 增加线程与减少线程均需要写 Map,故需保证该 Map线程安全。虽然从ConcurrentHashMap的演进看Java多线程核心技术一文介绍了几种实现线程安全 Map的方式,但它或多或少都需要锁来保证线程的安全性
    • 线程结束时,需要保证它所访问的所有 ThreadLocal 中对应的映射均删除,否则可能会引起内存泄漏(后文会介绍避免内存泄漏的方法)

    其中锁的问题,是 JDK 未采用该方案的一个原因。

    方案二:Thread维护ThreadLocal与实例的映射

      上述方案中,出现锁的问题,原因在于多线程访问同一个 Map。如果该 Map 由 Thread 维护,从而使得每个 Thread 只访问自己的 Map,那就不存在多线程写的问题,也就不需要锁。该方案如下图所示。
    这里写图片描述
      该方案虽然没有锁的问题,但是由于每个线程访问某 ThreadLocal 变量后,都会在自己的 Map 内维护该 ThreadLocal 变量与具体实例的映射,如果不删除这些引用(映射),则这些 ThreadLocal 不能被回收,可能会造成内存泄漏。后文会介绍 JDK 如何解决该问题。

    下面从源码的角度来看看具体实现。

    set方法:

    public void set(T value) {
        Thread t = Thread.currentThread();
        //获取当前线程维护的map。
        //Thread内部维护了一个ThreadLocal.ThreadLocalMap类型的变量,这个变量存储的就是线程本地变量
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            //创建map
            createMap(t, value);
    }
    
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    
    ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
        table = new Entry[INITIAL_CAPACITY];
        //ThreadLocal变量在map的Entry数组中的位置,是由threadLocal对象的hashCode决定的
        int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
        table[i] = new Entry(firstKey, firstValue);
        size = 1;
        setThreshold(INITIAL_CAPACITY);
    }

    get方法:

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        //设置初始值
        return setInitialValue();
    }

    五、ThreadLocal如何防止内存泄漏

      ThreadLocalMap 的每个 Entry 都是一个对键(也就是ThreadLocal变量)的弱引用,这一点从super(k)可看出。另外,每个 Entry 都包含了一个对值的强引用。

    static class Entry extends WeakReference<ThreadLocal> {
        Object value;
    
        Entry(ThreadLocal k, Object v) {
            //对键的弱引用
            super(k);
            //对值的强引用
            value = v;
        }
    }

      因为对键使用的是弱引用,所以在没有其他强引用指向ThreadLocal变量时,它可被回收,从而避免上文所述ThreadLocal不能被回收而造成的内存泄漏的问题。
      但是,这里又可能出现另外一种内存泄漏的问题。ThreadLocalMap 维护 ThreadLocal 变量与具体实例的映射,当 ThreadLocal 变量被回收后,该映射的键变为 null,该 Entry 无法被移除。从而使得实例被该 Entry 引用而无法被回收造成内存泄漏。
      针对该问题,ThreadLocalMap 的 set 方法中,通过 replaceStaleEntry 方法将所有键为 null 的 Entry 的值设置为 null,从而使得该值可被回收。另外,会在 rehash 方法中通过 expungeStaleEntry 方法将键和值为 null 的 Entry 设置为 null 从而使得该 Entry 可被回收。通过这种方式,ThreadLocal 可防止内存泄漏。

    展开全文
  • 这里简单说一下自己对于JVM的理解,如果有不正确的地方欢迎大家指正。 JVM运行时数据区分为程序计数器、虚拟机栈、本地方栈、堆和方法区。其中前三个即程序计数器、虚拟机栈、和本地方发栈是线程私有的。另外两个是...

    这里简单说一下自己对于JVM的理解,如果有不正确的地方欢迎大家指正。

    JVM运行时数据区分为程序计数器、虚拟机栈、本地方栈、堆和方法区。其中前三个即程序计数器、虚拟机栈、和本地方发栈是线程私有的。另外两个是所有线程共享的。


    程序计数器当中记录的是当前程序执行的字节码的行号指示器,因为要线程之间要进行切换以实现计算机的并发处理,所以每个线程都必须有自己的程序计数器。如果程序正在执行的是NATIVE方法,则计数器的值为空(Undifined)。此内存区域是唯一一个没有OutOfMemoryError的区域。


    虚拟机栈的生命周期是和线程相同的。它描述的是方法执行的内存模型,每个方法执行时都会创建一个栈帧,方法从调用到完成的过程就对应着栈帧在虚拟机栈中的入栈和出栈过程。占中用于存储局部变量表,方法出口等信息。局部变量表中存储基本数据类型、对象引用类型和returnAddress类型。虚拟机栈又分为固定长度和可动态扩展。如果请求的栈深度超过虚拟机栈所允许的深度则会抛出StackOverflowError异常,如果动态扩展时无法申请到足够的内存时则会抛出OutOfMemoryError异常。


    本地方法栈和虚拟机栈作用类似,只不过是为Native方法服务的。naative方法实质是指用其他语言编写的方法。


    堆的唯一目的就是存放对象实例。是垃圾回收的主要区域。从内存分配的角度来看,线程共享的java堆中可能划分出多个线程私有的分配缓冲区(TLAB),堆可以处于物理上不连续的内存。


    方法区用于存储被虚拟机加载的类信息,常量,静态变量等。它是堆的而一个逻辑部分,却有一个别名“Non—heap”,这个蛮有意思的。这个地方可以不识闲垃圾回收,如果要回收的话也是针对常量池和对类型的卸载。


    运行时常量池是方法区的一部分,它相对与Class文件的常量池具有动态性,不要求常量一定只有在编译时产生,也可以在运行期间将新的常量放入池中,比如String类的intern()方法。


    展开全文
  • 在看 《深入理解计算机系统》(CSAPP)第6章存储器层次结构 时候突然想到在java并发编程 中 可见性问题,在这里简单记录一下,也不一定正确 ^_^ 我们从上面图中可以看到Intel Core i7 中有4核,每一个核心...

     

    在看 《深入理解计算机系统》(CSAPP)第6章存储器层次结构 的时候突然想到在java并发编程 中的 可见性的问题,在这里简单记录一下,也不一定正确 ^_^

    我们从上面的图中可以看到Intel Core i7 中有4核,每一个核心中都有独立的L1 L2 高速缓存, 四个核心共享L3高速缓存。

    假设L1 L2高速缓存在数据写的时候采用的是写回的策略,简单来说就是仅仅更新高速缓存中的数据,这有在根据替换策略这条数据将要被新数据替换的时候才写回到更低层次的存储器中。

    那么现在有这样的场景: 每一核中都运行着一个线程,而这些线程共享同一变量a,如果线程0(运行在核0)对变量a执行写操作,那么从图中我们可以看到仅仅会更改核0中的L1高速缓存,另外三个核上运行的线程在写回操作(准确的说是 核0需要至少写回到L3,而另外三个核重新从L3读)前是不会看到这次写操作的,这就是并发编程中的写可见性问题。

    以上这是暂时的理解,留作记录以后补充。

     

    转载于:https://www.cnblogs.com/zh1164/p/7822915.html

    展开全文
  • 结合上篇文章《https的ssl加密方式》会更好理解,这片文章中有介绍...因为共享密钥加密要比公开密钥加密快,具体是怎么快的请查看《SSL 速度慢吗》遗憾的是在使用混合加密时,使用公开密钥加密方式还是存在一些问题...

    结合上篇文章《https的ssl加密方式》会更好理解,这片文章中有介绍了https的加密方式是 混合加密,公开密钥加密和共享密钥加密,先选公开密钥加密来交换共享密钥加密的密钥,然后在使用共享密钥加密传送密文,为什么会选择两者密钥加密呢?不直接使用共享加密机制呢?因为共享密钥加密要比公开密钥加密快,具体是怎么快的请查看《SSL 速度慢吗》

    遗憾的是在使用混合加密时,使用公开密钥加密方式还是存在一些问题。那就是无法证明公开密钥本身是货真价实的公开密钥。

    比如,正准备和某台服务器建立公开密钥加密方式下的通信时,如何证明收到的公开密钥就是原本预想的那台服务器发行的公开密钥。或许密钥在传输途中,真正的公开密钥已经被攻击者替换掉了。

    上面的问题可以使用由数字证书认证机构(CA,certificate Authority)和其相关颁发的公开密钥证书。

    数字证书认证机构处于是客户端和服务器双方可信赖的第三方机构立场上,威瑞信(veriSign)就是其中一家非常有名的数字证书认证机构。

    简介一下数字证书认证机构的业务流程。首先服务器的运营人员向数字证书认证机构提出公钥的申请。数字证书认证机构在判明提出申请者的身份后,会对已申请的公开密钥做数字签名,然后分配这个已签名的公开密钥,并将公开密钥放入公钥证书后绑定在一起(注意已签名的公共密钥和公开密码是两个不同的密钥,公开密钥是服务器提供的,已签名的公开密钥是签名机构自己生成的)

    服务器会将这份数字认证机构颁发的公钥证书发送给客户端,已进行公开加密方式通信。公钥证书也可以叫做数字证书或直接称为证书。

    接到证书的客户端可使用数字证书认证机构的公开密钥,对那张证书上的数字签名进行验证,一旦验证通过,客户端便可以明确两件事情: 1、 认证服务器的公开密钥是真实有效的数字证书认证机构。2、服务器的公开密钥是值得信赖的。

    此处认证机关的公开密钥必须安全地转交给客户端,使用通信发生时,如何安全转交是一件非常困难的事,因此,多数浏览器开发商发布版本时,会事先在内部植入常用认证机构的公开密钥


    可证明组织真实性的EV SSL证书

    证书的一个作用是用来证明为通信一方的服务器是否规范,另外一个作用是可确定对方服务器背后运营的企业是否真实存在。拥有该特性的证书就是EV SSL证书(Extended Validation ssl Cretificate )

    EV SSL 证书是基于国际标准的认证指导方针颁发的证书,其严格规定了对运营商组织是否真实的确认方针,因此,用过认证的web网站能获得更高的认可度。上述机制的原意就是为了防止用户钓鱼攻击,很多用户可能不了解EV SSL证书相关的知识。


    用以确认客户端的客户端证书

    https中还可以使用客户端证书,以客户端证书进行客户端认证,证明服务器正在通信的客户端时预料之内的客户端,起作用和服务器证书如出一辙。

    但客户端证书仍存在几处问题。其中的一个问题是证书的获取和发布。

    想获取证书时,用户需要自行安装客户端证书,但客户端证书需要付费购买的,且每张证书对应到每位用户也就意味着需要支付和用户对等的费用。另外让知识层次不同的用户来自行安装证书,这是一件充满挑战的事情。

    现在是,安全性极高的认证机构可颁发客户端证书但仅用于特殊用途的业务,比如有些可支撑客户端证书支出费用的业务。

    例如,银行的网上银行就采用了客户端证书,在登录网银时不仅要求客户端确认输入ID和密码,还会要求用户的客户端证书,以确认用户时候从特定终端访问网银。

    客户端证书存在的另一个问题是,客户端证书毕竟只能用来证明客户端实际存在,而不能用来证明用户本人的真实有效性。



    转载于:https://juejin.im/post/5c204d53e51d451de2462875

    展开全文
  • 线程安全:在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现脏数据。  非线程安全 对比上面的概念,在前提不变的情况下,未采取同步机制,...
  •  本人很少原创技术文章(其他文章页很少),一般都是看别人文章写得就转发一下,但是关于多线程这块我一直都没有找到能说中要害又通俗易懂文章,本人不是什么大牛,但在java线程锁这一块觉得自己理解的还算正确吧...
  •  其实很多人在面试过程中都有遇到面试官问分布式的实现?其实要回答这个问题,即使你...锁主要是解决并发环境中,某些资源能被正确的访问或修改,保证数据的正确性。在数据库中,锁从不同角度有多种叫法,比如...
  • 互斥同步属于一种悲观的并发策略,总是认为只要不去做正确的同步措施,就肯定会出现问题。无论共享数据是否出现竞争,它都要进行加锁。故称互斥同步的锁为悲观锁 ①使用synchronized关键字。synchronized同步代码...
  • 进程和线程这两个话题是...可是你真的理解了上面最后一句话吗?到底线程之间共享了哪些进程资源,共享资源意味着什么?共享资源这种机制是如何实现的?对此如果你没有答案的话,那么这意味着你几乎很难写出能正确.
  • 文章目录1. 线性一致性来源2. 线性一致性解析1. 单线程执行的正确性2. 并发运行历史分析3. 应用程序线性化判断1. 程序线性化理论要求2.... Wing共同提出的关于并行对象行为正确一个条件模型,在《多
  • 本文讨论:   多线程和共享内存线程模型争用及并发访问如何...绝大多数程序员编写的是顺序执行程序,可以彻底避免这个问题。但是现在,多处理器计算机正在普及。很快,非多线程程序将处于劣势,因为它们无法利用可用
  • 本文讨论:   多线程和共享内存线程模型争用及并发访问...绝大多数程序员编写的是顺序执行程序,可以彻底避免这个问题。但是现在,多处理器计算机正在普及。很快,非多线程程序将处于劣势,因为它们无法利用可用
  • 本文讨论: 多线程和共享内存线程模型 争用及并发访问如何能够打破...绝大多数程序员编写的是顺序执行程序,可以彻底避免这个问题。但是现在,多处理器计算机正在普及。很快,非多线程程序将处于劣势,因为它们...
  • 的理解是它满足了网络共享和数据交互,使用DTD最大的好处在于DTD文件的共享。(就是上文DTD说明语句中的PUBLIC属性)。比如,两个相同行业不同地区的人使用同一个DTD文件来作为文档创建规范,那么他们的数据就很容易...
  • JVM方法区变革

    2020-07-20 18:07:22
    学习JVM必看书籍无疑是《深入理解Java虚拟机》这本书了,在书中,关于运行时数据区域模型是这样描述: 在这里我们只针对HotSpot VM来说,它是OracleJDK和OpenJDK中所带虚拟机,也是目前使用范围最广Java...
  • freetype一些基本概念 -- EM

    千次阅读 2010-03-02 20:41:00
    但是其中有些关于字体的基本概念一直困扰着自己,今天找点空闲加班找了一些资料,基本上算是有些眉目了,也许自己的理解正确的,^_^ 通过查找truetype手册,对EM的概念有些更深一步的了解,摘抄来共享之。...
  • 指针与数组区别

    2008-10-08 14:44:00
    以前上课的时候老师说,将数组名作为指针来使用,一直是这样认为。后来在论坛中看到其实是错的,不过不明其理,十一期间在网上发现了关于数组与指针的文章,将其翻译了,与...所以正确和精确的理解问题是非常重要的,
  • 什么是JAVA内存模型

    2019-08-09 18:58:15
    尝试一句话概括java内存模型:保证多线程之间操作共享变量的正确性。 synchronization finalvolatile 在知识星球中,有个小伙伴提了一个问题: 有一个关于JVM名词定义问题,说”JVM内存模型“,有人会说是关于...
  • 知到章节单选(3分) 下列关于泥石流等地质灾害的说法正确的是参考答案如下单选(2分) 128 个用户共享 4.096 Mbps 链路,奇异使用TDM。当每个用户轮流使用时,以全速度发送8位。用户需要多长时间才能发送一次?单选(2分...
  • 各位仁兄:序言(这是我师兄写给我的关于如何设置DirectShow开发环境,现在把它资源共享,我也很希望早日写出自己的正确理解,对于DirectShow开发指南一书,本人刚刚开始,还学习请大家多多指教。)如下:首先,原...
  • Android 5.0 以上提供了 SharedElementTransition,然而默认情况下共享的界面元素在动画时却会被放置在其他界面元素之上,导致其突然越过 AppBar 或 StatusBar 的情况。 通过大量的文档阅读、源代码分析和调试,...
  • C / C ++ / Fortran编译器不仅实现了最新OpenMP 4.5共享内存并行化规范,还提供了许多提议5.0功能。 英特尔继续在此版本英特尔Parallel Studio XE 2018上展示对科学Python承诺。这种“加速”Python分发使用...
  • 5.3 设置正确的参数 136 5.3.1 查询优化器参数 137 5.3.2 pga管理 150 5.4 小结 153 第6章 执行计划 154 6.1 获取执行计划 154 6.1.1 sql语句explain plan 154 6.1.2 动态性能视图 157 6.1.3 awr...
  • 2.2 编译和测试共享库 38 2.2.1 缺少符号时调试 40 2.2.2 在设备上测试动态库 41 2.2.3 用strace调试 42 2.2.4 静态编译 43 2.3 测试本地应用 44 2.4 下一章内容 46 第3章 从头构建Java游戏 47 ...
  • foo正确的原型链如下图: <p><img alt="oop-2" src="https://raw.github.com/maxzhang/maxzhang.github.com/master/articles/images/oop-2.png" /></p> <p><strong>Note:</strong><strong>proto属性...
  • 关于 ThreadLocal 适用场景以及解决问题,描述都并不是很清楚,甚至误人子弟。比如下面是常见对于 ThreadLocal介绍(wrong) ThreadLocal为解决多线程程序并发问题提供了一种新思路; ThreadLocal...
  • 3.7.2 模式结构的正确命名 53 3.7.3 ER概念设计设计选择 54 3.7.4 ER图候选表示法 55 3.8 其他表示法示例:UML类图 55 3.9 高于2度联系类型 57 3.9.1 对二元和三元(或高度)联系选择 ...
  • 1、在Linux上,对于多进程,子进程继承了父进程的下列哪些?(共享内存、信号掩码、已打开的文件描述符) 2、java语言中,在同一包下,以下说法正确的有() ...3、对数据库,关于索引的理解正确的是(...

空空如也

空空如也

1 2 3 4 5
收藏数 93
精华内容 37
关键字:

关于共享的理解正确的是