0
点赞
收藏
分享

微信扫一扫

3.对象的共享

小a草 2022-05-06 阅读 93
java

文章目录

对象的共享

3.1可见性

/**
 * 主线程和读线程都将访问共享变量ready和number
 */
public class NoVisibility {
    private static boolean ready;
    private static int number;

	/**
	 * 读线程直到ready的值变为true,然后输出number的值。
	 */
    private static class ReaderThread extends Thread {
        @Override
        public void run() {
            while (!ready) {
                Thread.yield();
            }

			// 看起来会输出42,但事实上可能输出0,或者上面的循环根本无法终止
            System.out.println(number);
        }
    }

	/**
	 * 主线程启动读线程,然后设置number和ready的值
	 */
    public static void main(String[] args) {
        new ReaderThread().start();
        number = 42;
        ready = true;
    }
}

3.1.1失效数据

/**
 * 因为get和set都是在没有同步的情况下访问value的,失效值问题更容易出现。
 */
@NotThreadSafe
public class MutableInteger {
    private int value;

    public int get() {
        return value;
    }

    public void set(int value) {
        this.value = value;
    }
}
@ThreadSafe
public class SynchronizedInteger {
    @GuardedBy("this")
    private int value;

	// 仅对set方法进行同步是不够的,调用get的线程仍然会看见失效值
    public synchronized int get() {
        return value;
    }

    public synchronized void set(int value) {
        this.value = value;
    }
}

3.1.2非原子的64位操作

3.1.3加锁与可见性

在这里插入图片描述

3.1.4volatile变量

/**
 * 通过类似于数绵羊的方法进入休眠状态,为了使示例能正确执行,asleep必须为volatile变量。否则,
 * 当asleep被另一个线程修改时,执行判断的线程却发现不了。也可以用锁来确保asleep更新操作的可见性,
 * 但这将使代码变得更加复杂。
 */
volatile boolean asleep;

// 其他操作

	// 检查某个状态标记以判断是否退出循环
	while (!asleep) {
		countSomeSheep();
	}

3.2发布与逸出

public static Set<Secret> knownSecrets;

public void initialize() {
	knownSecrets = new HashSet<>();
}
/**
 * 发布了本应为私有的状态数组,可能会出现问题,因为任何调用者都能修改这个数组的内容。
 */
class UnsafeStates {
	private String[] states = new String[] {
		"AK", "AL", ...
	};

	public String[] getStates() {
		return states;
	}
}
/**
 * 当ThisEscape发布EventListener时,也隐含地发布了ThisEscape实例本身,因为在这个内部类的实例中包含了
 * 对ThisEscape实例的隐含引用。
 */
public class ThisEscape {
	public ThisEscape(EventSource source) {
		source.registerListener(new EventListener() {
			public void onEvent(Event e) {
				doSomething(e);
			}
		});

		// ThisEscape类实例的其他初始化操作
	}
}

安全的对象构造过程

public class SafeListener {
	private final EventListener listener;

	private SafeListener() {
		listener = new EventListener() {
			public void onEvent(Event e) {
				doSomething(e);
			}
		};
	}

	public static SafeListener newInstance(EventSource source) {
		SafeListener safe = new SafeListener();
		source.registerListener(safe.listener);
		return safe;
	}
}

3.3线程封闭

3.3.1Ad-hoc线程封闭

3.3.2栈封闭

public int loadTheArk(Collection<Animal> candidates) {
	SortedSet<Animal> animals;
	int numPairs = 0;
	Animal candidate = null;

	// animals被封闭在方法中,不要使它们逸出
	animals = new TreeSet<>(new SpeciesGenderComparator());
	animals.addAll(candidates);

	for (Animal a : animals) {
		if (candidate == null || !candidate.isPotentialMate(a)) {
			candidate = a;
		} else {
			ark.load(new AnimalPair(candidate, a));
			++numPairs;
			candidate = null;
		}
	}

	return numPairs;
}

3.3.3ThreadLocal

private static ThreadLocal<Connection> connectionHolder = new ThreadLocal() {
	public Connection initialValue() {
		return DriverManager.getConnection(DB_URL);
	}
};

public static Connection getConnection() {
	return connectHolder.get();
}

3.4不变性

/**
 * 尽管Set是可变的,但其构造完成之后就无法对其进行修改。
 */
@Immutable
public final class ThreeStooges {
	private final Set<String> stooges = new HashSet<>();

	public ThreeStooges() {
		stooges.add("Moe");
		stooges.add("Larry");
		stooges.add("Curly");
	}

	public boolean isStooge(String name) {
		return stooges.contains(name);
	}
}

3.4.1final

3.4.2示例:使用volatile类型来发布不可变对象

@Immutable
class OneValueCache {
	private final BigInteger lastNumber;
	private final BigInteger[] lastFactors;

	public OneValueCache(BigInteger i, BigInteger[] factors) {
		lastNumber = i;
		lastFactors = Arrays.copyOf(factors, factors.length);
	}

	public BigInteger[] getFactors(BigInteger i) {
		if (lastNumber == null || !lastNumber.equals(i)) {
			return null;
		} else {
			return Arrays.copyOf(lastFactors, lastFactors.length);
		}
	}
}
/**
 * 与cache相关的操作不会相互干扰,因为OneValueCache是不可变的,并且在每条相应的代码路径中只会访问它一次。
 * 通过使用包含多个状态变量的容器对象来维持不变性条件,并使用一个volatile类型的引用来确保可见性,使得
 * VolatileCachedFactorizer在没有显式地使用锁的情况下仍然是线程安全的。
 */
@ThreadSafe
public class VolatileCachedFactorizer extends HttpServlet {
	private volatile OneValueCache cache = new OneValueCache(null, null);

	public void service(ServletRequest req, ServletResponse resp) {
		BigInteger i = extractFromRequest(req);
		BigInteger[] factors = cache.getFactors(i);

		if (factors == null) {
			factors = factor(i);
			cache = new OneValueCache(i, factors);
		}

		encodeIntoResponse(resp, factors);
	}
}

3.5安全发布

// 不安全的发布
public Holder holder;

public void initialize() {
	holder = new Holder(42);
}

3.5.1不正确的发布:正确的对象被破坏

public class Holder {
	private int n;

	public Holder(int n) {
		this.n = n;
	}

	public void assertSanity() {
		if (n != n) {
			throw new AssertionError("This statement is false.");
		}
	}
}

3.5.2不可变对象与初始化安全性

3.5.3安全发布的常用模式

public static Holder holder = new Holder(42);

3.5.4事实不可变对象

3.5.5可变对象

3.5.6安全地共享对象

举报

相关推荐

0 条评论