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

當有多個線程設置對應的值的時候,讀取的值是不是那個線程設置的值???
若是咱們單獨對這個值上鎖的話,狀況會怎麼樣呢?
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的值設置完畢以後,其餘線程讀取的值,咱們也不知道是讀取那個線程設置的值ide

若是咱們把a設置爲數據庫的值的話,咱們會發現,不通的人請求拿到的結果竟然不是同樣的spa

很簡單舉個例子:線程

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

可是若是出現網絡波動,或者業務邏輯比較複雜,那麼就會致使B尚未修改完畢,A就把以前的舊數據讀取過去了,這樣就致使A系統用了一個錯的地址信息,blog

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

 

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

讀寫鎖,在讀的時候也要設置鎖,保證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;
        }
    }

效果:it

 

 

可是想必你們也發現問題了,那就是效率極低io

可是若是數據是比較珍貴,核心的數據,這點效率的丟失是否值得就是須要權衡的東西了

相關文章
相關標籤/搜索