CAS閮戒笉浜嗚В锛屼綘杩樻€庝箞鐪婮.U.C

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CAS閮戒笉浜嗚В锛屼綘杩樻€庝箞鐪婮.U.C相关的知识,希望对你有一定的参考价值。

鏍囩锛?a href='http://www.mamicode.com/so/1/%e7%a1%ac%e4%bb%b6' title='纭欢'>纭欢   util   璁剧疆   string   exception   safe   exec   鏁版嵁   his   

鍓嶈█

璇村埌CAS锛圕ompareAndSwap锛夛紝涓嶅緱涓嶅厛璇翠竴璇?strong>鎮茶閿?/strong>鍜?strong>涔愯閿?/strong>锛屽洜涓篊AS鏄箰瑙傞攣鎬濇兂鐨勪竴绉嶅疄鐜般€?/p>

鎮茶閿?/strong>锛氭€绘槸寰堟偛瑙傜殑璁や负锛屾瘡娆℃嬁鏁版嵁閮戒細鏈夊叾浠栫嚎绋嬪苟鍙戞墽琛岋紝鎵€浠ユ瘡娆¢兘浼氳繘琛屽姞閿侊紝鐢ㄥ畬涔嬪悗閲婃斁閿侊紝鍏朵粬鐨勭嚎绋嬫墠鑳芥嬁鍒伴攣锛岃繘鑰屾嬁鍒拌祫婧愯繘琛屾搷浣溿€俲ava涓殑synchronized鍜孯eentrantLock绛夌嫭鍗犻攣灏辨槸鎮茶閿佹€濇兂鐨勫疄鐜般€?/p>

涔愯閿?/strong>锛氭€绘槸寰堜箰瑙傝涓猴紝鑷繁鎷垮埌鏁版嵁鎿嶄綔鐨勬椂鍊欙紝娌℃湁鍏朵粬绾跨▼鏉ュ苟鍙戞搷浣滐紝绛夎嚜宸辨搷浣滅粨鏉熻鏇存柊鏁版嵁鏃讹紝鍒ゆ柇鑷繁瀵规暟鎹搷浣滅殑鏈熼棿鏈夋病鏈夊叾浠栫嚎绋嬭繘琛屾搷浣滐紝濡傛灉鏈夛紝鍒欒繘琛岄噸璇曪紝鐩村埌鎿嶄綔鍙樻洿鎴愬姛銆備箰瑙傞攣甯镐娇鐢–AS鍜岀増鏈彿鏈哄埗鏉ュ疄鐜般€俲ava涓?strong>java.util.atomic鍖呬笅鐨勫師瀛愮被閮芥槸鍩轰簬CAS瀹炵幇鐨勩€?/p>

涓€銆佷粈涔堟槸CAS

CAS鎸?strong>CompareAndSwap锛岄【鍚嶆€濅箟锛?strong>鍏堟瘮杈冨悗浜ゆ崲銆傛瘮杈冧粈涔堬紵浜ゆ崲浠€涔堝憿锛?/p>

CAS涓湁涓変釜鍙橀噺锛氬唴瀛樺湴鍧€V锛屾湡寰呭€糀, 鏇存柊鍊糂銆?/p>

褰撲笖浠呭綋鍐呭瓨鍦板潃V瀵瑰簲鐨勫€间笌鏈熷緟鍊糀鏃剁浉绛夋椂锛屽皢鍐呭瓨鍦板潃V瀵瑰簲鐨勫€兼洿鎹负B銆?/strong>

浜屻€乤tomic鍖?/h3>

鏈変簡鎮茶閿侊紝涔愯閿佺殑鐭ヨ瘑锛岃鎴戜滑璧拌繘java.util.atomic鍖咃紝鐪嬩竴鐪媕ava涓瑿AS鐨勫疄鐜般€?/p>

鎶€鏈浘鐗? src=

杩欏氨鏄?strong>java.util.atomic鍖呬笅鐨勭被锛屾垜浠潃閲嶇湅AtomicInteger婧愮爜锛堝叾浠栫殑閮芥槸涓€鏍风殑鎬濇兂瀹炵幇鐨勶級

