首页 / JAVA / Java多线程---多线程安全问题
Java多线程---多线程安全问题
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了Java多线程---多线程安全问题,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含3729字,纯文字阅读大概需要6分钟。
内容图文
![Java多线程---多线程安全问题](/upload/InfoBanner/zyjiaocheng/852/4a535154b4754965a80b57663b9edf8c.jpg)
上一次我们说到在卖票问题中如果不将总票数设置为static静态变量,就会出现错票,
即同样一张票会出售多次。
在今天的问题中,我们继续通过卖票问题来进行研究。
我们在每一个线程进行判断条件后让线程睡眠一段时间(让判断条件与数据操作之间相隔一段时间),看看会有什么效果?
运行结果:
通过运行结果我们可以发现有出售第0张票和第-1张票的情况 ,这是为什么呢?
是因为当有多个线程对一个共享数据进行操作时,可能会出现多线程的安全问题
这也是我们今天研究的主题。
当遇到这样的多线程安全问题时,我们可以通过synchronized关键字将它解决。
synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
我们用synchronized关键字修饰一个代码块,将if判断语句“包”起来--->放入代码块中,再次运行
结果如下:
我们可以看出,没有再发生出售第0,-1张票的情况。
这是因为我们使用了synchronized关键字,使得一个线程在调用此同步锁的时候,将对象锁锁住(拥有对象锁)
注意,这里锁的是作为对象锁的对象,并不是代码块,切记切记......
线程拥有对象锁即只有该线程在执行完代码块中的内容后,将对象锁释放,其它线程才能拥有此对象锁,执行代码快中的内容,
若该线程没有将对象锁释放,则其它线程便只能等待,等到该线程执行完代码块中内容后将锁释放,然后其它线程拥有此对象锁,进而执行代码快中的内容。
若没有synchronized关键字,则假设此时总票数ticket为1,A线程此时获得CPU执行权,通过判断ticket=1 > 0,进入if语句,然后睡眠。(此时ticket没有减1,仍为1)
A线程进入睡眠后,B线程获得CPU执行权,通过判断ticket=1 > 0,进入if语句,然后睡眠。
A线程苏醒后,进行ticket--操作,此时ticke输出t为1,之后ticket=0,通过判断ticket=0 > 0失败,不执行if语句,进而退出while循环,执行完毕。
B线程苏醒后,此时ticket为不为1,为0,则ticket进行--操作后,输出为0,之后ticket=-1,通过判断ticket= -1 > 0失败,不执行if语句,进而退出while循环,执行完毕。
若程序主线程中还定义了第三个线程,则如线程B,输出为-1 ----->这就是出现出售第0,-1张票情况的原因。
若加了synchronized代码块,则,A线程在执行完代码块中的内容后(进行ticket-1操作后),
其它线程才能根据ticket进行判断,若不满足条件,就会结束循环,执行完毕。就不会发生上述出售第0,-1张票的情况
从而解决多个线程对一个共享数据进行操作时出现的安全问题......
接下来我们使用两个线程分别调用同步代码块和同步方法
运行结果仍会有出售第0张票的情况,,小伙伴们可以自己试一下。
这是因为两个线程的对象锁是不同的,A线程的对像锁是obj,而B线程的对像锁是this,
(这里需要注意的是,B线程调用了同步方法【同步方法的对像锁是this】
而【静态同步方法的对象锁是当前类的字节码文件对象】,定义方法--->【类.class | 对象.getClass()】)
即A线程虽然将它的对象锁(obj)锁住,但并不影响B线程拥有它自己的对像锁(this),
它们的对像锁是不同的,结果很明显--->这样并不能解决多线程安全问题
当我们将同步代码块的对像锁设置为this时,两个线程的对像锁相同,便能解决多线程安全问题
使用静态同步方法与同步方法类似,将同步代码块的对像锁设置为 类.class 或者 对象.getClass(),也能解决多线程安全问题。
最后再说两个相关知识点:
同步方法与普通方法可以同时调用
synchronized获得的锁是可重入的
一个同步方法可以调用另外一个同步方法,一个线程已经拥有某个对象的锁,再次申请的时候仍然会得到该对象的锁.
【具体理解为:在使用synchronized时,当一个线程得到一个对象锁后,再次请求此对象锁时,是可以再次得到该对象的锁的。
也就是说在一个synchronized方法或块的内部调用本类的其他synchronized方法或块时,是永远可以得到锁的。】
这里需要注意的是,子类的同步方法中也可以调用父类的同步方法(通过super关键字)。
内容总结
以上是互联网集市为您收集整理的Java多线程---多线程安全问题全部内容,希望文章能够帮你解决Java多线程---多线程安全问题所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。