Java知识总结之线程安全与数据同步

1 synchronized

synchronized关键字可以实现一个简单的策略来防止干扰和内存一致性错误,如果一个对象是对多个线程是可见的,那么对该对象的所有读或写都将通过同步的方式来进行.

  • synchronized 关键字提供了一种锁机制,能够确保共享变量的互斥访问,从而防止数据不一致问题的出现.
  • synchronized关键字包括enter monitor和exit monitor两个jvm指令,它能够在任何时候任何线程执行到monitor enter成功之前都必须从主内存获取数据,而不是从缓存中.在monitor exit之后,共享变量被更新后的值会刷入主内存
  • synchronized严格遵守java happens-before规则,一个monitor exit之前必定有一个monitor enter

1.1 synchronized用法

synchronized可以对代码块或方法进行修饰.

1.1.1 同步方法

同步方法例子

1.1.2 同步代码块

同步代码块的例子

1.2 monitor

1.2.1 monitorenter

每个对象都有一个monitor相关联,一个monitor的lock锁只能被一个线程在同一时间获取,在一个线程尝试获取与对象关联的monitor的所有权时会发生如下:

  • 如果monitor的计数器为0,则意味着该对象的monitor的锁还没有被获得,某个线程获取后将立即对计数器加一,从此该线程就是这个monitor的所有者了.
  • 如果已经拥有该monitor的线程重入,则会导致该计数器再加1
  • 如果monitor已经被其他线程所拥有,则其他线程尝试获取该monitor的所有权时,会被陷入阻塞状态知道monitor计数器为0,才能再次尝试获取monitor的拥有权.

1.2.2 monitorexit

释放对monitor的拥有权,前提释放之前线程必须拥有该线程monitor的所有权.释放其实就是将monitor的计数器减1.如果计数器的结果为0,则该线程不再拥有对该monitor的拥有权,通俗来讲就是解锁.

1.3 使用synchronized的注意事项

  • 与monitor关联的对象不能为空
  • synchronized作用域太大:如果synchronized作用域越大,则代表着效率越低,甚至还会丧失并发的优势.
  • 不同的monitor企图锁相同的方法
  • 多个锁的交叉导致死锁

1.4 this monitor和class monitor

  • 同步成员方法实际就是用了对象的this monitor
  • 同步静态方法实际就是用来类的class对象的monitor

2 程序死锁的原因及其诊断

程序进入死锁

2.1 程序死锁

  • 交叉所可导致程序死锁:线程A持有R1的锁等待获取R2的锁,线程B持有R2的锁等待获取R1的锁.
  • 内存不足:当并发请求系统可用内存时,如果此时系统内存不足,可能会出现死锁情况.
  • 一问一答式的数据交换:服务端开启某个端口,等待客户端访问,客户端方位并立即等待接受响应,由于某种原因服务的错过了客户端的请求,仍然在等待一问一答式的数据交换,此时服务端和客户段都在等待着对方的答应.
  • 数据库锁:无论是数据表级别的锁,还是行级别的锁,比如某个线程for update语句退出了事务,其他线程访问该数据库时都将陷入死锁.
  • 文件锁:某线程获取文件锁但是意外退出,其他读取读取该文件的线程也会进入死锁直到系统释放文件句柄.
  • 死循环引起的死锁:程序由于代码原因,或则和对某些异常处理不正确,进入了死循环,虽然查看线程堆栈信息不会发现任何死锁的现象,但是程序不工作,CPU占用率又居高不下,这种死锁叫做系统假死,也是一种最为致命且很难排查的死锁现象.

你可能感兴趣的:(Java知识总结之线程安全与数据同步)