• Java 多线程 -- 线程安全 双重检测(double checking)


    先看一个经典的12306案例:

    public class SynBlockTest {
    	public static void main(String[] args) {
    		// 一份资源
    		SynWeb12306 web = new SynWeb12306();
    		// 多份代理
    		new Thread(web,"黄牛").start();
    		new Thread(web,"码农").start();
    		new Thread(web,"黑牛").start();
    	}
    }
    
    class SynWeb12306 implements Runnable{
    	private int ticketNums = 10;// 票数
    	private boolean flag = true;
    
    	@Override
    	public void run() {
    		while(flag) {
    			test01();
    		}
    		
    	}
    	
    	public void test01() {
    		if(ticketNums <= 0) {
    			flag = false;
    			return;
    		}
    		// 模拟延迟
    		try {
    			Thread.sleep(100);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		System.out.println(Thread.currentThread().getName() + "-->" + ticketNums--);
    	}
    }
    

    以上代码运行结果:
    在这里插入图片描述
    显然,在没有加锁的情况下,数据时不安全的

    下面我么使用代码代码块加锁:
    
    public void test02() {
    		synchronized(this) {
    			if(ticketNums <= 0) {
    				flag = false;
    				return;
    			}
    			// 模拟延迟
    			try {
    				Thread.sleep(100);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			System.out.println(Thread.currentThread().getName() + "-->" + ticketNums--);
    		}
    	}
    

    修改后运行测试:
    在这里插入图片描述

    好了数据安全了。
    但是,这样锁效率比较低:
    在进入synchronized 代码块之前,如果没票了,不用让所有线程都等待。
    于是修改代码:

    // //double checking
    	public void test03() {
    		if(ticketNums <= 0) { // 考虑没有票的情况
    			flag = false;
    			return;
    		}
    		synchronized(this) {
    			if(ticketNums <= 0) { // 考虑最后一张票
    				flag = false;
    				return;
    			}
    			// 模拟延迟
    			try {
    				Thread.sleep(100);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			System.out.println(Thread.currentThread().getName() + "-->" + ticketNums--);
    		}
    	}
    

    修改后运行:
    在这里插入图片描述
    好了这就是所谓 的双重检测。

    重视基础,才能走的更远。
  • 相关阅读:
    【Linux】在Linux上,使用校园出校器拨号的一个脚本。
    【Android】编译CM10.1遇到的错误解决方案
    【Android】编译CM10遇到的错误解决方案
    【Android】CM在repo中使用local manifest
    一个网站的诞生 MagicDict开发总结1 [首页]
    我记录网站综合系统 1.6发布
    带有ToolTip的ListBox
    字符串的宽度
    .NET开发的文本编辑器,(又发明轮子了,VB代码,不喜误入)
    捕获输入内容
  • 原文地址:https://www.cnblogs.com/xzlf/p/12681526.html
Copyright © 2020-2023  润新知