多线程之共享模型

一段代码块内如果存在对共享资源的多线程读写操作,称这段代码块为 临界区


1、synchronized解决方案

为了避免临界区的竞态条件发生,有多种手段可以达到目的。
阻塞式的解决方案:synchronized,Lock
非阻塞式的解决方案:原子变量


本次课使用阻塞式的解决方案:synchronized,来解决上述问题,即俗称的【对象锁】,它采用互斥的方式让同一时刻至多只有一个线程能持有【对象锁】,其它线程再想获取这个【对象锁】时就会阻塞住。这样就能保证拥有锁的线程可以安全的执行临界区内的代码,不用担心线程上下文切换。
注意
       虽然 java 中互斥和同步都可以采用 synchronized 关键字来完成,但它们还是有区别的:
       互斥是保证临界区的竞态条件发生,同一时刻只能有一个线程执行临界区代码;
       同步是由于线程执行的先后、顺序不同、需要一个线程等待其它线程运行到某个点;

 

2、类锁

类锁是加载类上的,而类信息是存在 JVM 方法区的,并且整个 JVM 只有一份,方法区又是所有线程共享的,所以类锁是所有线程共享的。

使用类锁的方式有如下方式:


1、锁住类中的静态变量

因为静态变量和类信息一样也是存在方法区的并且整个 JVM 只有一份,所以加在静态变量上可以达到类锁的目的。

2、直接在静态方法上加 synchronized

因为静态方法同样也是存在方法区的并且整个 JVM 只有一份,所以加在静态方法上可以达到类锁的目的。

3、锁住 xxx.class

对当前类的 .class 属性加锁,可以实现类锁。

类锁是所有线程共享的锁,所以同一时刻,只能有一个线程使用加了锁的方法或方法体,不管是不是同一个实例。

3、常见线程安全类

private 或 final 提供【安全】,请体会开闭原则中的【闭】

String (String 的 replace,substring 等方法本质上是创建新对象)
Integer
StringBuffer
Random
Vector
Hashtable
java.util.concurrent 包下的类


这里说它们是线程安全的是指,多个线程调用它们同一个实例的某个方法时,是线程安全的。也可以理解为它们的每个方法是原子的

Hashtable table = new Hashtable();
new Thread(()->{
table.put("key", "value1");
}).start();
new Thread(()->{
table.put("key", "value2");
}).start();

注意它们多个方法的组合不是原子的,

线程安全类方法的组合 ,分析下面代码是否线程安全?

Hashtable table = new Hashtable();
// 线程1,线程2
if( table.get("key") == null) {
table.put("key", value);
}

 

4、原理之 Monitor(锁)

 

Monitor 被翻译为监视器或管程
每个 Java 对象都可以关联一个 Monitor 对象,如果使用 synchronized 给对象上锁(重量级)之后,该对象头的Mark Word 中就被设置指向 Monitor 对象的指针
Monitor 结构如下 

刚开始 Monitor 中 Owner 为 null


当 Thread-2 执行 synchronized(obj) 就会将 Monitor 的所有者 Owner 置为 Thread-2,Monitor中只能有一个 Owner


在 Thread-2 上锁的过程中,如果 Thread-3,Thread-4,Thread-5 也来执行 synchronized(obj),就会进入EntryList BLOCKED


Thread-2 执行完同步代码块的内容,然后唤醒 EntryList 中等待的线程来竞争锁,竞争的时是非公平的


图中 WaitSet 中的 Thread-0,Thread-1 是之前获得过锁,但条件不满足进入 WAITING 状态的线程,后面讲wait-notify 时会分析


注意:
synchronized 必须是进入同一个对象的 monitor 才有上述的效果
不加 synchronized 的对象不会关联监视器,不遵从以上规则 

5、原理之synchronized

猜你喜欢

转载自blog.csdn.net/wang_nian/article/details/109136450