使用线程安全的单个实例变量

本文通过一个具体的Java示例探讨了线程安全问题。演示了当两个线程同时访问同一对象的实例变量时可能出现的问题,并展示了如何使用synchronized关键字来确保线程安全。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Created by csj on 2017/3/24.
 *
非线程安全存在于实例变量,,方法内部的私有变量不存在非线程安全问题, 永远都是线程安全的,这都是方法内部的变量是私有的特性造成的。
 *
如果多个线程共同访问1个对象中的实例变量,有可能出现"非线程安全"问题,这个实验是两个线程同时访问一个没有同步的方法,导致最后两个num=200
 *
 */
public class HasSelfPrivateNum {
    Logger logger = LoggerFactory.getLogger(HasSelfPrivateNum.class);
    int num =0;
    synchronized public void addName(String name){
        try{

            if(name.equals("a")){
                num=100;
                logger.info("a set over!");
                Thread.sleep(2000);

            }else{
                num = 200;
                logger.info("b set over");
            }
            logger.info("name={}, num={}",name,num);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}



/**
 * Created by csj on 2017/3/25.
 */
public class ThreadA extends Thread {

    private HasSelfPrivateNum num;

    public ThreadA(HasSelfPrivateNum num) {
        super();
        this.num = num;
    }

    @Override
    public void run() {
        super.run();
        num.addName("a");
    }
}



/**
 * Created by csj on 2017/3/25.
 */
public class ThreadB extends Thread {

    private HasSelfPrivateNum num;

    public ThreadB(HasSelfPrivateNum num) {
        super();
        this.num = num;
    }

    @Override
    public void run() {
        super.run();
        num.addName("b");
    }
}


/**
 * Created by csj on 2017/3/25.
 *
 */
public class Run {

    public static void main(String[] args){
        HasSelfPrivateNum num=new HasSelfPrivateNum();
        ThreadA threadA = new ThreadA(num);
        threadA.start();
        ThreadB threadB=new ThreadB(num);
        threadB.start();
    }

}



2017-03-26 10:23:37.893 [Thread-0] INFO  HasSelfPrivateNum - a set over!
2017-03-26 10:23:39.903 [Thread-0] INFO  HasSelfPrivateNum - name=a, num=100
2017-03-26 10:23:39.907 [Thread-1] INFO  HasSelfPrivateNum - b set over
2017-03-26 10:23:39.907 [Thread-1] INFO  HasSelfPrivateNum - name=b, num=200


根据日志显示:Thread-0 对addName() 访问结束(这个过程包含线程沉睡2s)后Thread-1才可以访问 addName(),这个是使用了synchronized 的效果, 

倘若没有使用的synchronized,建议把 int num =0;放到addName() 中,这样也是线程安全的;如果既没有使用synchronized,又没有把 int num =0;放到addName() 中,最后两个线程的 num 都会是200,

### Java 多线程中实例变量的行为 在Java多线程环境中,实例变量可以在不同线程间共享或保持私有状态。当多个线程操作同一个对象的实例变量时,可能会引发数据竞争和一致性问题。 #### 实例变量共享机制 对于非静态成员变量(即实例变量),它们属于创建该对象的具体线程所拥有。然而,在实际应用中,这些变量可能被传递给其他线程从而变成共享资源[^3]。为了确保这类情况下的安全性,开发者应当采取适当措施防止并发冲突: - **同步控制**:利用`synchronized`关键字或者显式的锁(`Lock`)来协调对共享资源的操作顺序,保证同一时刻只有一个线程能够修改特定的数据结构。 ```java public class Counter { private int count = 0; // 使用 synchronized 关键字的方法级锁定 public synchronized void increment() { this.count++; } public synchronized int getCount(){ return this.count; } } ``` - **不可变性设计**:使对象一旦初始化之后便不再改变其内部状态,则无需担心由并发带来的副作用。例如声明为final类型的字段以及采用Immutable模式构建的对象都是天然线程安全的选择。 - **ThreadLocal存储**:有时为了避免复杂的加锁逻辑,可以选择让每个线程都持有自己独有的副本而非全局唯一的那份数据拷贝。此时就可以借助于`ThreadLocal<T>`容器达成目的。 ```java public class UserSession { static final ThreadLocal<String> userContext = new ThreadLocal<>(); public String getUserInfo(){ return userContext.get(); } public void setUserInfo(String info){ userContext.set(info); } } ``` 上述代码展示了两种不同的策略用于处理潜在的竞争状况——一种是对公共区域施加访问权限约束;另一种则是彻底隔离各个参与者之间的联系使之互不影响。 #### 验证实例变量是否真正实现了跨线程共享 下面给出一段简单的测试程序验证两个独立运行的工作单元确实能感知到对方所做的更改: ```java class Task implements Runnable{ private volatile boolean flag=true; @Override public void run(){ while(flag){/*do something*/} } public void stopTask(){this.flag=false;} } // 主函数部分... Task task=new Task(); new Thread(task).start(); // 启动第一个工作线程 try{Thread.sleep(10);}catch(Exception e){} task.stopTask();// 修改标志位通知停止循环 System.out.println("Main thread finished."); ``` 这里定义了一个名为`flag`布尔型实例属性用来充当终止信号量的角色。由于它不是static修饰符限定的域,理论上讲每次新建一个`Task`实体都会获得一份新的默认初始值true。但是考虑到此处只存在单个这样的组件实例并将其提交给了另一个后台进程去执行,所以实际上二者之间还是形成了关联关系。最终主线程调用了`stopTask()`方法后会使得子进程中while语句里的判断条件变为false进而退出无限等待的状态。 综上所述,Java 中的实例变量能否在线程间共享取决于具体的应用场景和技术方案的设计选择。如果不加以妥善管理的话很容易造成难以预料的结果,因此建议开发人员充分理解相关概念并且遵循最佳实践指导方针来进行编码作业。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值