• 技术文章 >Java >java教程

    Java 线程全和共享资源

    黄舟黄舟2017-02-28 10:37:29原创656
    代码被多线程同时的调用是安全的称之为线程安全。如果一段代码是线程安全的,那么它就不包含竞态条件。竞态条件只是发生在多线程更新共享资源的时候。因此知道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");
    }


    在这个例子中的LocalObject实例不能从这个方法中返回,也不能传递给其他的对象。那个从someMethod方法的外部是可以访问的。每一个执行someMethod方法的线程将会创建它自己的LocalObject实例,以及把它分配给localObject引用。因此这种使用是线程安全的。

    事实上,这整个someMethod方法是线程安全的。甚至如果这个localObject实例被作为参数传递给相同类的其他方法,或者其他的类,它是线程安全的使用。

    当然,唯一的异常,如果这些方法中的一个使用LocalObject作为参数调用,在某种程度上存储LocalObject实例,允许来自其他线程的访问。

    对象成员变量

    对象成员变量(字段)连同对象一起存储在堆上。因此,如果两个线程调用相同对象实例的一个方法,以及这个方法更新对象成员变量,整个方法就不是线程安全的。这里有一个例子:


    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(sharedInstance)).start();
    new Thread(new MyRunnable(sharedInstance)).start();
    
    public class MyRunnable implements Runnable{
      NotThreadSafe instance = null;
    
      public MyRunnable(NotThreadSafe instance){
        this.instance = instance;
      }
    
      public void run(){
        this.instance.add("some text");
      }
    }


    注意这两个MyRunnable实例是怎么样分享相同的NotThreadSafe实例的。因此,当他们调用add方法的时候会导致竞态条件。


    然而,如果两个线程在不同的实例上同时的调用add方法,它就不会导致竞态条件。这里是来自于之前的例子,稍微有些修改:


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

    现在每一个线程都有一个他们自己的NotThreadSafe实例了,以至于他们调用这个add方法不会互相干扰。这个代码不带有竞态条件。以至于,甚至一个对象不是线程安全的,它仍然可以用这种方式使用不会导致竞态条件。

    线程控制溢出规则

    当尝试决定是否你的代码对某个资源访问是线程安全的时候,你可以使用下列规则:


    If a resource is created, used and disposed within
    the control of the same thread,
    and never escapes the control of this thread,
    the use of that resource is thread safe.


    资源可以是任何共享的资源,像一个对象,数组,文件,数据库连接,套接字等等。在Java中,你不能总是明确的销毁对象,以至于“销毁的”意味着对象丢失或者空的引用。


    甚至如果一个对象的使用是线程安全的,如果那个对象指向了一个共享的资源像一个文件或者数据库,你的应用作为一个整体可能就不是线程安全的了。例如,如果线程1和线程2各自创建他们自己的数据库连接,连接1和连接2,每一个他们自己的连接使用是线程安全的。但是这个连接指向的数据库的使用可能就不是线程安全的。例如,如果两个线程执行像这样的代码:


    check if record X exists
    if not, insert record X


    如果两个线程同时执行这个,以及他们正在检查的这个记录X放生在相同的记录上,这里就有一个风险,他们都会最终已插入而结束。如下所示:

    Thread 1 checks if record X exists. Result = no
    Thread 2 checks if record X exists. Result = no
    Thread 1 inserts record X
    Thread 2 inserts record X


    这个也会发生在操作在文件或者其他的共享资源的线程上。因此去区分被一个线程控制的对象是否是一个资源,或者仅仅是这个资源(像数据库连接)的引用是重要的。

    以上就是Java 线程全和共享资源的内容,更多相关内容请关注PHP中文网(m.sbmmt.com)!

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
    专题推荐:Java 线程 共享资源
    上一篇:Java 竞态条件和临界段 下一篇:java 线程安全和不可变性
    Web大前端开发直播班

    相关文章推荐

    • 带你搞懂Java的接口(实例详解)• Java技巧总结之如何看Lambda源码• 实例详解JAVA抽象工厂模式• 详细了解一下Java并发编程三要素• 一起聊聊Java多线程之线程安全问题

    全部评论我要评论

  • 取消发布评论发送
  • 1/1

    PHP中文网