首页 >Java >java教程 >Java 线程全和共享资源

Java 线程全和共享资源

黄舟
黄舟原创
2017-02-28 10:37:291620浏览

代码被多线程同时的调用是安全的称之为线程安全。如果一段代码是线程安全的,那么它就不包含竞态条件。竞态条件只是发生在多线程更新共享资源的时候。因此知道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中文网(www.php.cn)!


声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn