synchronized 关键字

目录

1 synchronized 的特性

1)互斥

2) 刷新内存(内存可见性)

3) 可重入

2 synchronized 使用示例

 1) 直接修饰普通方法:

2) 修饰静态方法:

3) 修饰代码块:        

.3 Java 标准库中的线程安全类 


 

1 synchronized 的特性

1)互斥

       synchronized 会起到互斥效果, 某个线程执行到某个对象的 synchronized 中时, 其他线程如果也执行到 同一个对象 synchronized 就会阻塞等待.        

        进入 synchronized 修饰的代码块, 相当于 加锁        

        退出 synchronized 修饰的代码块, 相当于 解锁

        bbc84eff6ac24964b2ea7d07bd5e3ebf.png 

            synchronized用的锁是存在Java对象头里的。

            synchronized的底层是使用操作系统的mutex lock实现的.

         e9b5c4e6d9424f58903531825b98e6eb.png

 理解 " 阻塞等待".
  •         针对每一把锁, 操作系统内部都维护了一个等待队列. 当这个锁被某个线程占有的时候, 其他线程尝试进行加锁, 就加不上了, 就会阻塞等待, 一直等到之前的线程解锁之后, 由操作系统唤醒一个新的线程, 再来获取到这个锁.
注意 :
  •        上一个线程解锁之后, 下一个线程并不是立即就能获取到锁. 而是要靠操作系统来 "唤醒". 这也就是操作系统线程调度的一部分工作.
  •         假设有 A B C 三个线程, 线程 A 先获取到锁, 然后 B 尝试获取锁, 然后 C 再尝试获取锁, 此时 B 和 C 都在阻塞队列中排队等待. 但是当 A 释放锁之后, 虽然 B 比 C 先来的, 但是 B 不一定就能 获取到锁, 而是和 C 重新竞争, 并不遵守先来后到的规则.

 

2) 刷新内存(内存可见性)

synchronized 的工作过程:
  1.  获得互斥锁
  2.  从主内存拷贝变量的最新副本到工作的内存
  3.  执行代码
  4.  将更改后的共享变量的值刷新到主内存
  5.  释放互斥锁

所以 synchronized 也能保证内存可见性. 与 volatile相似。 

3) 可重入

        synchronized 同步块对同一条线程来说是可重入的,不会出现自己把自己锁死的问题;

 

 代码示例

  •         在下面的代码中,  increase 和 increase2 两个方法都加了 synchronized, 此处的 synchronized 都是针对 this 当前 对象加锁的.
  •         在调用 increase2 的时候, 先加了一次锁, 执行到 increase 的时候, 又加了一次锁. (上个锁还没释放, 相当于连续加两次锁)

//一,针对this对象
static class Counter {
    public int count = 0;
    synchronized void increase() {
        count++;
   }
    synchronized void increase2() {
        increase();
   }
}
//二,针对locker
  private static Object locker = new Object();

Thread t1 = new Thread(() -> {
     synchronized (locker1) {
          // 此处的 sleep 很重要. 要确保 t1 和 t2 都分别拿到一把锁之后, 再进行后续动作.
     
                synchronized (locker1) {
                    System.out.println("t1 加锁成功!");
                }
            }
        });

  这个代码是完全没问题的. 因为 synchronized 是可重入锁.

在可重入锁的内部, 包含了 "线程持有者" 和 "计数器" 两个信息.
  •         如果某个线程加锁的时候, 发现锁已经被人占用, 但是恰好占用的正是自己, 那么仍然可以继续获取 到锁, 并让计数器自增.
  •         解锁的时候计数器递减为 0 的时候, 才真正释放锁. (才能被别的线程获取到)

2 synchronized 使用示例

     synchronized 本质上要修改指定对象的 "对象头". 从使用角度来看, synchronized 也势必要搭配一个具体的对象来使用.

 1) 直接修饰普通方法:

        锁的 SynchronizedDemo 对象

 

public class SynchronizedDemo {
 
   public synchronized void methond() {
 
  }
}

2) 修饰静态方法:

        锁的 SynchronizedDemo 类的对象 

public class SynchronizedDemo {
 
   public synchronized static void method() {
 
  }
}

3) 修饰代码块:        

        明确指定锁当前哪个对象
 
//1. 锁当前this对象
public class SynchronizedDemo {
 
   public void method() {
 
       synchronized (this) {
           
      }
  }
}
//2.锁类对象
public class SynchronizedDemo {
 
   public void method() {
 
       synchronized (SynchronizedDemo.class) {
      }
  }
}

两个线程竞争同一把锁, 才会产生阻塞等待.  

.3 Java 标准库中的线程安全类 

这些是线程安全的. 使用了一些锁机制来控制.
  • Vector (不推荐使用)
  • HashTable (不推荐使用)
  • ConcurrentHashMap
  • StringBuffer
    核心方法都带有 synchronized .

 结语:synchronized的相关分享到这里就结束了,希望对大家的学习会有帮助,如果大家有什么问题或者不同的见解,欢迎大家评论区的留言, 感谢支持 !!!

 

 

 

评论 30
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

爆浆曲奇饼~

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

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

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

打赏作者

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

抵扣说明:

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

余额充值