-
(1) 访问共享的可变数据时, 通常就需要__同步__
(2) 为了避免同步, 可以不共享数据, 在__单线程内__访问拘束的话就不需要共享数据, 这就叫__线程封闭__
-
线程封闭的方式
(1) Ad-hoc封闭: 完全交给程序实现者承担
尽量少用这种方式
(2) 栈封闭: 只能通过__局部变量__访问对象
示例
public class Animals { ... public int loadTheArk(Collection<Animal> candidates) { SortedSet<Animal> animals; int numPairs = 0; Animal candidate = null; // animals confined to method, don't let them escape! animals = new TreeSet<Animal>(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; } }
这个示例中, 输入参数 candidates 是共享的数据, 而栈封闭的意思是访问对象只能通过局部变量, 因此示例中使用局部变量animals作为candidates的副本,所有的访问都发生在局部变量animals上, 这样根本就不会共享数据。
使用栈封闭的风险是: 需求必须明确, 否则很有可能被后续的维护人员错误将对象逸出(例如示例中将candidates或者animals传到了其他线程中)
(2) 使用ThreadLocal类
1° JavaDoc说明
This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its {@code get} or {@code set} method) has its own, independently initialized copy of the variable. {@code ThreadLocal} instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).
For example, the class below generates unique identifiers local to each thread. A thread’s id is assigned the first time it invokes {@code ThreadId.get()} and remains unchanged on subsequent calls.
class ThreadId { // Atomic integer containing the next thread ID to be assigned private static final AtomicInteger nextId = new AtomicInteger(0); // Thread local variable containing each thread's ID private static final ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() { @Override protected Integer initialValue() { return nextId.getAndIncrement(); } }; // Returns the current thread's unique ID, assigning it if necessary public static int get() { return threadId.get(); } }
Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the {@code ThreadLocal} instance is accessible; after a thread goes away, all of its copies of thread-local instances are subject to garbage collection (unless other references to these copies exist).
2° 从概念上看, ThreadLocal相当于内置了Map<Thread, T>, 其中保存了特定于当前线程的值
3° 如果需要将一个单线程应用程序移植到多线程环境中,可以将共享的变量转换为ThreadLocal对象
4° 示例
public class ConnectionDispenser { static String DB_URL = "jdbc:mysql://localhost/mydatabase"; private ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() { @Override protected Connection initialValue() { try { return DriverManager.getConnection(DB_URL); } catch (SQLException e) { throw new RuntimeException("Unable to acquire Connection, e"); } } }; public Connection getConnection() { return connectionHolder.get(); } }
这样, 每个线程都会拥有属于自己的Connection对象副本, 它们互不干扰
-
Singleton与ThreadLocal结合使用的示例
class Singleton { private Singleton() { } private static ThreadLocal<Singleton> threadLocal = new ThreadLocal<Singleton>() { @Override protected Singleton initialValue() { return new Singleton(); } }; public static Singleton getInstance() { return threadLocal.get(); } } class ThreadLocalTest extends Thread { public void run() { Singleton instance1 = Singleton.getInstance(); System.out.println(getThreadName() + instance1); sleep(100, 50); // sleep for some time Singleton instance2 = Singleton.getInstance(); System.out.println(getThreadName() + instance2); boolean equal = (instance1 == instance2); String message = equal ? "Both are equal" : "Not equal"; System.out.println(getThreadName() + message); } private void sleep(int max, int min) { try { int time = new Random().nextInt(max - min + 1) + min; Thread.sleep(time); } catch (InterruptedException e) { e.printStackTrace(); } } private String getThreadName() { return "[" + Thread.currentThread().getName() + "] - "; } } class App { public static void main(String[] args) { Thread t1 = new ThreadLocalTest(); t1.start(); Thread t2 = new ThreadLocalTest(); t2.start(); Thread t3 = new ThreadLocalTest(); t3.start(); try { t1.join(); t2.join(); t3.join(); } catch (InterruptedException e) { e.printStackTrace(); } } }
输出结果
[Thread-2] - Singleton@67ab632 [Thread-1] - Singleton@42ae5a83 [Thread-0] - Singleton@483e4ed4 [Thread-1] - Singleton@42ae5a83 [Thread-1] - Both are equal [Thread-2] - Singleton@67ab632 [Thread-2] - Both are equal [Thread-0] - Singleton@483e4ed4 [Thread-0] - Both are equal
每个线程保存同一个Singleton副本,不同线程保存不同的Singleton副本
chapter03_对象的共享_3_线程封闭
最新推荐文章于 2023-03-29 00:06:44 发布