1 Java并发编程—原理

可见性,原子性,有序性

提高并发量策略:

【尽量不使用锁】

  • 类里面不共享变量
  • 单个线程保有自己的数据——ThreadLocal
  • 消息队列【生产者-消费者模式】(若是消费者可以批量执行,效率更高)
  • StampedLock【乐观锁】
  • 原子类【CAS+自旋】
  • ReentrantReadWriteLock【读写锁】
  • 线程池

线程安全:若这个数据在线程间共享且读写不互斥,就是线程不安全

特别注意:线程安全的容器,方法是线程安全的,但是方法的组合不是原子性的,多线程情况下还是会因为线程的执行顺序导致结果的不同,即存在竞态条件 【给对容器的所有操作加锁即可】

回调函数,要注意是主线程还是新建一个线程来执行任务。如果是主线程,最好新建一个单线程的线程池来异步执行任务

可变对象不能加锁

Integer,Long等包装类不适合加锁,因为他们用到享元模式,这会导致看上去私有的锁其实锁的是一个对象

volatile

volatile int x = 0 告诉编译器这个变量不能使用CPU缓存,要在内存中读写(实际上是在CPU缓存中改变值之后,强制刷新到内存)

Happens-Before 规则

(意思是:前面操作的结果对后面可见)

1.程序的顺序性规则(在一段代码里,几条代码顺序执行,后面的代码能看到前面代码执行的结果)

2.volatile 变量规则(如果对一个volatile 变量进行了写操作,后面的读操作可以看见写操作的结果)(前提是Java1.5及以上)

3.传递性(a对b可见 , b对c可见 则 a对c可见)

4.管程中锁的规则(锁的解锁对后面锁的加锁可见,就是某个线程在解锁前的操作,对另一个线程拿到锁之后可见)

5.线程start()规则(如果主线程start子线程,主线程的操作对子线程可见)

6.线程join()规则(如果主线程join子线程,子线程的操作对主线程可见)

使用final好处:值不会变,编译器可以使劲优化

synchronized

默认对代码块进行加锁解锁操作
加在静态方法上,意思是锁这个类的class对象

加在普通方法上,意思是锁this,锁类的对象

或者加在代码块上,括号里面加要锁的东西【不能用自己的锁锁别人的资源】

用两把锁锁不同的资源【下面的锁括号里面不能是 this.balance和this.password,因为这两个东西是可变的,不能锁可变对象】

class Account {
  // 锁:保护账户余额
  private final Object balLock
    = new Object();
  // 账户余额  
  private Integer balance;
  // 锁:保护账户密码
  private final Object pwLock
    = new Object();
  // 账户密码
  private String password;
  // 取款
  void withdraw(Integer amt) {
    synchronized(balLock) {
      if (this.balance > amt){
        this.balance -= amt;
      }
    }
  }
  // 查看余额
  Integer getBalance() {
    synchronized(balLock) {
      return balance;
    }
  }
  // 更改密码
  void updatePassword(String pw){
    synchronized(pwLock) {
      this.password = pw;
    }
  }
  // 查看密码
  String getPassword() {
    synchronized(pwLock) {
      return password;
    }
  }
}
预防死锁:

1.互斥(不能避免)

2.占有且等待(方法:一次性获取全部资源)

3.不可抢占(用Lock)

4.循环等待(把资源排序,按序号从小到大取)

class Account {
  private int id;
  private int balance;
  // 转账
  void transfer(Account target, int amt){
    Account left = this        ①
    Account right = target;    ②
    if (this.id > target.id) { ③
      left = target;           ④
      right = this;            ⑤
    }                          ⑥
    // 锁定序号小的账户
    synchronized(left){
      // 锁定序号大的账户
      synchronized(right){
        if (this.balance > amt){
          this.balance -= amt;
          target.balance += amt;
        }
      }
    }
  }
}
写并发程序要关注3大方面问题:

1.安全性(数据竞争:多个线程共享数据,且这个数据会被至少一个线程修改,会导致bug【加锁就可】)(竞态条件:程序的结果依赖于程序执行的顺序,比如两个线程同时改一个数据,数据不变,先后改数据,数据改变【加锁即可】)

2.活跃性(活锁:比如两个线程获取一个资源,一直互相谦让【等待随机时间再尝试获取资源】)(饥饿:因为优先级低,或者一直拿不到资源,线程一直不能被执行【使用公平锁按先后顺序获取资源】)

3.性能性(使用无锁的算法和数据结构 线程本地存储 写入时复制 乐观锁,减少锁的持有时间 读写锁 分段锁)

用锁的最佳实践

1.永远只在更新对象的成员变量时用锁

2.永远只在访问可变的成员变量时用锁

3.永远不要在调用其他对象的方法时用锁

4.减少锁的持有时间

5.减少锁的粒度

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

沛权

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

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

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

打赏作者

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

抵扣说明:

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

余额充值