java进阶(七)------多线程---多线程操作同一变量



情况

首先要肯定的是ThreadLocal和局部变量是线程安全的,静态和实例变量都是不安全的。


我们常常在系统中会用一些 静态变量 作为 共同的状态标记。


但在多线程中常常发现这个 变量的增减 会出现错乱  并不是预期中的结果显示。


例如:

package test.autorun;


public class ShareVar {
	private static int nCount=0;
	
	public  int getnCount() {
		return nCount;
	}
	public  void setnCount(int nCount) {
		ShareVar.nCount = nCount;
	}
	

}





原因

内存机制中的  "副本"概念 

多个线程访问一个成员变量时  每个线程都会得到一个该变量的副本  在自己的线程的栈中保存、计算 以提高速度。  但是这样就会有同步的问题了。   当一个线程修改了自己栈内副本的值  还没有立即将同步到主存中, 其他线程再来获取主存中的该变量时  就会得到过期数据。 




解决办法

为了解决这种问题 可以使用synchronized对该变量的操作同步 , 或使用volatile关键字声明该变量为易变对象  这样的话 每个线程就不会创建副本到自己的栈中  而是直接操作主存。


volatile

在对象/变量前加上 volatile 。 Volatile修饰的 成员变量 在每次被 线程 访问时,都强迫从 共享内存 中重读该成员变量的值。而且,当 成员变量 发生变化时,强迫线程将变化值回写到 共享内存 。这样在任何时刻,两个不同的线程总是看到某个 成员变量 的同一个值。 Java语言 规范中指出:为了获得最佳速度,允许线程保存共享 成员变量 的私有拷贝,而且只当线程进入或者离开 同步代码块 时才与共享成员变量的原始值对比。这样当多个线程同时与某个对象交互时,就必须要注意到要让线程及时的得到共享 成员变量 的变化。而volatile 关键字 就是提示JVM:对于这个 成员变量 不能保存它的私有拷贝,而应直接与共享成员变量交互。使用建议:在两个或者更多的线程访问的 成员变量 上使用volatile。当要访问的 变量 已在synchronized代码块中,或者为 常量 时,不必使用。由于使用volatile屏蔽掉了JVM中必要的 代码优化 ,所以在效率上比较低,因此一定在必要时才使用此 关键字 。

package test.autorun;


public class ShareVar {
	private volatile static int nCount=0;
	
	public  int getnCount() {
		return nCount;
	}
	public  void setnCount(int nCount) {
		ShareVar.nCount = nCount;
	}
	

}




 synchronized

将对象/变量加上锁 synchronized 修饰。在线程中,使用同步方法或者同步块。

package test.autorun;


public class ShareVar {
	private  static int nCount=0;
	
	public  int getnCount() {
		return nCount;
	}
	public  synchronized void setnCount(int nCount) {
		ShareVar.nCount = nCount;
	}
	

}


TimerTask

使用带有线程安全的线程。如:继承 TimerTask 类实现线程,用 Timer.schedule 启动线程   。





选择

但synchronized同步方法和块的话 损耗资源和性能是较大的,它会在该方法和块中阻塞变成类似于单线程来运作,所有优先考虑volatile






  

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
Java多线程编程中,线程同步是一个非常重要的概念。它用来确保多个线程在访问共享资源时不会产生冲突或竞争条件。在Java中,有几种方式可以实现线程同步,其中之一就是使用synchronized关键字。 synchronized关键字可以用于修饰代码块或方法。当我们使用synchronized修饰代码块时,需要指定一个对象作为锁对象。在代码块内部,只有获取了锁对象的线程才能执行代码块中的内容,其他线程则需要等待。 在给定的示例中,synchronized关键字被用于修饰insert方法,这意味着同一时刻只能有一个线程能够执行这个方法。这样就确保了对num变量的访问是安全的,不会出现竞争条件导致数据不一致的情况。 具体来说,当一个线程进入synchronized修饰的insert方法时,它会获取到insertData对象的锁,并执行方法内部的代码。而其他线程则需要等待,直到当前线程释放了锁。这样就保证了对num变量操作是线程安全的。 需要注意的是,synchronized关键字只能保证同一时刻只有一个线程能够执行被修饰的代码块或方法,但并不能保证线程的执行顺序。所以在多线程编程中,我们还需要考虑到线程的调度和执行顺序的不确定性。 除了synchronized关键字,Java还提供了其他的线程同步机制,比如Lock接口,它提供了更灵活和细粒度的线程同步控制。但是在大部分情况下,synchronized关键字已经能够满足我们的需求,使用它来实现线程同步是一种简单而有效的方式。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [Java高级特性 - 多线程基础(3)线程同步](https://blog.csdn.net/weixin_52034200/article/details/130253687)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [EduCoder Java高级特性 - 多线程基础(3)线程同步](https://blog.csdn.net/weixin_45981481/article/details/114494972)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张小凡vip

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值