并发访问对象和变量

原创 2012年03月29日 19:53:59

      单例模式中涉及到了synchronized和volatile,在这小结下synchronized和volatile来控制对象和变量的并发访问。

易变成员变量修饰符volatile:

      volatile用于告诉VM:它不应当保存变量的私有拷贝,而应当直接与共享拷贝交互。  volatile强调“读”。在目前大多数的处理器架构上,volatile 读操作开销非常低 —— 几乎和非 volatile 读操作一样。而 volatile 写操作的开销要比非 volatile 写操作多很多,因为要保证可见性需要实现内存界定(Memory Fence),即便如此,volatile 的总开销仍然要比锁获取低。

    Volatile 变量具有 synchronized 的可见性特性,但是不具备原子特性。这就是说线程能够自动发现 volatile 变量的最新值。Volatile 变量可用于提供线程安全,但是只能应用于非常有限的一组用例:多个变量之间或者某个变量的当前值与修改后值之间没有约束。因此,单独使用 volatile 还不足以实现计数器、互斥锁或任何具有与多个变量相关的不变式(Invariants)的类(例如 “start <=end”)。

    例如:volatile 的功能还不足以实现计数器。因为 ++x 实际上是三种操作(读、添加、存储)的简单组合,如果多个线程凑巧试图同时对 volatile 计数器执行增量操作,那么它的更新值有可能会丢失。      volatile关键字用于声明简单类型变量,如int、float、 boolean等数据类型。如果这些简单数据类型声明为volatile,对它们的操作就会变成原子级别的。

 (1)将volatile作为状态标识使用

public class  ZX extends Thread
{
	private volatile boolean zx;
	public void run(){
		while(!zx){}
	}
	public void tellMeTOStop(){
		zx=true;
	}
}  
   注:假如zx没有被声明为volatile,线程执行run的时候检查的是自己的副本,就不能及时得知其他线程已经调用tellMeToStop()修改了zx的值。

 (2)将volatile用于一次性安全发布


public class BackgroundFloobleLoader {
    public volatile Flooble theFlooble;

    public void initInBackground() {
        // do lots of stuff
        theFlooble = new Flooble();  // this is the only write to theFlooble
    }
}

public class SomeOtherClass {
    public void doWork() {
        while (true) { 
            // do some stuff...
            // use the Flooble, but only if it is ready
            if (floobleLoader.theFlooble != null) 
                doSomething(floobleLoader.theFlooble);
        }
    }
}
注:volatile主要强调“读”,但在这如果 theFlooble 引用不是 volatile 类型,doWork() 中的代码在解除对 theFlooble 的引用时,将会得到一个不完全构造的Flooble。  volatile 类型的引用可以确保对象的发布形式的可见性,但是如果对象的状态在发布后将发生更改,那么就需要额外的同步。

 (3)将volatile用于多个对立观察结果


public class UserManager {
    public volatile String lastUser;

    public boolean authenticate(String user, String password) {
        boolean valid = passwordIsValid(user, password);
        if (valid) {
            User u = new User();
            activeUsers.add(u);
            lastUser = user;
        }
        return valid;
    }
} 
 注:该模式是前面模式的扩展;将某个值发布以在程序内的其他地方使用,但是与一次性事件的发布不同,这是一系列独立事件。这个模式要求被发布的值是有效不可变的 —— 即值的状态在发布后不会更改。使用该值的代码需要清楚该值可能随时发生变化。

  (4)将volatile用于bean模式中


public class Person {
    private volatile String firstName;
    private volatile String lastName;
    private volatile int age;

    public String getFirstName() { return firstName; }
    public String getLastName() { return lastName; }
    public int getAge() { return age; }

    public void setFirstName(String firstName) { 
        this.firstName = firstName;
    }

    public void setLastName(String lastName) { 
        this.lastName = lastName;
    }

    public void setAge(int age) { 
        this.age = age;
    }
}
 注:在 volatile bean 模式中,JavaBean 的所有数据成员都是 volatile 类型的,并且 getter 和 setter 方法必须非常普通 —— 除了获取或设置相应的属性外,不能包含任何逻辑。此外,对于对象引用的数据成员,引用的对象必须是有效不可变的。(这将禁止具有数组值的属性,因为当数组引用被声明为volatile 时,只有引用而不是数组本身具有 volatile 语义)。对于任何 volatile 变量,不变式或约束都不能包含 JavaBean 属性。


java中提供了两种内在的同步机制:volatile变量和同步块(方法)。而同步块(方法)常用到synchronized关键字。

  synchronized关键字的作用域:

(1)同步方法:

     a.同步对象实例的方法:  synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。这时,不同的对象实例的synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法.

   b.同步类中的方法:某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static 方法。它可以对类的所有对象实例起作用。

public class TextThread
{

