IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 并发编程基础五----构造并发布正确的对象 -> 正文阅读

[Java知识库]并发编程基础五----构造并发布正确的对象

前面讲了通过同步的方式可以安全的访问共享状态,并讲述了同步的几种方式。接下来两个内容将对《JAVA并发编程实战》中第三章对象的共享进行总结。

同步机制可以避免多个线程在同一时间访问相同的数据,对于共享变量来说如果被static修饰时是被分配到方法区中,被final修饰的静态变量是不可变的,这种本身就是线程安全的,如果没有被final修饰则在访问时需要通过同步方式保证其线程安全性,常用的方法有加锁和volatile两种方式,没被static修饰的变量在创建对象时存放在堆区中,依赖于对象的安全性。而在多线程里发布一个安全对象是要保证在发布的过程中创建一个完整的对象。

该部分主要包含两个部分:

1、对象的发布和逸出

2、发布对象的几种方式

3、另一种数据安全的共享方式

对象发布

首先理解一下什么是对象发布

发布:在对象构建完成以后可以在当前代码之外的地方可以使用。主要是可以让其他类和线程可见

逸出:某个不该发布的对象发布了就是对象的逸出,比如说并发编程中如果不能保证安全的访问对象的情况下则在对象构造完成之前如果被发布出去了则说明该对象逸出了。

常用的发布对象的方式有以下几种

1、在对象构造完成后将对象的引用保存在一个共有的静态变量中。这样做的好处就是在发布一个对象的时候可能还会发布其他对象比如说非私有域中引用的所有对象。

举个例子说明以下:

????????比如说将一个对象存放在一个集合中,这样只要访问这个集合的其他类或者其他线程都可以看到这个对象的引用,但是这个只限定于对非private修饰的对象可以这样发布,如果是类的私有对象这样做就会导致这个对象逸出破坏了封装性,因为用private修饰的对象只限定于该类中可见,对其他类和线程是不可见的。

这里解释以下封装的主要原因:封装可以对简化对程序正确性的分析,并将private作用域作为一个约束防止无意中对作用域的破坏。


public static Set<Secret> knownSecrets;
public void setSecret(){
    knownSecret = new HashSet<Secret>();
}

2、发布一个内部类实例。但这种方式需要注意一点,在发布时候防止外部类的this逸出。

因为在发布内部类时在外部封装的外部类实例也会被发布,但此时外部类还没有被初始化完成,这样就会导致对象逸出。因此为了避免这种方式的对象逸出必须避免在对象的构造过程中使this引用逸出。编程过程中就是避免在构造过程中启动一个线程,这样就不会看到一个不完整的对象。

举例:

不安全的方式

public class ThisEscape{
    public ThisEscape(EventSource source){
        //注册一个监听器也是开启一个新线程
        source.registerlistener(
            new EventListener(){
                public void onEvent(Event e){
                    doSomething();
                }
            }
        );
    }
}

安全的发布一个对象的方式

public class SafeListener{
    private final EventListener listener;

    public SafeListener(){
        listener = new EventListener(){
                public void onEvent(Event e){
                    doSomething();
                }
        };
    }
    //单独拿出来进行注册
    public static SafeListener newInstance(EventSource source){
        SafeListener safe = new SafeListener();
        source.registerListener(safe.listener);
        return safe;
    }
}

3、安全的访问共享数据有两种方式:一种是之前讲过的通过同步的方式,第二种就是通过线程封闭的方式,即将每个线程使用共享数据的副本的形式或者每次只允许一个线程获取该对象。

线程封闭有以下三种方式:

第一种是Ad-hoc线程封闭

? ? ? ? Ad-hoc线程封闭模式是通过设计一个特定职责的单线程的子系统来维护线程的封闭。

? ? ? ?常见的采用这种模式的是安全的数据库连接池,让线程池来维护封闭性,连接池承接多个线程的访问,但每次只允许一个线程进入连接池获取connection对象,在该对象返回之前不允许其他线程进入线程池获取connection对象。

但这种线程封闭的方式比较脆弱,为什么脆弱?,看书上的解释是没有任何一种语言特性,但是太抽象了,不是很理解,有了解的同学可以一起探讨以下。

第二种是栈封闭的方式

栈封闭就是将基本类型的变量或者引用类型的变量设置为局部变量的方式,从而将其封装在线程中。

对于基本变量,由于其他线程用任何方法都不能获得这种类型的引用,而且其作用域只存在该变量所在的方法体中,因此对于局部基本变量,一直都是很安全的。

对于引用变量,一般是创建一个集合和一个实例对象,并将该实例对象的引用指向该集合,只要该集合的引用不被发布,就能确保这个实例对象的引用被封装在该线程中。

例子:

public void loadTheArk(Collection<Animal> candidates){
    Set<Animal> animals;
    int numPairs = 0;
    Animal candidates = null;
    
    animals = new TreeSet<Animal>;
    animals.addAll(candidates);
}

第三种方式就是使用TheadLoacl类

这种方式原理是将内存中的数据做n个副本,存储在线程局部的缓存中,在涉及到ThreadLocal的线程中使用该数据的副本,从而防止可变的单实例变量或全局变量共享。但过多的使用ThreadLocal对象会降低代码的可重用性,并增加了各类之间的耦合度。因此不能过多的使用。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-09-24 10:24:23  更:2021-09-24 10:26:37 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 18:49:40-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码