【併發】10、當有多個線程設置對應的值的時候,讀取的值是否是那個線程設置的值?

當有多個線程設置對應的值的時候,讀取的值是否是那個線程設置的值???
如果我們單獨對這個值上鎖的話,情況會怎麼樣呢?
volatile Integer a = 0;

    /**
     * 當有多個線程設置對應的值的時候,讀取的值是否是那個線程設置的值???
     * 事實證明,單獨上鎖,並不能保證數據的唯一性,因爲在部分線程在進行設置值的時候,我們並沒有設置讀鎖
     * 也就是說,這些線程讀取的是之前的值,而不是線程阻塞設置的值
     * 那麼這種情況要怎麼才能保證讀寫一致呢??讀寫鎖。。。。
     */
    @Test
    public void test1() {

        //設置柵欄,保證同時啓動
        CyclicBarrier cyclicBarrier = new CyclicBarrier(5);
        for (int i = 1; i < 6; ++i) {
            //每個線程獲取並設置值
            final int tmp = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
//                    System.out.println(Thread.currentThread().getName() + "準備:" + System.currentTimeMillis());
                    try {
                        cyclicBarrier.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
//                    System.out.println(Thread.currentThread().getName() + "開始:" + System.currentTimeMillis());
                    while (true) {
                        synchronized (a) {
//                            if (tmp == 5) {
//                                try {
//                                    System.out.println(Thread.currentThread().getName() + "等待設置值:" + System.currentTimeMillis());
//                                    Thread.sleep(4000);
//                                } catch (InterruptedException e) {
//                                    e.printStackTrace();
//                                }
//                            }

                            a = tmp;
                            System.out.println(Thread.currentThread().getName() + "設置a值:" + a + "---" + System.currentTimeMillis());
                            try {
                                Thread.sleep(1000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
//                        try {
//                            Thread.sleep(2000);
//                        } catch (InterruptedException e) {
//                            e.printStackTrace();
//                        }
                        System.out.println(Thread.currentThread().getName() + "-read a =:" + a);
                    }
                }
            }).start();
        }

        while (true) {

            int j = 1;
        }
    }

 

結果顯示:

 

 我們發現讀取的a值雜亂無章,並不能保證是這個線程設置之後的值,爲什麼爲這樣呢???

因爲我們只對a進行上鎖的話,那麼在對a進行設置值的時候,其他線程可以繼續讀取a的值,當a的值設置完畢之後,其他線程讀取的值,我們也不知道是讀取那個線程設置的值

如果我們把a設置爲數據庫的值的話,我們會發現,不通的人請求拿到的結果居然不是一樣的

很簡單舉個例子:

A系統請求,需要獲取某個地址信息的時候,B系統正在修改地址信息,那麼在B修改完成之後,這個值還是可以讀取的,只是不能被C系統修改而已

但是如果出現網絡波動,或者業務邏輯比較複雜,那麼就會導致B還沒有修改完畢,A就把之前的舊數據讀取過去了,這樣就導致A系統用了一個錯的地址信息,

結果就是地址錯了,業務也就做錯地方了,那麼緊接着是不是就應該是投訴了!!!

 

那麼這種情況要怎麼才能保證讀寫一致呢??

讀寫鎖,在讀的時候也要設置鎖,保證B系統在修改數據的時候,其他系統無法讀取

 

/**
     * 這樣就完全一致了。。。。
     * 但是這個鎖會導致效率極低,具體情境使用,看來還是要分情況啊
     */
    @Test
    public void test2() {

        final ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();

        //設置柵欄,保證同時啓動
        CyclicBarrier cyclicBarrier = new CyclicBarrier(5);
        for (int i = 1; i < 6; ++i) {
            //每個線程獲取並設置值
            final int tmp = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        cyclicBarrier.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                    while (true) {
                        //這裏對變量a上寫鎖
                        reentrantReadWriteLock.writeLock().lock();
                        reentrantReadWriteLock.readLock().lock();
                        a = tmp;
                        System.out.println(Thread.currentThread().getName() + "設置a值:" + a + "---" + System.currentTimeMillis());
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        //讀數據的時候,不允許讀數據
                        reentrantReadWriteLock.readLock().unlock();
                        reentrantReadWriteLock.writeLock().unlock();

                        //這裏上讀鎖
                        System.out.println(Thread.currentThread().getName() + "-read a =:" + a);
                    }
                }
            }).start();
        }

        while (true) {

            int j = 1;
        }
    }

效果:

 

 

但是想必大家也發現問題了,那就是效率極低

但是如果數據是比較珍貴,核心的數據,這點效率的丟失是否值得就是需要權衡的東西了

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章