我这份代码里面,多个线程,要抢占的临界资源是Integer,可是经过测试,这并不会导致并发问题。代码如下
package com.vecheer.deadlock_test;
//自定义线程类
class ResourcesConsumer extends Thread{
private static Integer resourceA = 100;
private static Integer resourceB = 100;
//模拟资源的拥有情况
private int resourceGet;
public ResourcesConsumer(int resourceGet) {
this.resourceGet = resourceGet;
}
//模拟死锁的情况
@Override
public void run() {
if (resourceGet == 1){
synchronized (resourceA){
System.out.println(Thread.currentThread().getName() + "-->已获得资源A");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (resourceB){
System.out.println(Thread.currentThread().getName() + "-->已获得资源B");
}
}
}
else{
synchronized (resourceB){
System.out.println(Thread.currentThread().getName() + "-->已获得资源B");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (resourceA){
System.out.println(Thread.currentThread().getName() + "-->已获得资源A");
}
}
}
}
}
public class Deadlock_Test {
public static void main(String[] args) {
new Thread(new ResourcesConsumer(1),"线程 1 ").start();
new Thread(new ResourcesConsumer(2),"线程 2 ").start();
}
}
经过测试,并没有导致并发问题。下面是输出结果:
线程 1 -->已获得资源A
线程 1 -->已获得资源B
线程 2 -->已获得资源B
线程 2 -->已获得资源A
但是如果我把Integer改成自定义的一个类,则会导致死锁!!!求大家指教!
因为Integer在-128到127范围会使用方法区缓存,所以resourceA,resourceB实际上指向的是同一个对象,也就只有一把锁,线程1拿到锁之后,线程2只能等待线程1执行完(每个线程都拿了2次锁)。如果将Integer定义超出缓存范围,此时才会发生死锁
这个不能测试,必须按照文档来。因为线程不安全,不等于每次都能测试出来。有时候概率会非常小。
你不能用Integer,因为文档上说它不是线程安全的。应该用AtomicInteger
5、6 两行变更为以下代码,则 发生死锁
private static Integer resourceA = new Integer(100);
private static Integer resourceB = new Integer(100);
至于原因,详细阅读 JDK 源码已经说得很明白了:
/**
* Returns an {@code Integer} instance representing the specified
* {@code int} value. If a new {@code Integer} instance is not
* required, this method should generally be used in preference to
* the constructor {@link #Integer(int)}, as this method is likely
* to yield significantly better space and time performance by
* caching frequently requested values.
*
* This method will always cache values in the range -128 to 127,
* inclusive, and may cache other values outside of this range.
*
* @param i an {@code int} value.
* @return an {@code Integer} instance representing {@code i}.
* @since 1.5
*/
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
/**
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage. The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
*/
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}