为编程爱好者分享易语言教程源码的资源网
好用的代理IP,游戏必备 ____广告位招租____ 服务器99/年 ____广告位招租____ ____广告位招租____ 挂机,建站服务器
好用的代理IP,游戏必备 ____广告位招租____ 服务器低至38/年 ____广告位招租____ ____广告位招租____ 挂机,建站服务器

网站首页 > 网络编程 > 其它综合 正文

多线程并发之对象的共享(多线程共享进程的什么资源)

三叶资源网 2022-10-09 19:18:50 其它综合 200 ℃ 0 评论

在编写多线程程序的时候,关键之处在于:在操作共享可变状态时需要进行正确的管理

可见性

  • 在单线程中,对于状态变量的更改总能正确操作。
  • 在多线程中,由于线程执行的不确定性顺序,再加上指令的重排序(在不使用同步的情况下,编译器、处理器可能会对操作的执行顺序进行调整),会造成不确定性结果。

因此,只要有数据被多个线程操作,就应该同步。

失效数据:在多线程中,线程可能会获得某个变量的最新值,也可能获得失效值(不正确的值)。而多个线程,可能一个线程获取正确值,另一个线程获取失效值;也可能两个都同时获取正确值或者同时获取失效值,取决于运气。

public class A {
    private int value;
    public int getValue() {
        return this.value;
    }
    public void setValue ( int value ) {
        this.value = -1;
        this.value = value;
        this.value=-2;
    }
}

上面的类不是线程安全类,当一个线程设置值时,另一个线程读取值,可能存在结果:

  • 结果一:0。读线程先于写线程执行。
  • 结果二:-1。写线程中this.value=-1后继续读线程。
  • 结果三:-2。指令重排序先this.value=-2后立即读线程。
  • 结果三:设置的值。期待的正确值。

非原子的64位操作

对于64位的long和double变量,JVM允许读操作或写操作分解为2个32位的操作,因此就不是原子操作了,可以使用关键字volatile或者锁来保护。

有序性

volatile 和 synchronized 两个关键字来保证线程之间操作的有序性,volatile 是因为其本身包含“禁止指令重排序”的语义,synchronized决定了持有同一个锁的两个同步块只能串行执行。

加锁和可见性

加锁,不仅仅是为了线程之间的互斥行为,也包含内存可见性,另外为了保证所有的线程安全,都必须使用同一把锁。

可见性,是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。

  • volatile关键字

用于修饰变量,提供一种弱同步机制,确保变量的更新操作通知到其他线程。

volatile只能保证可见性;而同步既能保证可见性,也能保证原子性。

volatile修饰的变量不允许线程内部缓存和重排序,即直接修改内存,所以对其他线程是可见的。但是需要注意一个问题,volatile只能让被它修饰变量具有可见性,但不能保证它具有原子性。比如 volatile int a = 0;之后有一个操作 a++;这个变量a具有可见性,但是a++ 依然是一个非原子操作,也就是这个操作同样存在线程安全问题。

volatile之内存屏障:内存屏障有两个能力:

1. 阻止屏障两边的指令重排序:对volatile关键字定义的变量进行操作时,会在其前后加上内存屏障,使其之后的代码不能重排序到之前运行;

2. 刷新处理器缓存/冲刷处理器缓存:非volatile关键字的变量,正常情况下是,CPU会对其变量值进行缓存,由于缓存的操作非原子操作(内存值存进缓存、缓存修改、缓存值写出到内存),因此缓存中的数值的改变,其他线程是不能立即发现的。volatile修饰的变量值,会使其缓存失效,直接操作共享内存中的数值,因此其他线程会立即所见。

对于volatile关键字修饰的共享变量,不会存在线程阻塞,因此性能上强于synchronized,但是其仅能保证可见性,原子操作是不能保证的,再加上其难以理解和控制,因此使用volatile需要更加的小心验证。

伪共享

