zoukankan      html  css  js  c++  java
  • 线程安全和共享资源

    能够同时被多个线程安全地调用的代码,就是线程安全。如果一段代码是线程安全的,就说明它没有竞态。竞态只会在多线程更新共享资源的时候出现。所以,知道 Java 在执行的时候是共享了什么资源是很重要的。

    局部变量

    局部变量存储在每个线程自己的栈中。也就是说局部变量不会再多个线程中共享。也意味着本地的基本类型的变量是线程安全的。

    public void someMethod() {
      	long threadSafeInt = 0;
      	threadSafeInt++;
    }
    

    局部对象引用

    局部对象引用有些不同。尽管引用没有被共享,但是引用指向的对象不是存储在线程的栈中,而是存放在共享堆中。

    如果一个对象没有从创建该对象的方法逃离,它就是线程安全的。实际上,你可以把这个对象传递给其他方法或者对象使用,只要这个对象没有被传递到其他线程就行。

    下面是一个线程安全的局部对象的例子

    public void someMethod() {
      	LocalObject localObject = new LocalObject();
      	localObject.callMethod();
      	method2(localObject);
    }
    
    public void method2(LocalObject localObject) {
      	localObject。setValue("value");
    }
    

    对象的成员变量

    对象的成员变量和对象一起存放在堆中。因此,如果两个县城调用了同一个对象中的方法,并且该方法改变了成员变量的值,那么这个方法就不是线程安全的。

    下面例子中的方法不是线程安全的

    public class NotThreadSafe {
      	StringBuilder builder = new StringBuilder();
      
      	public add(String text) {
          	this.builder.append(text);
      	}
    }
    

    如果两个线程同时调用了同一个NotThreadSafe实例add() 方法,就会导致竞态。比如

    NotThreadSafe sharedInstance = new NotThreadSafe();
    
    new Thread(new MyRunnable(shareInstance)).start();
    new Thread(new MyRunnable(shareInstance)).start();
    
    public class MyRunnable implements Runnable {
      	NotThreadSafe instance = null;
      
      	public MyRunnable(NotThreadSafe instance) {
          	this.instance = instance;
      	}
      
      	public void run() {
          	this.instance.add("some text");
      	}
    }
    

    可以看到,两个线程共享的是同一个 NotThreadSafe 实例,因此当它们调用 add() 方法时,就导致了竞态。

    如果两个线程调用的是不同的实例的话,即使这两个线程是同时调用,就不会产生竞态了,因此上面的代码可以这么修改

    new Thread(new MyRunnable(new NotThreadSafe())).start();
    new Thread(new MyRunnable(new NotThreadSafe())).start();
    

    所以,即使一个对象不是线程安全的,也可以换一种方法来避免竞态。

    线程控制逃逸规则

    如果你想判断你的代码访问资源的时候是否是线程安全的,可以借助县城控制逃逸规则:

    如果资源的创建、使用和销毁都是在同一个线程内,并且从不脱离线程的控制。那么对该资源的使用就是线程安全的。
    

    不过,即使对象的使用时线程安全的,但是如果对象指向的是一个共享资源,比如文件或者数据库,那么你的应用整体上来说就不是线程安全了。比如线程1和线程2都创建了一个数据库连接:connection 1 和 connection 2。connection 1 和 connection 2 的使用时线程安全的,但是connection 1 和 connection 2 指向的数据库也许不是线程安全的。比如两个线程都执行如下操作

    检查记录 X 是否存在
    如果不存在,就插入记录 X
    

    如果两个线程同时执行,就有能出现同时插入的隐患

    线程 1 检查记录 X 是否存在:不存在
    线程 2 检查记录 X 是否存在:不存在
    线程 1 插入记录 X
    线程 2 插入记录 X
    

    这种情况也有可能发生在线程操作文件或者其他共享资源的时候。所以,区分对象代表的是资源本身,还是指向资源的引用(比如数据库连接这样的对象)。

  • 相关阅读:
    iOS多线程_06_GCD其它用法
    iOS多线程_05_线程间通信NSThread/GCD
    iOS多线程_04_GCD
    iOS多线程_03_Block
    iOS多线程_02_多线程的安全问题
    iOS多线程_01_简介和NSThread
    shell 根据端口号输出所有的pid
    【java核心36讲】接口和抽象类的区别
    CSS布局
    CSS基础
  • 原文地址:https://www.cnblogs.com/okadanana/p/5894638.html
Copyright © 2011-2022 走看看