鐒跺悗鎬濊€僀AS鏈変粈涔堝紛绔紵濡備綍瑙e喅寮婄锛熸湁浠€涔堜紭缂虹偣锛?/strong>

2.1銆佽蛋杩汚tomicInteger婧愮爜

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;

    // 浣跨敤Unsafe.compareAndSwapInt杩涜鍘熷瓙鏇存柊鎿嶄綔
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    //value瀵瑰簲鐨勫瓨鍌ㄥ湴鍧€鍋忕Щ閲?    private static final long valueOffset;

    static {
        try {
            //浣跨敤鍙嶅皠鍙妘nsafe.objectFieldOffset鎷垮埌value瀛楁鐨勫唴瀛樺湴鍧€鍋忕Щ閲忥紝杩欎釜鍊兼槸鍥哄畾涓嶅彉鐨?            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    //volatile淇グ鐨勫叡浜彉閲?    private volatile int value;
    //..........
    }

涓婇潰鐨勪唬鐮佸叾瀹炲氨鏄负浜嗗垵濮嬪寲鍐呭瓨鍊煎搴旂殑鍐呭瓨鍦板潃鍋忕Щ閲弙alueOffset锛屾柟渚垮悗缁墽琛孋AS鎿嶄綔鏃朵娇鐢ㄣ€傚洜涓鸿繖涓€间竴鏃﹀垵濮嬪寲锛屽氨涓嶄細鏇存敼锛屾墍浠ヤ娇鐢╯tatic final 淇グ銆?/p>

鎴戜滑鍙互鐪嬪埌value浣跨敤浜唙olatile淇グ锛屽叾涓篃璇翠簡volatile鐨勮涔夈€?/p>

鎴戜滑閮界煡閬撳鏋滆繘琛寁alue++鎿嶄綔锛屽苟鍙戜笅鏄笉瀹夊叏鐨勩€備笂涓€绡囦腑鎴戜滑涔熼€氳繃渚嬪瓙璇佹槑浜唙olatile鍙兘淇濊瘉鍙鎬э紝涓嶈兘淇濊瘉鍘熷瓙鎬с€傚洜涓?strong>value++鏈韩涓嶆槸鍘熷瓙鎿嶄綔锛寁alue++鍒嗕簡涓夋锛屽厛鎷垮埌value鐨勫€硷紝杩涜+1锛屽啀璧嬪€煎洖value銆?/p>

2.2銆乧ompareAndSwapXxx

鎴戜滑鍏堢湅涓€鐪婣tomicInteger鎻愪緵鐨凜AS鎿嶄綔銆?/p>

    /**
     * 鍘熷瓙鍦板皢value璁剧疆涓簎pdate锛屽鏋渧alueOffset瀵瑰簲鐨勫€间笌expect鐩哥瓑鏃?     *
     * @param expect 鏈熷緟鍊?     * @param update 鏇存柊鍊?     * @return 濡傛灉鏇存柊鎴愬姛锛岃繑鍥瀟rue;鍦╲alueOffset瀵瑰簲鐨勫€间笌expect涓嶇浉绛夋椂杩斿洖false
     */
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

鎴戜滑宸茬粡鐭ラ亾CAS鐨勫師鐞嗭紝閭f潵鐪嬬湅涓嬮潰鐨勬祴璇曘€?strong>浣犵煡閬撹緭鍑虹殑缁撴灉鏄灏戝悧锛?/strong>璇勮鍖虹粰鍑轰綘鐨勭瓟妗堝惂銆?/p>

public class AtomicIntegerTest {
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger();
        atomicInteger.compareAndSet(0, 1);
        atomicInteger.compareAndSet(2, 1);
        atomicInteger.compareAndSet(1, 3);
        atomicInteger.compareAndSet(2, 4);
        System.out.println(atomicInteger.get());
    }
}

Unsafe鎻愪緵浜嗕笁涓師瀛愭洿鏂扮殑鏂规硶銆?/p>

