Java多线程【1】synchronized对象锁、内置锁使用

系列文章目录


Java多线程【1】synchronized对象锁、内置锁使用
Java多线程【2】Java wait/notify的使用于同步模式保护性暂停
Java多线程【3】同步模式之保护性暂停案例 相亲问题
Java多线程【4】interrupt线程的打断机制、两阶段终止模式
Java多线程【5】异步模式之生产者消费者
Java多线程【6】LockSupport park/unpark原理和使用以及于wait/notify的区别


前言

竞态条件

当两个线程竞争同一资源时,如果对资源的访问顺序敏感,就称存在竞态条件。

临界区

发生竞态条件的代码块就称为临界区。

为什么需要使用synchronized

在多核线程操作共有资源的时会发生状态的安全问题。需要synchronized将临界区中的代码进行同步操作。


一、synchronized能保证什么?

要想保证线程本身操作共同状态数据安全,必须保证临界区种的代码操作原子性,synchronized(对象锁、内置锁)可以保证代码的原子性。

   //线程共有状态
    static int count = 1000;
    public void increment(){
        for (int i = 0; i < 4000; i++) {
            //临界区
            {count--; }
        }
    }

count- -其实是非原子性的操作,如果多个线程同时执行increment方法就会导致count最后的值出现问题,最简单的解决办法如下

    static final Object lock = new Object();


    //线程共有状态
    static int count = 1000;


    public void increment(){
        for (int i = 0; i < 4000; i++) {
           synchronized (lock){
               //临界区
               {count--; }
           }
        }
    }

二、synchronized的使用

1、synchronized加在方法上

1.1、普通方法

    static Integer count = 1000;
    public synchronized void increment(){
        for (int i = 0; i < 4000; i++) {
           //临界区
           {count--; }
        }
    }
1.1.1 操作同一个对象
 		Test4 obj = new Test4(o);
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                obj.increment();
            }
        });

        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                obj.increment();
            }
        });

等同于synchronized(this){临界区},属于是锁this对象,这个时候如果线程操作的是同一个对象则不会发生安全问题。

1.1.2 操作多个对象(存在线程安全问题)

如果Test4是new的不同对象,那么this锁就无效,会发生安全问题
原因:每个对象的都有自己的this对象锁。在申请monitor管程的时候不同this锁的mark word指向的是不同的管程,那么也就意味着存在多个管程和多个管程中的entryList,这些entryList被CUP调度的可能只会跟自身的锁有关,会导致类的静态变量同时被线程修改发生安全性问题。

 		Test4 obj = new Test4(o);
        Test4 obj2 = new Test4(o);
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                obj.increment();
            }
        });

        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                obj2.increment();
            }
        });

1.2、静态方法

在类加载时,静态方法、静态变量等内容会存储到元空间数据区中,直接提供给类使用。那么在静态方法上加锁,其实就是锁住了类对象。

    public static synchronized void increment(){
        for (int i = 0; i < 4000; i++) {
           //临界区
           {count--; }
        }
    }

这个时候如果Test4是new的不同对象,也能保证临界区的原子性操作。

2、synchronized加在代码块中

2.1、this锁

  • 等同于普通方法上加锁
 	public void increment(){
        for (int i = 0; i < 4000; i++) {
           //临界区
           synchronized(this) {
                {count--; }
           }
        }
    }

2.2、类锁

  • 等同于synchronized修饰静态方法。
  • synchronized(clazz){},锁类。
    类锁在多个线程操作一个类的同一个对象时能保证安全性,一个类的多个对象就也可以保证安全性。因为不同对象的类在元空间只保存一份,属于同一个锁
 	public void increment(){
        for (int i = 0; i < 4000; i++) {
           //临界区
           synchronized(Test4.class) {
                {count--; }
           }
        }
    }

2.3、对象锁

  • synchronized(object){}
    对象锁相较于前两种锁更加的灵活,可以在创建对象的时候由线程传入,动态给分临界区的分配锁。
    
    static Object lock;
    
    public Test4(Object lock){
    	this.lock = lock;
    }
    
    public void increment(){
        for (int i = 0; i < 4000; i++) {
           //临界区
           synchronized(lock){
            	{count--; }
           }
        }
    }

三、synchronized的缺点和解决办法

1、synchronized锁的缺点

1.1、上下文切换效率

1.1、分布式系统失效

  • 频繁的加锁解锁,造成线程间的上下文切换,有时候反而会导致系统的资源利用率降低
  • 分布式的情况synchronized有可能无效

2、解决办法

2.1、上下文切换问题

  • java在synchronized内部其实隐式的实现了轻量锁(CAS)偏向锁等一下不需要切换上下文切换的锁。
  • 同步的颗粒度越小越好,同步的次数越少越好,
  • 尽量使用代码块加锁不要使用方法上加锁
  • 类锁少使用多使用对象锁。
  • 实际生产问题上我们得考虑synchronized的使用场景,根据业务灵活多变。

2.2、分布式系统失效问题

  • Redis分布式锁。
  • Redisson分布式增强锁(开箱即用,实现了锁的及时续费)推荐
  • ZK分布式锁 推荐
  • MySQL实现分布式锁

但不管如何,合理的使用Java内置锁,可以有效的解决大部分的线程安全问题。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

王乐乐君

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

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

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

打赏作者

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

抵扣说明:

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

余额充值