并发编程实战--对象的共享

同步中的一个重要方面:内存可见性。我们不仅希望防止某个线程正在使用对象状态而另一个线程正在同时修改该状态,而且希望确保党一个线程修改了对象状态后,其他线程能够看到发生的状态变化。

1.可见性
在没有同步的情况下,编译器,处理器以及运行时环境等都可能对操作的执行顺序进行一些意想不到的调整。在缺乏足够同步的多线程程序中,要想对内存操作的执行顺序进行判断,几乎无法得出正确结论。

package thread_test;

public class ReadThread {
    private static boolean ready;
    private static int number;
    private static class RT extends Thread{
        public void run(){
            while(!ready)
                Thread.yield();
            System.out.println(number);
        }
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        new RT().start();
        number=42;
        ready=true;
    }

}

1.1失效数据
失效数据是指,当读线程查看ready变量时,可能会得到一个已经失效的值。除非在每次访问变量时都使用同步,否则很可能得到该变量的一个失效值。更糟糕的是,失效值可能不会同时出现,即一个线程可能获得某个变量的最新值,但是获得另一个变量的失效值。

1.2加锁与可见性
内置锁可以用于确保某一个线程以一种可预测的方式来查看另一个线程的执行结果。如图所示。当线程A执行某个同步代码块时,线程B随后进入由同一个锁保护的同步代码块,在这种情况下可以保证,在锁被释放前,A看到的变量值在B获得锁后同样可以由B看到。
这里写图片描述

1.3Volatile变量
vilatile变量是一种java提供的同步机制,其作用是确保将变量的更新操作通知到其他线程,在读取volatile类型的变量时总会返回最新写入的值。volatile变量的一个典型用法是:检查某个状态标记以判断是否退出循环(如下所示)。

volatile boolean asleep;
...
    while(!asleep)
    countSomeSleep();

2.发布与逸出
“发布”(publish)一个对象的意思是指,是对象能够在当前作用于之外的代码中使用。如果在发布时要确保线程的安全性,则可能需要同步。如果在对象构造完成之前就发布该对象,就会破坏线程安全性。当某个不应该发布的对象被发布时,这种情况就称为“逸出”(escape).

3.线程封闭

当访问共享的可变数据是,通常需要使用同步。一种避免使用同步的的方式就是不共享数据。如果仅在单线程内访问数据,就不需要同步。这种技术被称为线程封闭,它是实现线程安全性的最简单方式之一。当某个对象封闭在一个线程中时,这种用法将自动实现线程的安全性,及时被封闭的对象本身不是线程安全的。典型的线程封闭技术体现是Swing组件和JDBC连接方式。

3.1三种线程封闭方式:
· Ad-hoc线程封闭
· 栈封闭
· ThreadLocal类

4.不变性
满足同步需求的另一种方法是使用不可变对象。如果某个对象在创建后其状态就不能被修改,那么这个对象就被称为不可变对象。线程安全性是不可变对象的固有属之一,他们的不变性条件是由构造函数创建的,只要他们的状态不改变,那么这些不变性条件就能够得以维持。不可变对象一定是线程安全的。

当满足以下条件时,对象才是不可变的。
·对象创建以后其状态就不能修改
·对象的所有域都是final类型
·对象是正确创建的(没有逸出)

4.1Final域:
关键字final用于构造不可变性对象。final类型的域是不能修改的。在java内存模型中,final域还有特殊的语意。final域能够保证初始化过程的安全性,从而可以不受限制的访问不可变对象,并在共享这些对象时无需同步。即使对象是可变的,通过将对象声明为final类型,仍然可以简化对状态的判断,因此限制对象的可变性也就相当于限制了该对象可能的状态集合。

5安全发布的常用模式:
可变对象必须通过安全的方式来发布,这通常意味着在发布和使用该对象的线程时都必须使用同步。要安全的发布一个对象,对象的引用以及对象的状态必须同时对其他线程可见。一个正确构造的对象可以通过以下方式来安全的发布:
·在静态初始化函数中初始化一个对象引用。
·将对象的引用保存到volatile类型的域或者AtomicReference对象中。
·将对象的引用保存到某个正确构造对象的final类型域中。
·将对象的引用保存到一个由锁保护的域中。

在线程安全容器同步意味着,再将对象放入到某个容器,例如Vector或者synchroniedList时,将满足上述最后一条需求。在线程安全库中的容器类均提供了以下的安全发布保证:

·通过将一个键或者值放到Hashtable,synchroniedMap,concurrentMap中,可以安全地将它发布给任何从从这些容器中访问的线程。
·通过将某个元素放入Vector、CopyOnWriteArrayList,CopyOnWriteArraySet,synchroniedList或者synchroniedSet中,可以将该元素安全的发布到任何从这些容器中访问该元素的线程。
·通过将某个元素放入BlockingQueue或者concurrentLinkedQueue中,可以将该元素安全地发布到任何从这些对垒中访问该元素的线程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值