zoukankan      html  css  js  c++  java
  • 【并发】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;
            }
        }

    效果:

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

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

  • 相关阅读:
    组合模式
    HashMap,ArrayList扩容
    Maven入门使用(一)
    OutputStreamWriter API 以及源码解读
    java.io.BufferedWriter API 以及源码解读
    java.io.writer API 以及 源码解读
    自定义redis序列化工具
    策略模式
    Spring下redis的配置
    简单工厂模式
  • 原文地址:https://www.cnblogs.com/cutter-point/p/11949105.html
Copyright © 2011-2022 走看看