线程同步

本文通过示例介绍了Java中并发编程时可能出现的问题,如线程不安全的买票和取钱场景,展示了线程不安全的集合操作。通过引入`synchronized`关键字实现线程安全,确保了在多线程环境下对象状态的一致性。同时,讨论了线程同步的重要性以及如何通过锁机制避免数据竞争,以保证程序的正确运行。
摘要由CSDN通过智能技术生成
  • 并发:同一个对象被多个线程同时操作

  • 处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象,这个时候就需要线程同步。

  • 线程同步实际上就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用

  • 形成条件:队列+锁(synchronized)

    例1:线程不安全买票

    //有负数出现
    public class UnsafeBuyTicket {
    	public static void main(String[] args) {
    		BuyTicket station = new BuyTicket();
    		
    		new Thread(station,"抢票的你").start();
    		new Thread(station,"开挂的我").start();
    		new Thread(station,"黄牛党").start();
    
    	}
    }
    
    class BuyTicket implements Runnable{
    
    	//票
    	private int ticketNums = 10;
    	boolean flag = true; //外部停止方式
    	
    	@Override
    	public void run() {
    		while(flag) {
    			try {
    				buy();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    		
    	}
    	
    	private void buy() throws InterruptedException {
    		//判断是否有票
    		if(ticketNums <= 0) {
    			flag = false;
    			return;
    		}
    		//模拟延时
    		Thread.sleep(100);
    		//买票
    		System.out.println(Thread.currentThread().getName() + "拿到了" + ticketNums--);
    	}
    }
    /*
    抢票的你拿到了10
    开挂的我拿到了10
    黄牛党拿到了9
    开挂的我拿到了8
    抢票的你拿到了7
    黄牛党拿到了6
    开挂的我拿到了5
    黄牛党拿到了3
    抢票的你拿到了4
    抢票的你拿到了2
    开挂的我拿到了1
    黄牛党拿到了0
    抢票的你拿到了-1
    */
    

    例2:线程安全买票

    ...
    //synchronized同步方法,锁的是this
    private synchronized void buy() throws InterruptedException {
        ...
    }
    ...
    

    例3:线程不安全取钱

    public class UnsafeBank{
    	public static void main(String[] args) {
    		Account account = new Account(100,"存款");
    	    Drawing you = new Drawing(account,50,"you");
    	    Drawing i = new Drawing(account,100,"1");
    	    
    	    you.start();
    	    i.start();
    	}
    }
    
    //账户
    class Account{
        int money; //余额
        String name; //账户名
        
        public Account(int money,String name){
            this.money = money;
            this.name = name;
        }
    }
    
    //银行模拟取钱
    class Drawing extends Thread{
        
        Account account; //账户
        int drawingMoney; //取了多少钱
        int nowMoney; //现在手上有多少钱
        
        public Drawing(Account account,int drawingMoney,String name){
            super(name);
            this.account = account;
            this.drawingMoney = drawingMoney;
        }
        
        //取钱
        @Override
        public void run(){
            if(account.money - drawingMoney < 0){
                System.out.println(Thread.currentThread().getName() + "钱不够,取不了");
                return;
            }
            //sleep可以放大问题的发生性
            try{
                Thread.sleep(1000);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            //卡内余额
            account.money = account.money - drawingMoney;
            //手上的钱
            nowMoney = nowMoney + drawingMoney;
            System.out.println(account.name + "余额为:" + account.money);
            //Thread.currentThread().getName() = this.getName()
            System.out.println(this.getName() + "手上的钱:" + nowMoney);
        }
    }
    /*
    存款余额为:-50
    i手上的钱:100
    存款余额为:-50
    you手上的钱:50
    */
    

    例4:线程安全取钱

    ...
    //synchronized默认锁的是this.
    @Override
    public void run(){
        //锁的对象就是变化的量
        synchronized(account){
            if(account.money - drawingMoney < 0){
                System.out.println(Thread.currentThread().getName() + "钱不够,取不了");
                return;
            }
            //sleep可以放大问题的发生性
            try{
                Thread.sleep(1000);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            //卡内余额
            account.money = account.money - drawingMoney;
            //手上的钱
            nowMoney = nowMoney + drawingMoney;
            System.out.println(account.name + "余额为:" + account.money);
            //Thread.currentThread().getName() = this.getName()
            System.out.println(this.getName() + "手上的钱:" + nowMoney);
        }
    }
    ...
    

    例5:线程不安全的集合

    //结果并不是10000,说明有元素被替换,线程不安全
    public class UnsafeList{
        public static void main(String[] args){
            List<String> list = new ArrayList<String>();
            for(int i = 0; i < 10000; i++){
                new Thread(()->{
                    list.add(Thread.currentThread().getName());
                }).start();
            }
            try{
                Thread.sleep(3000);
            }catch(InterruptedException e){
                 e.printStackTrace();
            }
            System.out.println(list.size()); //9997
        }
    }
    

    例6:线程不安全的集合变为安全

    ...
    new Thread(()->{
        synchronized(list){
            list.add(Thread.currentThread().getName());
        }
    }).start();
    ...
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Remote_Li

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值