哲学家进餐及Java中的IntegerCache

哲学家进餐及Java中的IntegerCache

  • 哲学家进餐问题
    • 问题描述
    • 代码实现
    • 实现思路
  • Java中的IntegerCache
    • 起因
    • 结论
    • 原因探索
      • 自动装箱
      • IntegerCache

哲学家进餐问题

问题描述

五个哲学家竞争五只筷子,只有同时强到两只筷子才能吃饭,吃完后释放筷子,然后思考,思考完了继续抢筷子吃饭...大致如此

代码实现

public class PhilosopherEating {
	
	private static Integer[] chopsticks = {1, 2, 3, 4, 5};

	public static void main(String[] args) {
		for (int i = 0; i < chopsticks.length; i++) {
			new Philosopher(i).start();
		}
	}
	
	static class Philosopher extends Thread {
		private int order;
		
		public Philosopher(int order) {
			this.order = order;
		}
		
		@Override
		public void run() {
			deadLock();
//			liveLock2();
		}
		
		public void liveLock2() {
			while(true) {
				boolean f = order % 5 < (order + 1) % 5;
				if (f) {
					synchronized (chopsticks[order % 5]) {
						System.out.println("p" + order + ": got left chopstic");
						sleepWithoutTryCatch(3000);
						synchronized (chopsticks[(order + 1) % 5]) {
							System.out.println("p" + order + ": got right chopstic");
							System.out.println("p" + order + ": eating...");
							sleepWithoutTryCatch(5000);
						}
					}
					System.out.println("p" + order + ": thinking...");
					sleepWithoutTryCatch(5000);
				} else {
					synchronized (chopsticks[(order + 1) % 5]) {
						System.out.println("p" + order + ": got right chopstic");
						sleepWithoutTryCatch(3000);
						synchronized (chopsticks[order % 5]) {
							System.out.println("p" + order + ": got left chopstic");
							System.out.println("p" + order + ": eating...");
							sleepWithoutTryCatch(5000);
						}
					}
					System.out.println("p" + order + ": thinking...");
					sleepWithoutTryCatch(5000);
				}
			}
		}
		
		public void liveLock1() {
			while(true) {
				synchronized (chopsticks) {
					System.out.println("p" + order + ": got left chopstic");
					sleepWithoutTryCatch(3000);
					System.out.println("p" + order + ": got right chopstic");
					System.out.println("p" + order + ": eating...");
					sleepWithoutTryCatch(5000);
				}
				System.out.println("p" + order + ": thinking...");
				sleepWithoutTryCatch(5000);
			}
		}
		
		public void deadLock() {
			while(true) {
				synchronized (chopsticks[order % 5]) {
					System.out.println("p" + order + ": got left chopstic");
					sleepWithoutTryCatch(3000);
					synchronized (chopsticks[(order + 1) % 5]) {
						System.out.println("p" + order + ": got right chopstic");
						System.out.println("p" + order + ": eating...");
						sleepWithoutTryCatch(5000);
					}
				}
				System.out.println("p" + order + ": thinking...");
				sleepWithoutTryCatch(5000);
			}
		}
		
		void sleepWithoutTryCatch(long millis) {
			try {
				sleep(millis);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

实现思路

  • 发生死锁的情况
    就五个人同时取左边的筷子,取到后休眠一会儿,再去取右边的筷子,这样五个人都取到了左边的筷子,再取右边的时候就没了,于是每个人拿一只筷子,等待他人释放另一只筷子,就发生了死锁
  • 不发生死锁的情况
    前四个人取左边的筷子,第五个人取右边的筷子,这样就会有一个人取不到筷子,有一个人可以取到两只筷子,就可以进餐了,进餐完毕释放筷子,其他就可以循环进餐了,就不会死锁了

Java中的IntegerCache

起因

private static Integer[] chopsticks = {1, 2, 3, 4, 5};

把上面代码中的数组改为{1, 1, 1, 1, 1}
发现死锁没了,哲学家们一个接一个的进餐
于是有了一点猜测,又试了一次把数组改为{1, 1, 3, 4, 5}
就发现有四个哲学家同时拿到了一只筷子,紧接着有一个哲学家又拿到了一只,然后循环进餐

结论

数组中相同的1是同一个对象,两个线程使用synchronized无法同时获取这个对象的锁,自然就造成了同时只有四个哲学家拿到筷子的现象

原因探索

自动装箱

Integer i = 1;

我们这样的一段代码在javac(jdk1.5及之后)编译之时会有一个自动装箱的过程,编译后的代码相当于这样的

Integer i = Integer.valueOf(1);

IntegerCache

为什么Integer.valueOf(1)产生的是同一个对象,进源码看一下发现

// Integer.class
static final int low = -128;
static final int high;
static final Integer cache[];
public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}
// Integer.$IntegerCache.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;
    }

从以上源码不难发现,默认情况下在[-128~127]区间内,Integer.valueOf(i)返回的都是同一个对象

你可能感兴趣的:(Java基础,Java,多线程,哲学家进餐,IntegerCache)