<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                ## 構造方法非線程安全 當你在腦子里想象一個對象構造的過程,你會很容易認為這個過程是線程安全的。畢竟,在對象初始化完成前對外不可見,所以又怎會對此產生爭議呢?確實,[Java 語言規范](https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.8.3) (JLS)自信滿滿地陳述道:“*沒必要使構造器的線程同步,因為它會鎖定正在構造的對象,直到構造器完成初始化后才對其他線程可見。*” 不幸的是,對象的構造過程如其他操作一樣,也會受到共享內存并發問題的影響,只是作用機制可能更微妙罷了。 設想下使用一個 **static** 字段為每個對象自動創建唯一標識符的過程。為了測試其不同的實現過程,我們從一個接口開始。代碼示例: ```java //concurrent/HasID.java public interface HasID { int getID(); } ``` 然后 **StaticIDField** 類顯式地實現該接口。代碼示例: ```java // concurrent/StaticIDField.java public class StaticIDField implements HasID { private static int counter = 0; private int id = counter++; public int getID() { return id; } } ``` 如你所想,該類是個簡單無害的類,它甚至都沒一個顯式的構造器來引發問題。當我們運行多個用于創建此類對象的線程時,究竟會發生什么?為了搞清楚這點,我們做了以下測試。代碼示例: ```java // concurrent/IDChecker.java import java.util.*; import java.util.function.*; import java.util.stream.*; import java.util.concurrent.*; import com.google.common.collect.Sets; public class IDChecker { public static final int SIZE = 100_000; static class MakeObjects implements Supplier<List<Integer>> { private Supplier<HasID> gen; MakeObjects(Supplier<HasID> gen) { this.gen = gen; } @Override public List<Integer> get() { return Stream.generate(gen) .limit(SIZE) .map(HasID::getID) .collect(Collectors.toList()); } } public static void test(Supplier<HasID> gen) { CompletableFuture<List<Integer>> groupA = CompletableFuture.supplyAsync(new MakeObjects(gen)), groupB = CompletableFuture.supplyAsync(new MakeObjects(gen)); groupA.thenAcceptBoth(groupB, (a, b) -> { System.out.println( Sets.intersection( Sets.newHashSet(a), Sets.newHashSet(b)).size()); }).join(); } } ``` **MakeObjects** 類是一個生產者類,包含一個能夠產生 List\<Integer> 類型的列表對象的 `get()` 方法。通過從每個 `HasID` 對象提取 `ID` 并放入列表中來生成這個列表對象,而 `test()` 方法則創建了兩個并行的 **CompletableFuture** 對象,用于運行 **MakeObjects** 生產者類,然后獲取運行結果。 使用 Guava 庫中的 **Sets.`intersection()` 方法,計算出這兩個返回的 List\<Integer> 對象中有多少相同的 `ID`(使用谷歌 Guava 庫里的方法比使用官方的 `retainAll()` 方法速度快得多)。 現在我們可以測試上面的 **StaticIDField** 類了。代碼示例: ```java // concurrent/TestStaticIDField.java public class TestStaticIDField { public static void main(String[] args) { IDChecker.test(StaticIDField::new); } } ``` 輸出結果: ``` 13287 ``` 結果中出現了很多重復項。很顯然,純靜態 `int` 用于構造過程并不是線程安全的。讓我們使用 **AtomicInteger** 來使其變為線程安全的。代碼示例: ```java // concurrent/GuardedIDField.java import java.util.concurrent.atomic.*; public class GuardedIDField implements HasID { private static AtomicInteger counter = new AtomicInteger(); private int id = counter.getAndIncrement(); public int getID() { return id; } public static void main(String[] args) { IDChecker.test(GuardedIDField::new); } } ``` 輸出結果: ``` 0 ``` 構造器有一種更微妙的狀態共享方式:通過構造器參數: ```java // concurrent/SharedConstructorArgument.java import java.util.concurrent.atomic.*; interface SharedArg{ int get(); } class Unsafe implements SharedArg{ private int i = 0; public int get(){ return i++; } } class Safe implements SharedArg{ private static AtomicInteger counter = new AtomicInteger(); public int get(){ return counter.getAndIncrement(); } } class SharedUser implements HasID{ private final int id; SharedUser(SharedArg sa){ id = sa.get(); } @Override public int getID(){ return id; } } public class SharedConstructorArgument{ public static void main(String[] args){ Unsafe unsafe = new Unsafe(); IDChecker.test(() -> new SharedUser(unsafe)); Safe safe = new Safe(); IDChecker.test(() -> new SharedUser(safe)); } } ``` 輸出結果: ``` 24838 0 ``` 在這里,**SharedUser** 構造器實際上共享了相同的參數。即使 **SharedUser** 以完全無害且合理的方式使用其自己的參數,其構造器的調用方式也會引起沖突。**SharedUser** 甚至不知道它是以這種方式調用的,更不必說控制它了。 同步構造器并不被java語言所支持,但是通過使用同步語塊來創建你自己的同步構造器是可能的(請參閱附錄:[并發底層原理](./Appendix-Low-Level-Concurrency.md),來進一步了解同步關鍵字—— `synchronized`)。盡管JLS(java語言規范)這樣陳述道:“……它會鎖定正在構造的對象”,但這并不是真的——構造器實際上只是一個靜態方法,因此同步構造器實際上會鎖定該類的Class對象。我們可以通過創建自己的靜態對象并鎖定它,來達到與同步構造器相同的效果: ```java // concurrent/SynchronizedConstructor.java import java.util.concurrent.atomic.*; class SyncConstructor implements HasID{ private final int id; private static Object constructorLock = new Object(); SyncConstructor(SharedArg sa){ synchronized (constructorLock){ id = sa.get(); } } @Override public int getID(){ return id; } } public class SynchronizedConstructor{ public static void main(String[] args){ Unsafe unsafe = new Unsafe(); IDChecker.test(() -> new SyncConstructor(unsafe)); } } ``` 輸出結果: ``` 0 ``` **Unsafe**類的共享使用現在就變得安全了。另一種方法是將構造器設為私有(因此可以防止繼承),并提供一個靜態Factory方法來生成新對象: ```java // concurrent/SynchronizedFactory.java import java.util.concurrent.atomic.*; final class SyncFactory implements HasID{ private final int id; private SyncFactory(SharedArg sa){ id = sa.get(); } @Override public int getID(){ return id; } public static synchronized SyncFactory factory(SharedArg sa){ return new SyncFactory(sa); } } public class SynchronizedFactory{ public static void main(String[] args){ Unsafe unsafe = new Unsafe(); IDChecker.test(() -> SyncFactory.factory(unsafe)); } } ``` 輸出結果: ``` 0 ``` 通過同步靜態工廠方法,可以在構造過程中鎖定 **Class** 對象。 這些示例充分表明了在并發Java程序中檢測和管理共享狀態有多困難。即使你采取“不共享任何內容”的策略,也很容易產生意外的共享事件。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看