【并发】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;
        }
    }

效果:

 

 

但是想必大家也发现问题了,那就是效率极低

但是如果数据是比较珍贵,核心的数据,这点效率的丢失是否值得就是需要权衡的东西了

 

posted @ 2019-11-28 12:09  cutter_point  阅读(315)  评论(0编辑  收藏  举报