 /**
  * @param args
  */
 public static void main(String[] args)
 {
  // TODO 自动生成方法存根
        TxtThread tt = new TxtThread();
        new Thread(tt).start();
        new Thread(tt).start();
 }

}
class TxtThread implements Runnable
{
 int num = 3;
 String str = new String();
 public void run()
 {
  while (true)
  {
   synchronized(str)
   {
   if (num>0)
   {
    try
    {
     Thread.sleep(2);
    }
    catch(Exception e)
    {
     e.getMessage();
    }
    System.out.println(Thread.currentThread().getName()+ "this is "+ num--);
   }
   }
  }
 }
}

(2)同步块:synchronized关键字还可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。用法是: synchronized(this){/*区块*/},它的作用域是当前对象。 

   :synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定它的某个方法为synchronized方法。

   总的说来,synchronized关键字可以作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块。如果再细的分类,synchronized可作用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)身上。


volatile和synchronized的区别:

1.volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
2.volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的。
3.volatile仅能实现变量的修改可见性,并能保证原子性;而synchronized则可以保证变量的修改可见性和原子性。
4.volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。

5.volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化。


 结合volatile和synchronized实现“开销较低的读写锁”

public class CheesyCounter {
  private volatile int value;
  private   int value1;               
  public int getvalue1() {return value1;}   
  public int getValue() { return value; }
    public synchronized int increment() {
        return value++;
    }
}

    得到存储在当前线程中value1的数值。多个线程有多个value1变量拷贝,而且这些value1之间可以互不相同。换句话说,另一个线程可能已经改变了它线程内的value1值,而这个值可以和当前线程中的value1值不相同。事实上,Java有个思想叫“主”内存区域,这里存放了变量目前的“准确值”。每个线程可以有它自己的变量拷贝,而这个变量拷贝值可以和“主”内存区域里存放的不同。因此实际上存在一种可能:“主”内存区域里的value1值是1,线程1里的value1值是2,线程2里的value1值是3——这在线程1和线程2都改变了它们各自的value1值,而且这个改变还没来得及传递给“主”内存区域或其他线程时就会发生。
  而 getiValue()得到的是“主”内存区域的i2数值。用volatile修饰后的变量不允许有不同于“主”内存区域的变量拷贝。换句话说,一个变量经 volatile修饰后在所有线程中必须是同步的;任何线程中改变了它的值,所有其他线程立即获取到了相同的值。理所当然的,volatile修饰的变量存取时比一般变量消耗的资源要多一点,因为线程有它自己的变量拷贝更为高效。


参考:http://www.ibm.com/developerworks/cn/java/j-jtp06197.html



版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

对象能够直接访问其私有成员变量

转自:http://www.cnblogs.com/dwdxdy/archive/2012/07/17/2595741.html【C++】 私有成员变量的理解 私有成员变量的概念,在脑海中的...

通过反射访问对象的私有变量和执行私有方法

package reflection.domain; /** * * * @author snowday88 */ public class Student { ...

派生类的对象访问基类中被派生类覆盖或隐藏了的函数或变量

 隐藏基类成员函数的情况:如果在派生类中定义了一个与基类同名的函数,不管这个函数的参数列表是不是与基类 中的函数相同,则这个同名的函数就会把基类中的所有这个同名的函数的所有重载版本都隐藏了,这...

C++在类的成员函数中,允许直接访问该类的成员对象的私有成员变量

问题(知识点)描述:   a .   在 C++ 的类的成员函数中,允许直接访问该类的对象的私有成员变量。   b .   在类的成员函数中可以访问同类型实例的私有变量。   c ....

Java中的null对象也可以访问static成员变量和方法

注意:Java中的null对象也可以访问static成员。不过虽然这样的做法没错,却不值得提倡,因为有时会给人带来困扰,我自己在走读代码时就经历过...

C++在类的成员函数中,允许直接访问该类的成员对象的私有成员变量

问题(知识点)描述: a. 在C++的类的成员函数中,允许直接访问该类的对象的私有成员变量。 b. 在类的成员函数中可以访问同类型实例的私有变量。 c. 拷贝构造函数里,可以直接访问另外一个同类...

派生类的对象访问基类中被派生类覆盖或隐藏了的函数或变量

其实很简单 隐藏基类成员函数的情况:如果在派生类中定义了一个与基类同名的函数,不管这个函数的参数列表是不是与基类 中的函数相同,则这个同名的函数就会把基类中的所有这个同名的函数的所有重载版本都隐藏...

java 父类访问子类对象的实例变量 继承过程中的执行顺序

子类的方法可以访问父类的实例变量,

Java之访问子类对象的实例变量

子类的方法可以访问父类的实例变量,这是因为子类继承父类就会获得父类的成员变量和方法。 父类的方法不能访问子类的实例变量,父类无从知道它将被哪个子类继承,它的子类将会增加怎样的成员变量。 Java对象不...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)