ThreadLocal,静态变量,实例变量,局部变量的线程安全
之前都是业务层次开发,现在公司进行的网络编程,一下子要了解太多java底层的东西并进行应用,我现在边学习边应用。由于知识能力有限,在上次发博客时出现了一个小小的纰漏,而这个纰漏被细心的博友发现了。
首先感谢你的关注,其次非常感谢你的建议和批评。其实上次博客中说道要线程安全的取得缓冲变量确实有安全取得某变量的意思,不过那个例子只是一个讲解Socket应用的小示例。如果真的要保证变量安全,使用静态变量,这好像有点不正常了。
其实这一下子就围绕在了一个话题上面,那就是变量的线程安全性。现在就一个个来说。
首先要肯定的是除了ThreadLocal和局部变量安全以外,静态和实例变量都是不安全的。
首先来看静态变量:
- package com;
- /**
- * @说明 变量安全测试
- * @author 崔素强
- */
- public class ThreadLocalTest {
- public static void main(String[] args) {
- Runnable accumelatora = new Accumulatort();
- Thread threada = new Thread(accumelatora, "ThreadA");
- Thread threadb = new Thread(accumelatora, "ThreadB");
- threada.start();
- threadb.start();
- }
- }
- class Accumulatort implements Runnable {
- // 静态变量
- private static int local = 0;
- @SuppressWarnings("unchecked")
- public void run() {
- // 静态变量
- for (int i = 0; i <= 10; i++) {
- local += 1;
- try {
- Thread.sleep(500);
- } catch (Exception e) {
- }
- System.out.println(Thread.currentThread().getName() + "-->"
- + local);
- }
- }
- }
运行后看控制台输出,很容就发现有时候某线程使用变量时已经被另一个线程修改了。
因为静态变量是 静态存储方式,所谓静态存储方式是指在程序运行期间分配固定的存储空间的方式。也就是说不管多少线程,访问都是一个变量,安全问题显而易见。
再说说实例变量:
- package com;
- /**
- * @说明 变量安全测试
- * @author 崔素强
- */
- public class ThreadLocalTest {
- public static void main(String[] args) {
- Runnable accumelatora = new Accumulatort();
- Thread threada = new Thread(accumelatora, "ThreadA");
- Thread threadb = new Thread(accumelatora, "ThreadB");
- threada.start();
- threadb.start();
- }
- }
- class Accumulatort implements Runnable {
- // 实例变量
- int locals = 0;
- @SuppressWarnings("unchecked")
- public void run() {
- for (int i = 0; i <= 10; i++) {
- locals += 1;
- try {
- Thread.sleep(1000);
- } catch (Exception e) {
- }
- System.out.println(Thread.currentThread().getName() + "-->"
- + locals);
- }
- }
- }
也许你觉得这会安全,但是运行后安全问题你会马上发现。
实例变量为对象实例私有,在java虚拟机的堆中分配,如果在系统中只存在一个此对象的实例,在多线程环境下,就像静态变量那样,被某个线程修改后,其他线程对修改均可见,故线程非安全;如果每个线程执行都是在不同的对象中,那对象与对象之间的实例变量的修改将互不影响,所以线程安全。 而上面我们虽然是两个线程,但是对象却是一个,所以不是你想想中的安全了。
局部变量:
- package com;
- /**
- * @说明 变量安全测试
- * @author 崔素强
- */
- public class ThreadLocalTest {
- public static void main(String[] args) {
- Runnable accumelatora = new Accumulatort();
- Thread threada = new Thread(accumelatora, "ThreadA");
- Thread threadb = new Thread(accumelatora, "ThreadB");
- threada.start();
- threadb.start();
- }
- }
- class Accumulatort implements Runnable {
- @SuppressWarnings("unchecked")
- public void run() {
- // 局部变量
- int locals = 0;
- for (int i = 0; i <= 5; i++) {
- locals += 1;
- try {
- Thread.sleep(1000);
- } catch (Exception e) {
- }
- System.out.println(Thread.currentThread().getName() + "-->"
- + locals);
- }
- }
- }
不行你就多运行几遍,没事的,线程安全。
每个线程执行时将会把局部变量放在各自栈帧的工作内存中,线程间不共享,所以没有安全问题。
一般多线程编程时最会想到的是ThreadLocal:
ThreadLocal使用场合主要解决多线程中数据数据因并发产生不一致问题。ThreadLocal为每个线程的中并发访问的数据提供一个副本,通过访问副本来运行业务,这样的结果是耗费了内存,单大大减少了线程同步所带来性能消耗,也减少了线程并发控制的复杂度。
该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。
- package com;
- /**
- * @说明 变量安全测试
- * @author 崔素强
- */
- public class ThreadLocalTest {
- // 线程安全变量
- @SuppressWarnings("unchecked")
- public static ThreadLocal threadLocal = new ThreadLocal();
- public static void main(String[] args) {
- Runnable accumelatora = new Accumulatort();
- Thread threada = new Thread(accumelatora, "ThreadA");
- Thread threadb = new Thread(accumelatora, "ThreadB");
- threada.start();
- threadb.start();
- }
- }
- class Accumulatort implements Runnable {
- @SuppressWarnings("unchecked")
- public void run() {
- // 测试线程安全
- ThreadLocal threadLocal = ThreadLocalTest.threadLocal;
- for (int i = 1; i <= 10; i++) {
- if (threadLocal.get() == null)
- threadLocal.set(new Integer(0));
- int x = ((Integer) threadLocal.get()).intValue();
- x += 1;
- threadLocal.set(new Integer(x));
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- }
- System.out.println(Thread.currentThread().getName() + "-->"
- + ((Integer) threadLocal.get()).intValue());
- }
- }
- }
上面的代码其实每个线程都会有自己的变量副本,所以也不会有安全问题的。
至于它和synchronized的区别,虽然都是为了线程安全,但是却又本质的区别。
synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。当然ThreadLocal并不能替代synchronized,它们处理不同的问题域。synchronized是用来处理多线程环境下的数据同步,而ThreadLocal只是为了保存当前线程私有的某种状态。
ThreadLocal,静态变量,实例变量,局部变量的线程安全的更多相关文章
- Java父类与子类中静态代码块 实例代码块 静态变量 实例变量 构造函数执行顺序
实例化子类时,父类与子类中的静态代码块.实例代码块.静态变量.实例变量.构造函数的执行顺序是怎样的? 代码执行的优先级为: firest:静态部分 second:实例化过程 详细顺序为: 1.父类静态 ...
- 3.ruby语法基础,全部变量,实例变量,类变量,局部变量的使用和注意的要点
1.ruby的全局变量的概念和Java的全局变量的概念不同, ruby的全局变量是以$符号开头的,如果给全局变量的初始化值为nil会出现警告. 赋值给全局变量,这是ruby不推荐的,这样会使程序变得很 ...
- iOS中的成员变量,实例变量,属性变量
在ios第一版中: 我们为输出口同时声明了属性和底层实例变量,那时,属性是oc语言的一个新的机制,并且要求你必须声明与之对应的实例变量,例如: 注意:(这个是以前的用法) @interface MyV ...
- java入门---变量类型&类变量&局部变量&实例变量&静态变量
在Java语言中,所有的变量在使用前必须声明.声明变量的基本格式如下: type identifier [ = value][, identifier [= value] ...] ; ...
- JAVA类与对象(六)------实例变量与类变量的区别,实例方法和类方法的区别
实例变量 实例变量声明在一个类中,但在方法.构造方法和语句块之外: 当一个对象被实例化之后,每个实例变量的值就跟着确定: 实例变量在对象创建的时候创建,在对象被销毁的时候销毁: 实例变量的值应该至少被 ...
- JAVA类与对象---实例变量与类变量的区别,实例方法和类方法的区别
实例变量 实例变量声明在一个类中,但在方法.构造方法和语句块之外: 当一个对象被实例化之后,每个实例变量的值就跟着确定: 实例变量在对象创建的时候创建,在对象被销毁的时候销毁: 实例变量的值应该至少被 ...
- JAVA中 成员变量和和实例变量区别
java语言支持的变量类型 类变量:独立于方法之外的变量,用 static 修饰. 局部变量:类的方法中的变量. 实例变量(全局变量):独立于方法之外的变量,不过没有 static 修饰. publi ...
- python(四)类变量和实例变量
转载自[1] 实际这是个实例变量是否指向类变量的问题. python的类变量和实例变量,顾名思义,类变量是指跟类的变量,而实例变量,指跟类的具体实例相关联的变量,具体体现为self.x 等.实际要注意 ...
- 变量、变量作用域、常量final、变量的命名规范
变量 变量是什么:就是可以变化的量! Java是一种强类型语言,每个变量都必须声明其类型. Java变量是程序中最基本的存储单元,其要素包括变量名,变量类型和作用域. 注意事项: 每个变量都有类型,类 ...
随机推荐
- opencv 图像各方向旋转
1. 简介 计算机图形学中的应用非常广泛的变换是一种称为仿射变换的特殊变换,在仿射变换中的基本变换包括平移.旋转.缩放.剪切这几种.本文以及接下来的几篇文章重点介绍一下关于旋转的变换,包括二维旋转变换 ...
- python包与模块
Python基础-包与模块 摘要 为重用以及更好的维护代码,Python使用了模块与包:一个Python文件就是一个模块,包是组织模块的特殊目录(包含__init__.py文件). 模块搜索路径,Py ...
- Eclipse安装和使用TFS
第一步下载Tfs插件 去微软官网下载https://www.microsoft.com/en-us/download/details.aspx?id=4240 点击 选择下载 随便放置到一个本地或者服 ...
- 水图(牛客练习赛(DFS搜索))
题意: 小w不会离散数学,所以她van的图论游戏是送分的 小w有一张n个点n-1条边的无向联通图,每个点编号为1~n,每条边都有一个长度小w现在在点x上她想知道从点x出发经过每个点至少一次,最少需要走 ...
- 使用官方组件下载图片,保存到MySQL数据库,保存到MongoDB数据库
需要学习的地方,使用官方组件下载图片的用法,保存item到MySQL数据库 需要提前创建好MySQL数据库,根据item.py文件中的字段信息创建相应的数据表 1.items.py文件 from sc ...
- C#学习笔记_08_面向对象
08_面向对象 面向对象:一种看待问题解决问题的思维方式,着眼点在于找到一个能够帮助我们解决问题的实体,然后委托这个实体来帮我们解决问题:(在面向对象之前你要有一个女朋友,否则代码会经常出现bug) ...
- 多层gmetad配置
经实验表明: ①多层gmetad与ganglia版本无关,且可以多版本兼容 ②多层gmetad只有最底层gmetad能保存详细指标,非底层gmetad收集到的都只能是summary信息,当然也许我配置 ...
- Linux系统自带服务罗列
/ect/services 文件列出了系统详细的服务 红色字体为常用服务 acpid ACPI(全称 Advanced Configuration and Power Interface)服务是电源管 ...
- 【ACM】NYOJ_288_Time_20130725
Time时间限制:1000 ms | 内存限制:65535 KB 难度:2描述 Digital clock use 4 digits to express time, each digit is ...
- C - 剪花布条
一块花布条,里面有些图案,另有一块直接可用的小饰条,里面也有一些图案.对于给定的花布条和小饰条,计算一下能从花布条中尽可能剪出几块小饰条来呢? Input输入中含有一些数据,分别是成对出现的花布条和 ...