欢迎点赞阅读, 一同学习交流, 有疑问请留言 .
GitHub 上也有开源 https://github.com/buerbl/JavaHouse , 欢迎 star
引用
当开发过程中, 我们遇到并发问题. 怎么解决?
一种解决方式, 简单粗暴: 上锁. 将千军万马都给拦下来, 只允许一个人过独木桥. 书面意思就是将并行的程序变成串行的程序. 现实的锁有门锁, 挂锁和抽屉锁等等. 在 Java 中, 我们的锁就是 synchronized 关键字和 Lock 接口.
synchronized 关键字
synchronized 也叫同步锁, 是 Java 里面的关键字. 我们可以猜测到 synchronized 原理也 JVM 虚拟机有关联.
synchronized 锁的是对象. 对象里面有一个叫做监视锁 (monitor) 的东西, 监视锁依赖操作系统的互斥锁(Mutex Lock). 操作系统切换线程其实就是从用户态编程核心态(CPU 的两种状态). 这个代价有点高, 所以 synchronized 这个重量级锁后面也引进了偏向锁和轻量级锁.
加锁 (监视锁 monitor) 过程分析():
当 monitor 的进入数为 0, 线程 A 进入
monitor 的进入数为 1
线程 B 想进入该 monitor 就会被阻塞.
线程 A 可以重复进入该 monitor, 所以 synchronized 是可重入锁, 和 Lock 实现的锁一样.
程序验证
- public class SynchronizedTest {
- private static int i = 0;
- public static void main(String[] args) {
- test();
- }
- public static void test(){
- synchronized (SynchronizedTest.class){
- synchronized (SynchronizedTest.class){
- i++;
- }
- }
- }
- }
运行结果
程序正常运行, 没有报错
synchronized 可以修饰方法以及代码块, 代码块就是上面重入锁的例子.
修饰方法
- public class SynchronizedTest {
- static int n = 100;
- final static CountDownLatch start = new CountDownLatch(n);
- private static int i = 0;
- public static void main(String[] args) throws InterruptedException {
- for (int j = 0; j < n; j++) {
- Thread thread = new Thread(new addNoSynchronized());
- thread.start();
- }
- start.await();
- System.out.println(i);
- }
- public static class addSynchronized implements Runnable{
- @Override
- public void run() {
- addSynchronized();
- }
- public static synchronized void addSynchronized(){
- for (int j = 0; j < 1000; j++) {
- i++;
- }
- start.countDown();
- }
- }
- }
运行结果
100000
如果去掉 synchronized 关键字的话, 运行结果大概率不是 100000, 因为线程不安全问题.
Lock 接口
一般我们使用 ReentrantLock 类作为重入锁, 实现 Lock 接口.
使用方法
- public class ReentranLockTest {
- private static int j;
- private static int n = 100;
- private static CountDownLatch latch = new CountDownLatch(n);
- public static void main(String[] args) throws InterruptedException {
- for (int i = 0; i < n; i++) {
- new Thread(new LockTest()).start();
- }
- latch.await();
- System.out.println("结果为:"+j);
- }
- public static class LockTest implements Runnable{
- static Lock lock = new ReentrantLock();
- @Override
- public void run() {
- lockTest();
- latch.countDown();
- }
- private void lockTest() {
- lock.lock();
- try {
- for (int i = 0; i < 1000; i++) {
- j++;
- }
- }finally {
- lock.unlock();
- }
- }
- }
- }
运行结果
结果为: 100000
这里我们锁住的 j++ 这块资源区(公共资源),lock 是 static 关键字修饰的, 是类对象, 思考一下如果不是类对象会怎么样? 那就是连环锁了(看图).
每一个线程都对可以用钥匙解开这把锁, 对于程序而言, 加锁操作就没有意义了. 因为我们需要的是一个锁.
来源: https://www.cnblogs.com/chenzhuantou/p/11964787.html