鍏充簬Unsafe绫伙紝鍥犱负java涓嶆敮鎸佺洿鎺ユ搷浣滃簳灞傜‖浠惰祫婧愶紝濡傚垎閰嶅唴瀛樼瓑銆傚鏋滀綘浣跨敤unsafe寮€杈熺殑鍐呭瓨锛屾槸涓嶈JVM鍨冨溇鍥炴敹绠$悊锛岄渶瑕佽嚜宸辩鐞嗭紝瀹规槗閫犳垚鍐呭瓨娉勬紡绛夈€?/strong>

2.3銆丄tomicInteger鐨勫師瀛愯嚜澧炴柟娉?/h4>

鎴戜滑涓婇潰璇翠簡锛寁alue++涓嶆槸鍘熷瓙鎿嶄綔锛屼笉鑳藉湪骞跺彂涓嬩娇鐢ㄣ€傛垜浠潵鐪嬬湅AtomicInteger鎻愪緵鐨勫師瀛?+鎿嶄綔銆?/p>

    /**
     * 鍘熷瓙鍦板value杩涜+1鎿嶄綔
     *
     * @return 杩斿洖鏇存柊鍚庣殑鍊?     */
    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }

    /**
     * unsafe鎻愪緵鐨勬柟娉?     * var1 鏇存敼鐨勭洰鏍囧璞?     * var2 鐩爣瀵硅薄鐨勫叡浜瓧娈靛搴旂殑鍐呭瓨鍦板潃鍋忕Щ閲弙alueOffset
     * var4 闇€瑕佸湪鍘焩alue涓婂鍔犵殑鍊?     * @return 杩斿洖鏈洿鏂板墠鐨勫€?     */
     public final int getAndAddInt(Object var1, long var2, int var4) {
        //鏈熷緟鍊?        int var5;
        do {
            //鑾峰彇valueOffset瀵瑰簲鐨剉alue鐨勫€硷紝鏀寔volatile load
            var5 = this.getIntVolatile(var1, var2);
            //濡傛灉鍘熷瓙鏇存柊澶辫触锛屽垯涓€鐩撮噸璇曪紝鐩村埌鎴愬姛銆?        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }

鎴戜滑鐪嬪埌CAS鍙兘鍘熷瓙鐨勬洿鏂颁竴涓€硷紝濡傛灉鎴戜滑瑕佸師瀛愭洿鏂板涓€硷紝CAS鍙互鍋氬埌鍚楋紵绛旀鏄彲浠ョ殑銆?/p>

2.4銆丄tomicReference

濡傛灉瑕佸師瀛愬湴鏇存柊澶氫釜鍊硷紝灏遍渶瑕佷娇鐢?strong>AtomicReference銆傚叾浣跨敤鐨勬槸compareAndSwapObject鏂规硶銆傚彲浠ュ皢澶氫釜鍊煎皝瑁呭埌涓€涓璞′腑锛屽師瀛愬湴鏇存崲瀵硅薄鏉ュ疄鐜板師瀛愭洿鏂板涓€笺€?/p>

public class MultiValue {
    private int value1;
    private long value2;
    private Integer value3;

    public MultiValue(int value1, long value2, Integer value3) {
        this.value1 = value1;
        this.value2 = value2;
        this.value3 = value3;
    }
 }

public class AtomicReferenceTest {
    public static void main(String[] args) {
        MultiValue multiValue1 = new MultiValue(1, 1, 1);
        MultiValue multiValue2 = new MultiValue(2, 2, 2);
        MultiValue multiValue3 = new MultiValue(3, 3, 3);
        AtomicReference<MultiValue> atomicReference = new AtomicReference<>();
        //鍥犱负鏋勯€燗tomicReference鏃讹紝娌℃湁浣跨敤鏈夊弬鏋勯€犲嚱鏁帮紝鎵€浠alue榛樿鍊兼槸null
        atomicReference.compareAndSet(null, multiValue1);
        System.out.println(atomicReference.get());
        atomicReference.compareAndSet(multiValue1, multiValue2);
        System.out.println(atomicReference.get());
        atomicReference.compareAndSet(multiValue2, multiValue3);
        System.out.println(atomicReference.get());
    }
}
//杈撳嚭缁撴灉
//MultiValue{value1=1, value2=1, value3=1}
//MultiValue{value1=2, value2=2, value3=2}
//MultiValue{value1=3, value2=3, value3=3}

鎴戜滑鍐嶇湅涓€鐪婣tomicReference鐨刢ompareAndSet鏂规硶銆?/p>

娉ㄦ剰锛?strong>杩欓噷鐨勬瘮杈冮兘鏄娇鐢?=鑰岄潪equals鏂规硶銆傛墍浠ユ渶濂藉皝瑁呯殑MultiValue涓嶈鎻愪緵set鏂规硶銆?/p>

 public final boolean compareAndSet(V expect, V update) {
     return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
 }

2.5銆丆AS鐨凙BA闂

鍋囪浣犵殑璐︽埛涓婃湁100鍧楅挶锛屼綘瑕佺粰濂崇エ杞?0鍧楅挶銆?/strong>

鎴戜滑浣跨敤CAS杩涜鍘熷瓙鏇存柊璐︽埛浣欓銆傜敱浜庢煇绉嶅師鍥狅紝浣犵涓€娆$偣鍑昏浆璐﹀嚭鐜伴敊璇紝浣犱互涓烘病鏈夊彂璧疯浆璐﹁姹傦紝杩欐椂鍊欎綘鍙堢偣鍑讳簡涓€娆°€傜郴缁熷紑鍚簡涓や釜绾跨▼杩涜杞处鎿嶄綔锛岀涓€涓嚎绋嬭繘琛孋AS姣旇緝锛屽彂鐜颁綘鐨勮处鎴蜂笂棰勬湡鏄?00鍧楅挶锛屽疄闄呬篃鏈?00鍧楅挶锛岃繖鏃跺€欒浆璧颁簡50锛岄渶瑕佽缃负100 - 50 = 50 鍏冿紝杩欐椂璐︽埛浣欓涓?0

绗竴涓嚎绋嬫搷浣滄垚鍔熶簡锛岀浜屼釜绾跨▼鐢变簬鏌愮鍘熷洜闃诲浣忎簡锛涜繖鏃跺€欙紝浣犵殑瀹朵汉鍙堢粰浣犺浆浜?0鍧楅挶锛屽苟涓旇浆璐︽垚鍔熴€傞偅浣犺处鎴蜂笂鐜板湪鍙堟槸100鍧楅挶锛?/strong>

澶阀浜嗭紝绗簩涓嚎绋嬭鍞ら啋浜嗭紝鍙戠幇浣犵殑璐︽埛鏄?00鍧楅挶锛岃窡棰勬湡鐨?00鏄浉绛夌殑锛岃繖鏃跺€欏張CAS涓?0銆傚ぇ鍏勫紵锛屽摥鎯ㄤ簡锛屼綘绠楃畻锛屾纭殑鍦烘櫙浣犺鏈夊灏戦挶锛?/strong>杩欏氨鏄疌AS瀛樺湪鐨凙BA闂銆?/p>

public class AtomicIntegerABA {

    private static AtomicInteger atomicInteger = new AtomicInteger(100);

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        //绾跨▼1
        executorService.execute(() -> {
            System.out.println(Thread.currentThread().getName() + " - " + atomicInteger.get());
            atomicInteger.compareAndSet(100, 50);
            System.out.println(Thread.currentThread().getName() + " - " + atomicInteger.get());
        });

        //绾跨▼2
        executorService.execute(() -> {
            try {
                TimeUnit.MILLISECONDS.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " - " + atomicInteger.get());
            atomicInteger.compareAndSet(50, 100);
            System.out.println(Thread.currentThread().getName() + " - " + atomicInteger.get());
        });

        //绾跨▼3
        executorService.execute(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " - " + atomicInteger.get());
            atomicInteger.compareAndSet(100, 50);
            System.out.println(Thread.currentThread().getName() + " - " + atomicInteger.get());
        });

        executorService.shutdown();
    }
}
//杈撳嚭缁撴灉
//pool-1-thread-1 - 100
//pool-1-thread-1 - 50
//pool-1-thread-2 - 50
//pool-1-thread-2 - 100
//pool-1-thread-3 - 100
//pool-1-thread-3 - 50

澶у蹇冩兂锛岄潬锛岃繖涓嶆槸鍧戝悧锛熼偅杩樼敤銆傘€傘€傘€傘€傘€傘€傘€傘€傘€傘€傘€傘€傘€傚喎闈欙紝鍐烽潤銆備綘鑳芥兂鍒扮殑闂锛宩dk閮借兘鎯冲埌銆俛tomic鍖呮彁渚涗簡涓€涓?strong>AtomicStampedReference

2.6銆丄tomicStampedReference

鐪嬪悕瀛楁槸涓嶆槸璺烝tomicReference寰堝儚鍟婏紝鍏跺疄灏辨槸鍦ˋtomicReference涓?strong>鍔犱笂浜嗕竴涓増鏈彿锛屾瘡娆℃搷浣滈兘瀵圭増鏈彿杩涜鑷锛岄偅姣忔CAS涓嶄粎瑕佹瘮杈僾alue锛岃繕瑕佹瘮杈僺tamp锛屽綋涓斾粎褰撲袱鑰呴兘鐩哥瓑锛屾墠鑳藉杩涜鏇存柊銆?/strong>

public AtomicStampedReference(V initialRef, int initialStamp) {
        pair = Pair.of(initialRef, initialStamp);
    }
//瀹氫箟浜嗗唴閮ㄩ潤鎬佸唴閮ㄧ被Pair锛屽皢鏋勯€犲嚱鏁板垵濮嬪寲鐨勫€间笌鐗堟湰鍙锋瀯閫犱竴涓狿air瀵硅薄銆?private static class Pair<T> {
        final T reference;
        final int stamp;
        private Pair(T reference, int stamp) {
            this.reference = reference;
            this.stamp = stamp;
        }
        static <T> Pair<T> of(T reference, int stamp) {
            return new Pair<T>(reference, stamp);
        }
    }

//鎵€浠ユ垜浠箣鍓嶇殑value灏卞搴斾负鐜板湪鐨刾air
    private volatile Pair<V> pair;

璁╂垜浠潵鐪嬩竴鐪嬪畠鐨凜AS鏂规硶銆?/p>

 public boolean compareAndSet(V   expectedReference,
                                 V   newReference,
                                 int expectedStamp,
                                 int newStamp) {
        Pair<V> current = pair;
        return
            //鍙湁鍦ㄦ棫鍊间笌鏃х増鏈彿閮界浉鍚岀殑鏃跺€欐墠浼氭洿鏂颁负鏂板€硷紝鏂扮増鏈彿
            expectedReference == current.reference &&
            expectedStamp == current.stamp &&
            ((newReference == current.reference &&
              newStamp == current.stamp) ||
             casPair(current, Pair.of(newReference, newStamp)));
    }
private boolean casPair(Pair<V> cmp, Pair<V> val) {
        return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
    }

杩樻槸涓婇潰杞处鐨勪緥瀛愶紝鎴戜滑浣跨敤AtomicStampedReference鏉ョ湅鐪嬫槸鍚﹁В鍐充簡鍛€?/p>

public class AtomicStampedReferenceABA {
    /**
     * 鍒濆鍖栬处鎴蜂腑鏈?00鍧楅挶锛岀増鏈彿瀵瑰簲0
     */
    private static AtomicStampedReference<Integer> atomicInteger = new AtomicStampedReference<>(100, 0);

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        int[] result = new int[1];
        //绾跨▼1
        executorService.execute(() -> {
            System.out.println(Thread.currentThread().getName() + " - " + atomicInteger.get(result));
            //灏?00鏇存柊涓?0锛岀増鏈彿+1
            atomicInteger.compareAndSet(100, 50, 0, 1);
            System.out.println(Thread.currentThread().getName() + " - " + atomicInteger.get(result));
        });

        //绾跨▼2
        executorService.execute(() -> {
            try {
                TimeUnit.MILLISECONDS.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " - " + atomicInteger.get(result));
            //灏?0鏇存柊涓?00锛岀増鏈彿+1
            atomicInteger.compareAndSet(50, 100, 1, 2);
            System.out.println(Thread.currentThread().getName() + " - " + atomicInteger.get(result));
        });

        //绾跨▼3
        executorService.execute(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " - " + atomicInteger.get(result));
            //姝ょ嚎绋嬭繕鏄互涓烘病鏈夊叾浠栫嚎绋嬭繘琛岃繃鏇存敼锛屾墍浠ユ棫鐗堟湰鍙疯繕鏄?
            atomicInteger.compareAndSet(100, 50, 0, 1);
            System.out.println(Thread.currentThread().getName() + " - " + atomicInteger.get(result));
        });

        executorService.shutdown();
    }
}
//杈撳嚭缁撴灉
//pool-1-thread-1 - 100
//pool-1-thread-1 - 50
//pool-1-thread-2 - 50
//pool-1-thread-2 - 100
//pool-1-thread-3 - 100
//pool-1-thread-3 - 100

濡堝鍐嶄篃涓嶇敤鎷呭績鎴戠殑閽卞皯浜嗐€?/p>

涓夈€佹€荤粨

鏈瘒璇︾粏璁茶В浜咰AS鐨勫師鐞嗭紝CAS鍙互杩涜鍘熷瓙鏇存柊涓€涓€硷紙鍖呮嫭瀵硅薄锛夛紝涓昏鐢ㄤ簬璇诲鍐欏皯鐨勫満鏅紝濡傚師瀛愯嚜澧炴搷浣滐紝濡傛灉澶氱嚎绋嬭皟鐢紝鍦–AS澶辫触涔嬪悗锛屼細姝诲惊鐜竴鐩撮噸璇曪紝鐩村埌鏇存柊鎴愬姛銆傝繖绉嶆儏鍐垫槸寰堣€桟PU璧勬簮鐨勶紝铏界劧娌℃湁閿侊紝浣嗗惊鐜殑鑷棆鍙兘姣旈攣鐨勪唬浠疯繕楂樸€傚悓鏃跺瓨鍦ˋBA闂锛屼絾AtomicStampedReference閫氳繃鍔犲叆鐗堟湰鍙锋満鍒跺凡缁忚В鍐炽€傚叾瀹炲浜巃tomic鍖咃紝jdk1.8鏂板鐨?strong>LongAdder锛屾晥鐜囨瘮AtomicLong楂橈紝9榫欒繕鏈秹瓒筹紝浠ュ悗鑲畾浼氬搧涓€鍝併€侸.U.C锛坖ava.util.concurrent锛夊寘涓ぇ閲忎娇鐢ㄤ簡CAS锛孋oncurrentHashMap涔熶娇鐢ㄥ埌锛屽鏋滀笉浜嗚ВCAS锛屾€庝箞鍏ユ墜J.U.C鍛€?/p>

以上是关于CAS閮戒笉浜嗚В锛屼綘杩樻€庝箞鐪婮.U.C的主要内容,如果未能解决你的问题,请参考以下文章

UI銆佸師鍨嬩笌瀹為檯涓嶇锛涚爺鍙戞€间綘锛屼綘瑕佹€庝箞鍔烇紵-Dotest杞欢娴嬭瘯

Vim淇偧绉樼睄涔嬭娉曠瘒

棰樺璇濓細2020 骞磋鎬庝箞鐢?Intellij IDEA 鎼缓 Haskell 鐜

闈㈣瘯涓殑缃戠孩铏氭嫙DOM锛屼綘鐭ュ灏戝憿锛熸繁鍏ヨВ璇籨iff绠楁硶