`
须等待
  • 浏览: 212805 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

神奇的线程安全问题

    博客分类:
  • Java
阅读更多

    读完了《Java并发编程实践》这本书以后,以为对一般的线程安全问题有一个大概的理解,但是今天遇到的这个问题着实非常神奇,在书中也没有被提到过,这里特别记录下来。

 

public class Test {
    public void test1(O o) throws Exception {
        System.out.println("begin o:" + o.getId() + " " + o.getName());
        if (o.getId() == 1) {
            Thread.sleep(10000);
            System.out.println("o1:" + o.getId() + " " + o.getName());
        }

        if (o.getId() == 2) {
            System.out.println("o2:" + o.getId() + " " + o.getName());
        }
        System.out.println("end o:" + o.getId() + " " + o.getName());
    }

    public static void main(String[] args) throws Exception {

        final O o1 = new O(1, "o1");
        Thread t1 = new Thread() {
            @Override
            public void run() {
                try {
                    Test test = new Test();
                    test.test1(o1);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        Thread t2 = new Thread() {
            @Override
            public void run() {
                try {
                    o1.setId(2);
                    o1.setName("o2");
                    Test test = new Test();
                    test.test1(o1);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        t1.start();
        Thread.sleep(10);
        t2.start();

    }
}

class O {

    public O(int id, String name) {
        super();
        this.id = id;
        this.name = name;
    }

    private int id;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

 

    两个线程先后启动,传入的是不同状态的同一对象。在test1方法的第一条打印中可以看出来,参数对象的状态是不同的,但是当第一个线程在第一个判断处休眠第二个线程开始执行以后,第一个线程打印出来的状态就和之前不一致了,其状态和第二个参数对象的状态一样。按照理论,方法执行以后,JVM会在独立的线程中压入一个方法栈,栈中会包含一个方法执行所需要的所有元素(包括代码和参数),这里为什么会出现方法执行到一半的时候参数状态改变呢?

 

    推测:因为方法栈中压入的是一个对象的指针,而不是整个对象,所有当对象的状态被改变的时候还是可以第一时间被独立线程所察觉,所以尽管已经进入了方法栈,还是会引起对象状态的改变。

 

    以后在处理多线程的问题时,必须要所用无状态Immutable的VO对象。这样才能保证线程安全

0
1
分享到:
评论
3 楼 须等待 2012-11-10  
beiyeren 写道
zfc827 写道
看来你还没有理解线程安全的基本概念啊。
判断一个对象线程安全与否只有一个标准,即此对象在不同的上下文中,是否仍指向同一个对象。
关于这点也很好把握,由于线程在执行一个方法时,会创建自己的堆栈,此堆栈对别的线程而言是透明的。
比如你上面的代码中,run方法中有Test test = new Test();
那么开启的线程会在自己的线程堆栈中创建test这个实例,有多少个线程执行,就会有多少个实例,这些实例之间互不影响。
要引发线程安全问题,只有一个方法,即在同一个JVM进程中,对象实例只存在一个,多个线程对此实例进行操作的场景。
在你上面的代码中,o1对象是在main方法中被实例化,由于main方法是static的,所以o1也是static的,在下面两个线程上下文中引用的都是同一对象。


楼主还可以多看看虚拟机规范,会有帮助的。很多事情,要有理论支撑,不能全靠认为。


嗯,你说得对,这点确实应该是属于虚拟机规范的,我犯的这个错误最主要的原因是太迷信方法栈参数的线程安全性,完全不会想到原来对象成为参数入栈之后其仍然存在线程不安全的情况,现在看来入栈的只是指针不是整个对象,从这点看来方法参数也不一定完全就是安全的,确实应该再认真研究一下虚拟机规范
2 楼 beiyeren 2012-11-10  
zfc827 写道
看来你还没有理解线程安全的基本概念啊。
判断一个对象线程安全与否只有一个标准,即此对象在不同的上下文中,是否仍指向同一个对象。
关于这点也很好把握,由于线程在执行一个方法时,会创建自己的堆栈,此堆栈对别的线程而言是透明的。
比如你上面的代码中,run方法中有Test test = new Test();
那么开启的线程会在自己的线程堆栈中创建test这个实例,有多少个线程执行,就会有多少个实例,这些实例之间互不影响。
要引发线程安全问题,只有一个方法,即在同一个JVM进程中,对象实例只存在一个,多个线程对此实例进行操作的场景。
在你上面的代码中,o1对象是在main方法中被实例化,由于main方法是static的,所以o1也是static的,在下面两个线程上下文中引用的都是同一对象。


楼主还可以多看看虚拟机规范,会有帮助的。很多事情,要有理论支撑,不能全靠认为。
1 楼 zfc827 2012-11-10  
看来你还没有理解线程安全的基本概念啊。
判断一个对象线程安全与否只有一个标准,即此对象在不同的上下文中,是否仍指向同一个对象。
关于这点也很好把握,由于线程在执行一个方法时,会创建自己的堆栈,此堆栈对别的线程而言是透明的。
比如你上面的代码中,run方法中有Test test = new Test();
那么开启的线程会在自己的线程堆栈中创建test这个实例,有多少个线程执行,就会有多少个实例,这些实例之间互不影响。
要引发线程安全问题,只有一个方法,即在同一个JVM进程中,对象实例只存在一个,多个线程对此实例进行操作的场景。
在你上面的代码中,o1对象是在main方法中被实例化,由于main方法是static的,所以o1也是static的,在下面两个线程上下文中引用的都是同一对象。

相关推荐

    神奇通道系统

    4. **安全控制**:考虑到“神奇”一词,这个系统可能具有高级的安全特性,如端到端加密、身份验证、访问控制、防火墙策略等,确保数据在传输过程中的安全性。 5. **性能优化**:为了实现高效传输,系统可能采用了...

    网盘下载神奇.rar

    "网盘下载神奇.rar"这个压缩包文件,很可能包含了一款名为PanDownload的软件,这是一款专为解决百度网盘下载速度问题而设计的第三方工具。PanDownload能够帮助用户绕过官方的限速策略,实现接近满速的下载速度,极大...

    网络安全试验一X-scan的安装和基本用法).docx

    X-Scan 采用多线程方式对指定 IP 地址段(或单机)进行安全漏洞检测,支持插件功能。扫描内容包括:远程服务类型、操作系统类型及版本、各种弱口令漏洞、后门、应用服务漏洞、网络设备漏洞、拒绝服务漏洞等二十几个...

    探索Java代码的隐秘角落:静态分析工具的神奇力量

    4. **多线程**:Java内置了对多线程编程的支持,允许开发者创建同时执行的多个线程。 5. **网络编程**:Java提供了丰富的网络通信API,使得开发网络应用变得容易。 6. **安全性**:Java提供了一个安全

    神奇的paint方法

    `paint`方法通常不直接由程序员调用,而是由Java事件调度线程(Event Dispatch Thread, EDT)在需要时调用,确保了界面的线程安全。在`paint`方法内部,它首先调用`paintComponent`、`paintBorder`和`paintChildren`...

    X-Scancn服务器扫描器

    X-Scan采用多线程方式对指定IP地址段(或单机)进行安全漏洞检测,支持插件功能,提供了图形界面和命令行两种操作方式,扫描内容包括:远程操作系统类型及版本,CGI漏洞,IIS漏洞,RPC漏洞,SQL-SERVER、FTP-SERVER等...

    通往Android的神奇之旅第九章Network.rar

    在“通往Android的神奇之旅第九章Network.rar”这个压缩包中,主要涵盖了Android应用程序开发中的一个重要领域——网络编程。在Android应用开发中,网络功能是不可或缺的,它使得应用程序能够与互联网上的服务器进行...

    android XMl 解析神奇xstream 一

    然而,需要注意的是,由于Android的安全机制,直接在主线程上执行XML解析可能会导致ANR(应用无响应)错误,因此应当在后台线程如AsyncTask中进行。 在使用XStream时,我们还需要关注一些最佳实践。例如,为了提高...

    MuetzillaClicker

    并且它们表现得“神奇”,那么可能它们采用了并发控制策略,如synchronized关键字、Lock接口(如ReentrantLock)或者并发工具类(如Semaphore、CountDownLatch),来确保线程安全并优化性能。 程序的运行状态可能是...

    MutexSpinlockAndCAS.tar.gz

    在多线程编程中,确保线程安全是至关重要的,而这通常涉及到同步机制。本压缩包"MutexSpinlockAndCAS.tar.gz"包含了关于互斥锁(Mutex)、自旋锁(Spinlock)以及比较器和交换(Compare-And-Swap, CAS)操作的源码...

    PE 可执行文件超强查壳神奇/Die最新版2.0

    在IT领域,尤其是逆向工程和恶意软件分析中,"PE可执行文件超强查壳神奇/Die最新版2.0"是一款重要的工具,主要用于检测和处理PE(Portable Executable)格式的可执行文件上的加壳技术。PE文件是Windows操作系统中...

    JAVA小实例 边练边学 我已经用过了,很好用,开始学的同学也可以先看看Java带来的神奇效果!呵呵

    Java是一种广泛使用的编程语言,以其跨平台、高性能、安全性和稳定性而备受青睐。本实例集主要针对那些刚开始接触Java或者希望巩固基础的学员。它将带领你逐步探索Java的基础语法、类和对象、封装、继承、多态等核心...

    VM_Tweaker_0.12.1.21.zip

    《VM Tweaker 0.12.1.21:深度优化虚拟机的神奇工具》 VM Tweaker,一个在虚拟机领域广受好评的强大工具,版本号0.12.1.21,以其丰富的功能和高效的操作性赢得了用户的青睐。这个小巧的压缩包,虽然只有两个主要...

    randbats-winrates:神奇宝贝对决随机战斗的使用情况统计

    6. 并发处理:通过Rust的`async/await`和tokio库实现多线程或多任务并发,加快数据处理速度。 项目的源代码位于"randbats-winrates-main"目录下,包含了整个应用的实现细节。开发者可以通过阅读源码学习如何用Rust...

    MyBatis Source MyBatis源代码

    SqlSession对象是SqlSessionFactory的非线程安全的实例,每次数据库交互都应创建一个新的SqlSession。 3. **SqlSession**: SqlSession提供了与数据库进行交互的方法,如执行SQL语句、提交事务、回滚事务等。它也是...

    jvm学习,面试,性能优化等等

    而双亲委派模型是类加载器之间关系的典型设计,防止类的重复加载和保证核心类库的安全性。 垃圾收集是JVM性能优化的关键。常见的垃圾收集器有Serial、ParNew、Parallel Scavenge、CMS、G1等,它们各有优缺点,如...

    Mybatis框架

    3. **SqlSession**:每个线程都应该有自己的SqlSession实例,它是非线程安全的,用于执行数据库操作,使用完毕后必须关闭。 4. **Mapper接口**:这是用户自定义的接口,用于定义数据库操作方法。Mybatis通过动态代理...

    java 仿qq 聊天工具 mvc模式 简单易学版

    通过以上分析,我们可以看到,开发Java仿QQ聊天工具不仅需要理解MVC模式,还需要掌握网络编程、数据持久化、安全性和多线程等核心技术。这个简单的易学版项目对于初学者来说是一个很好的实践平台,可以帮助他们将...

    JAVA编程精灵

    "JAVA编程精灵"是一款专为Java初学者和资深开发者设计的实用工具,旨在提供一个安全、无毒的环境,让学习者能够进行编程练习和项目开发。这款软件可能包含了丰富的编程资源、实例代码、测试环境以及可能的调试功能,...

    Pokedex_Ocean_15_07_2020

    Kotlin的设计目标是提供一种简洁、安全且富有表现力的语法,同时兼顾可空安全性,避免空指针异常,这在Java中是一个常见的问题。Kotlin的出现,为Android开发者提供了新的选择,逐渐成为Android开发的首选语言。 在...

Global site tag (gtag.js) - Google Analytics