线程资源同步---synchronized加锁

多线程读取一个静态资源是不安全的

模拟一个卖票程序,有100张票,3个窗口在卖。
卖票程序:

public class ticketSell implements Runnable {
    static int num = 100;
    @Override
    public void run() {
        while (num > 0) {
            System.out.println(Thread.currentThread().getName() + ":  还剩" + num-- + "张票");
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

主函数:

public class test {
    public static void main(String[] args) {
        ticketSell ts=new ticketSell();
        Thread ck1 = new Thread(ts, "窗口1");
        Thread ck2 = new Thread(ts, "窗口2");
        Thread ck3 = new Thread(ts, "窗口3");
        ck1.start();
        ck2.start();
        ck3.start();
    }
}

结果为:

窗口3:  还剩98张票
窗口2:  还剩99张票
窗口1:  还剩100张票
窗口3:  还剩97张票
窗口2:  还剩97张票
窗口1:  还剩97张票
窗口3:  还剩96张票
窗口2:  还剩95张票
窗口1:  还剩94张票
窗口1:  还剩93张票
窗口2:  还剩93张票
窗口3:  还剩93张票
窗口2:  还剩92张票
窗口1:  还剩92张票
窗口3:  还剩92张票
窗口2:  还剩91张票
窗口3:  还剩91张票
窗口1:  还剩91张票
窗口2:  还剩90张票
窗口1:  还剩90张票
窗口3:  还剩90张票
窗口1:  还剩89张票
窗口2:  还剩89张票
窗口3:  还剩89张票
窗口1:  还剩88张票
窗口3:  还剩88张票
窗口2:  还剩88张票
窗口2:  还剩87张票
窗口3:  还剩87张票
窗口1:  还剩87张票
窗口1:  还剩86张票
窗口2:  还剩86张票
窗口3:  还剩85张票
窗口3:  还剩84张票
窗口1:  还剩84张票
窗口2:  还剩83张票
窗口2:  还剩82张票
窗口3:  还剩82张票
窗口1:  还剩82张票
窗口2:  还剩81张票
窗口3:  还剩80张票
窗口1:  还剩81张票
窗口2:  还剩79张票
窗口1:  还剩78张票
窗口3:  还剩78张票
窗口3:  还剩77张票
窗口2:  还剩77张票
窗口1:  还剩77张票
窗口1:  还剩76张票
窗口3:  还剩76张票
窗口2:  还剩75张票
窗口2:  还剩73张票
窗口3:  还剩74张票
···

输出结果总票数变大,静态变量读取错误,3个线程的执行循序出现错乱,这是由于cpu给线程分配时间片时,类实例完成了票数修改但是还未完成语句输出,线程就被阻塞了。

此时我们应该引入同步的概念,同步是多线程在资源共享的程序中,单一线程对在代码未执行完之前对代码有锁的操作。
*同步 synchronized *

synchronized关键字可以用于锁定代码块,表面在本块代码执行时,当前线程有唯一操控权。
用法为
卖票代码变为:
synchronized (某对象) {代码块}
这里需要声明任意对象,将对象或者this传入synchronized作为参数,每次执行时,synchronized将对象作为锁标记。其他线程使用时会查询锁标记。

public class ticketSell implements Runnable {
    static int num = 100;
    Object obj = new Object();

    @Override
    public void run() {
        synchronized (obj) {
            while (num > 0) {
                System.out.println(Thread.currentThread().getName() + ":  还剩" + num-- + "张票");
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

结果就不会有错:

窗口1:  还剩100张票
窗口1:  还剩99张票
窗口1:  还剩98张票
窗口1:  还剩97张票
窗口1:  还剩96张票
窗口1:  还剩95张票
窗口1:  还剩94张票
窗口1:  还剩93张票
窗口1:  还剩92张票
窗口1:  还剩91张票
窗口1:  还剩90张票
窗口1:  还剩89张票
窗口1:  还剩88张票
窗口1:  还剩87张票
窗口1:  还剩86张票
窗口1:  还剩85张票
···

解释一下这里为什么全是窗口一在执行,这是因为窗口一抢到了资源并加锁,执行过程中别的线程是无法操作的。其他窗口唯一可以抢到操作权的机会是在窗口一执行结束,如果此时线程一的时间片分配正好结束,那其他线程是可以插进来的。不过这种概率微乎其微。


改进:
只做锁住该锁的代码:

    @Override
    public void run() {
        while (num > 0) {
            synchronized (this) {
                System.out.println(Thread.currentThread().getName() + ":  还剩" + num-- + "张票");
            }
        }
    }

这样就不会出现只被一个窗口锁定的情况:
结论,使用锁时,应在满足要求的情况下,尽量减小锁定范围。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值