cpu中的缓存一致性协议是锁的缓存行,但是在cpu缓存架构中,如果我们多线程频繁的对一些变量进行频繁操作,那么线程之间的数据就频繁失效,频繁的去主存中加载;缓存行的大小最大为64byte,那么如果多个线程都在同一个缓存行中频繁操作,那么就会出现缓存失效,然后又要重新加载,这种不合理的资源竞争的关系称为伪共享。

伪共享在多线程中性能开销比较大,因此需要处理,其有两种方法:

  • 增加变量将其存储直接占满一个缓存行;
  • JDK8提供了一种变量注解“@Contended”标明可以占满缓存行,但是需要增加启动参数-XX:-RestrictContended

对象的发布与逸出

发布:使对象能够在当前作用域之外的代码中使用。

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

上面的knownSecrets对象可以被其他任意代码引用。同时,Set中的元素 Secret对象也会被发布出去。

逸出:当某个不应该被发布的对象发布出去了。

class UnsafeStates{
 	  private String[] states = new String[]{"AK","AL"};
   	public String[] getStates(){return states;}
 }
 //调用者修改
 UnsafeStates u = new UnsafeStates();
 String[] myStates = u.getStates();
 myStates[0] ="ABC"

states数组是一个私有的,被公共方法发布出去了,调用者可以修改这个数组的内容,因此states已经逸出了它的作用域,这个私有的变量就被发布了。

一个已经被发布的对象通过非私有的变量的引用和方法调用到其他对象,那其他对象也会被发布。

当把一个对象传递给某个外部方法时,就相当于发布了这个对象,这时无法知道哪些代码会执行,也不知道在外部方法中究竟会发布这个对象,还是会保留对象的引用并随后由另一个线程使用。主要风险在于误用这个对象的引用。

当外部类构造函数发布一个内部类实例时,内部类EventListener也会隐式地发布这个外部类本身,内部类实例中包含了对外部类的隐式引用this

public class ThisEscape {
  public ThisEscape(EventSource source) {
    source.registerListner(
      new EventListener(){
        public void onEvent(Event e) {
          doSomthing(e); 
       }
      });
  }
}
  • 安全的对象构造过程

在上面例子中,this引用在构造函数中逸出,当且仅当对象的构造函数返回时,对象才处于可预测和一致的状态,因此,当从对象的构造函数中发布对象时,只是发布了一个尚未构造完成的对象,如果this引用在构造函数中逸出,那么这种对象就是不正确构造(构造函数中this引用的本类对象实际并没有生成)。

只有当构造函数返回时,this引用才应该从线程中逸出,可以将this引用保存到某个地方,只要其他线程不会在构造函数完成之前使用它。

一个常见的错误是,在构造函数中启动一个线程,这个新的线程将会看到一个未构造完成的this引用,虽然可以,但是不要立即启动这个线程。

因此最好不要在对象构造中使用this引用逸出(内部类、直接调用可改写实例方法)。

如果非要这样使用,可以使用私有的构造器和工厂方法。

public class SafeListener{
    private final EventListener listener;
    private SafeListener() {
        listener = new EventListener() {
            public void onEvent (Event e){
                doSomthing(e);
            }
        }
    }
    public static SafeListener newInstance (EventSource source) {
        SafeListener safe = new SafeListener();
        source.registerListener (safe.listener);
         return safe;
    }
}

这个私有的构造器不会被外部方法调用,因此,this不能逸出了。

来源:三叶资源网,欢迎分享,公众号:iisanye,(三叶资源网⑤群:21414575

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

百度站内搜索
关注微信公众号
三叶资源网⑤群:三叶资源网⑤群

网站分类
随机tag
装修预算计算器自媒体平台MIDI解析二维数组奇易模块网页图片批量分析下载自定义执行代码屏幕录像高仿WPF界面外挂枚举IE插件易语言调试助手微信公众号代理注册RC4加密源码smartQQ画板源码异步服务总结应用企鹅FM评论填表登录
最新评论