您的位置:首页 > 其它

对共享可变数据的同步访问

2010-02-08 17:20 197 查看
一般认为同步就是一般的互斥行文。不是很全面。

同步的意义:同步不仅可以阻止一个线程看到对象处于不一致的状态中,它还可以保证通过一系列看似顺序执行的状态转变序列,对象从一种一致的状态变迁到另外一种一致的状态。每一个线程进入到一个被同步的方法或是代码块的时候,它会看到由同一个锁控制的以前所有状态转变的结果。当线程退出了这个被同步的区域之后,任何线程在进入到由这同一把锁同步的区域时,它就可以看到由前面那个线程带来的状态转变(如果有状态转变的话)。也就是代码是固定的,变量的地址,或是值变量也是固定的,不同的线程会造成数据被修改的情况,第2个线程进入就会看到被修改的内存值域。

JAVA语言保证读或者写一个变量是原子的,除非这个变量类型为long或是double.也就是说,读入一个非long或double类型的变量,可以保证返回值一定是某个线程保存在该变量中的,即使多个线程在没有同步的情况下并发修改这个变量,也是如此。

有些人说,为了提高性能,在读或写原子数据的时候,你应该避免使用同步。其实这个建议是非常危险而且是错误的。虽然原子性保证了一个线程在读取原子数据的时候,不会看到一个随机的数值,但是它并不保证一个线程写入的值对于另外一个线程是可见的:为了在线程之间可靠的通信,以及为了互斥访问,同步是需要的。

以下代码就可以看出来:

/**

*

*/

package thread;

/**

* 单例的Test类,保证唯一性。

* @author Administrator

*

*/

public class Test {

private int flag=0;

private static Test temp=null;

public synchronized void method(){

flag=flag+1;

System.out.println(flag);

try {

Thread.sleep(4000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

public static Test getInstance(){

if(temp==null){

temp=new Test();

return temp;

}

return temp;

}

}

这个类是执行一个方法method,该方法要被多次调用。

/**

*

*/

package thread;

/**

* 实例一个单例的test对象启动线程,调用test.method同步方法。

* @author Administrator

*

*/

public class ThreadTemp extends Thread{

public void run(){

Test test=Test.getInstance();

test.method();

}

}

这个类是启动线程的,他使用的单例,让这个test对象成为唯一的共享对象,包括对象的值域。

/**

*

*/

package thread;

/**

* 开启两个线程去访问单例的test,造成域被改变。

* @author Administrator

*

*/

public class ThreadFactory {

public static void main(String[] args) {

Thread t1=new ThreadTemp();

t1.start();

Thread t2=new ThreadTemp();

t2.start();

}

}

开始访问可以看到第2个线程进入的时候值域flag已经是1了。

所以打印出来是1,2.

所以JAVA线程的特性我们应该引起注意。这个是线程安全的,当你把method方法的synchronized取消,然后在主函数调用的地方改成循环实例线程,并且把sleep也去掉。你将看到奇怪的现象。这里就是竞争造成的。

记住一句话:无论任何时候当多个线程共享可变数据的时候,每个读或写数据的线程必须获得一把锁,而这个锁只能是对象锁。如果没有同会察到。而且这样的错误,是很难调试和重现的,因为线程调试是很麻烦的。他们是高度依赖JVM和操作系统硬件平台。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: