美文网首页
线程安全

线程安全

作者: 思而忧 | 来源:发表于2017-09-21 01:47 被阅读0次

在多线程程序中,如果多个线程同时对同一个对象进行读写,由于读写操作会在内存中先复制一份缓存数据,修改完缓存数据后再用缓存数据覆盖原来的数据,所以写操作不会马上影响到原始数据,这时候其他线程读取到的数据就是旧的数据(也成为“脏数据”),这个数据的读写就是线程不安全了。在数据库里面所谓的“脏读”也是类似的原理。

例子程序:

class UnSafeClass{
    // 线程不安全的类
    private int a = 0;

    public void add(){
        a+=1;
        System.out.println("UnSafeClass: " + Thread.currentThread().getName() + ":a is " + a);
    }

}

class SafeClass{
    // 线程安全的类
    private int a = 0;
    public void add(){
        synchronized (this) {
            a++;
            System.out.println("SafeClass: " + Thread.currentThread().getName() + ":a is " + a);
        }
    }
}


class TestUnSafeThread implements Runnable{

    private static UnSafeClass unSafeClass = new UnSafeClass();

    @Override
    public void run(){
        for(int i=0; i<10; i++) {
            try {
                unSafeClass.add();
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.out.println(e);
            }
        }
    }
}

class TestSafeThread implements Runnable{

    private static SafeClass safeClass = new SafeClass();

    @Override
    public void run() {
        for(int i=0;i<10;i++) {
            try {
                safeClass.add();
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.out.println(e);
            }
        }
    }
}


public class SafeThreadTest{


    public static void main(String[] args){
        System.out.println("SafeThreadTest-------------------");


        Thread[] t1 = new Thread[3];

        for(int i=0;i<t1.length;i++){
            t1[i] = new Thread(new TestUnSafeThread());
            t1[i].start();
        }

        Thread[] t2 = new Thread[3];
        for(int i=0;i<t2.length;i++){
            t2[i] = new Thread(new TestSafeThread());
            t2[i].start();
        }
    }
}

程序输出结果

输出

由结果可以看出来,线程不安全的类读写存在脏读的情况(不一定每次都会出现),而线程安全的类(即加锁)读取没有存在脏读的情况。

对象的无状态与有状态

  • 有状态就是有数据存储功能。有状态对象(Stateful Bean),就是有实例变量的对象,可以保存数据,是非线程安全的。在不同方法调用间不保留任何状态。

  • 无状态就是一次操作,不能保存数据。无状态对象(Stateless Bean),就是没有实例变量的对象.不能保存数据,是不变类,是线程安全的。

// 无状态对象
class StatelessClass {
   public void method(){
      //do something
      ....
   }
}

// 有状态对象
class stateClass{
    private int count = 0;  // 对象的状态变量

    public void method(){
      count ++;  // 对象的变量变化,在多线程的时候不安全。
  }
}

利用无状态的技术有单例模式,这样可以共享实例,提高性能。有状态的Bean,多线程环境下不安全,那么适合用Prototype原型模式。Prototype: 每次对bean的请求都会创建一个新的bean实例。 有状态的bean都使用prototype作用域,而对无状态的bean则应该使用singleton作用域。

关于volatile

对于非volatile类型的long和double变量,jvm允许将64位的读操作或写操作分解为两个32位的操作。当读取一个非volatile类型的long变量是,如果对改变量的读操作和写操作在不同的线程中执行,那么很可能会读取到某个值的高32位和另一个值的低32位。因此,即使不考虑失效数据问题(就是读取到了“脏数据”),在多线程程序中使用共享且可变的long和double等类型的变量也是不安全的,除非用关键字volatile来声明或者用锁保护起来。

当把变量声明为volatile类型后,编译器与运行的虚拟机都会注意到这个变量是共享的,因此不会讲改变量上的操作与其他内存操作一起重排序。在访问volatile变量是不会执行加锁操作,因此也就不会是执行线程阻塞,所以volatile变量是一个种比sychronized关键字更轻量级的同步机制。

volatile变量一般用于某个状态标记变量,volatile变量不能确保数据的原子性,只能确保可见性(内存可见性)

相关文章

  • atomic & nonatomic

    什么是线程安全??? 线程安全:多线程操作共享数据不会出现想不到的结果就是线程安全的,否则,是线程不安全的。 at...

  • ConcurrentHashMap源码设计分析

    二、线程安全(Thread-safe)的集合对象:● Vector 线程安全● HashTable 线程安全● S...

  • HashMap 和 Hashtable 的区别

    线程安全: HashMap 是非线程安全的,而 Hashtable 是线程安全的,因为 Hashtable 内部的...

  • Java 的 StringBuffer 和 StringBuil

    区别就是:线程安全,StringBuffer 是线程安全的,StringBuilder 不是线程安全的。 他俩的实...

  • Java单例模式,线程安全

    懒汉式:线程安全,开销大 双重检查锁:线程安全,根据需求使用 静态内部类锁:线程安全,比较推荐 饿汗式:线程安全,...

  • 2018-06-12 第三十七天

    一、线程安全 线程安全的问题,是针对多线程的程序。单线程的情况下,是不存在线程安全问题。 产生线程安全问题的原因:...

  • 线程安全知多少

    1. 如何定义线程安全 线程安全,拆开来看: 线程:指多线程的应用场景下。 安全:指数据安全。 多线程就不用过多介...

  • JAVA 线程安全

    线程安全定义 一个类在可以被多个线程安全调用时就是线程安全的。 线程安全分类 线程安全不是一个非真即假的命题,可以...

  • synchronized锁

    一、线程安全的概念与synchronized 1、线程安全概念 并发程序开发的一大关注重点就是线程安全,线程安全就...

  • 线程安全的NSMutableDictionary

    NSDictionary是线程安全的,NSMutableDictionary是线程不安全的。利用锁来保证线程的安全...

网友评论

      本文标题:线程安全

      本文链接:https://www.haomeiwen.com/subject/dyiasxtx.html