竞争条件 java_Java 并发编程笔记 1 - 竞争条件 & 初识原子类 & 可重入锁

Java 并发编程笔记 1 - 竞争条件 & 初识原子类 & 可重入锁

我们知道, 在多线程访问一个共享变量的时候会发生安全问题

首先看下面例子:publicclassCounter{

privateintcount;

publicvoidadd(){

try{

for(inti=0;i<200;i++){

Thread.sleep(100);

this.count++;

System.out.println(this.count);

}

}catch(Exceptione){

e.printStackTrace();

}

}

}

publicclassTest{

publicstaticvoidmain(String[]args){

finalCountercounter=newCounter();

newThread(newRunnable(){

publicvoidrun(){

counter.add();

}

}).start();

newThread(newRunnable(){

publicvoidrun(){

counter.add();

}

}).start();

newThread(newRunnable(){

publicvoidrun(){

counter.add();

}

}).start();

}

}

运行结果如下:

ab7653affab982b574eb7acc55df2e04.gif

如果没发生线程对数据的赃操作, 3 个线程执行, 结果会累加到 600 的, 看上面结果明显没有, 并且出现一下重复的数据这是因为存在 3 个线程同时操作同一个数字, 导致输出重复数字

解决办法:

1. 在方法上加上 synchronized 关键字如下图:

ab7653affab982b574eb7acc55df2e04.gif

虽然结果不会出现重复, 但是 synchronized 效率及其低下, 运行速度明显被拖慢原因是, for 循环中的 i 变量是每个线程都有独自的局部变量, 各不影响, 但是每个线程都要排队进入这个方法, 排队睡觉, 这样就导致效率低下

2. 在竞争条件上加 synchronized, 我们知道各个线程其实竞争的是 count 这个成员变量因此在此地方加即可如下图:

ab7653affab982b574eb7acc55df2e04.gif

这样运行效率比方法一快了很多, 因为省去了排队进入方法, 排队睡觉只需要排队取 count 值即可, 这样效率比方法一快

注意上图中, 输出语句并不是竞争条件, 并不一定要放在 synchroized 里面, 这里放在里面是为了让线程取到值自增后立即输出, 这样输出就不会发生混论, 不发生抢占输出问题, 一样能累加到 600,

如果把输出放在 synchronized 外面会出现值有重复现象, 因为累加后的值并没有立即输出, 这样导致输出混乱, 但仍然能加到 600. 知识输出不安全罢了

3. 使用原子类型, 比如将上面的代码的 int count 类型改成 AtomicInteger count 类型, 我们知道获取 count 的值然后再自加个 1 是可能会出现问题的, 也就是结果出现重复数字 AtomicInteger 类型是以同步的方法解决这个问题的如下图:

ab7653affab982b574eb7acc55df2e04.gif

结果如下图:

ab7653affab982b574eb7acc55df2e04.gif

可以看到数字的输出没有严格的排队, 但是数据确实给你保证的了, 就是完整的加到 600. 这也恰恰因为不是严格的进行排队, 才是的这种方法比前面两种方法的效率大大改进

理论总结: synchronized 是一种内部锁, 就是所对象内部给我们提供的, 因为每一个对象有一个状态变量, 相当于一个锁, 进入同步块, 改变这个变量别的线程进入之后就要判断这个变量有没有改变

一个线程获取它本生已经持有的锁, 这是可以成功的我们知道多个线程同时抢占同一个锁它们是失败的因为它们之间是互斥的但是呢, 一个线程再次获取一个自己已经拿过的锁是可以成功的, 那么它是能够成功的

看如下例子:publicclassWidget{

publicsynchronizedvoiddoSth(){

}

}

publicclassChildWidgetextendsWidget{

@Override

publicsynchronizedvoiddoSth(){

super.doSth();

}

}

publicclassTest{

publicstaticvoidmain(String[]args){

Widgetw=newChildWidget();

w.doSth();

}

}

子类调用自己的方法的那个 synchroized 那个锁是 w 对象的这个锁, 而在子类方法中的 super.doSth() 父类中的方法的 synchronized 的锁也是 w 对象的锁因此不要被子类继承父类的方法中的锁所迷惑了

因此这种方式叫做内部锁的可重入机制, 也叫可重入锁

来源: http://www.bubuko.com/infodetail-2515321.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值