【并发】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; } }
效果:
但是想必大家也发现问题了,那就是效率极低
但是如果数据是比较珍贵,核心的数据,这点效率的丢失是否值得就是需要权衡的东西了