Java主流锁

 

 

 

1、公平锁,非公平锁

        公平锁:非常公平,不能够插队,必须先来后到

        非公平锁:非常不公平,可以插队(默认的都是非公平锁)

        

public ReentrantLock() {  //非公平锁,默认
        sync = new NonfairSync();
    }

public ReentrantLock(boolean fair) {//公平锁,new ReentrantLock(true),参数改为true时为公平锁
        sync = fair ? new FairSync() : new NonfairSync();
    }

2、可重入锁(递归锁)-------所有的锁,都是可重入锁

        概念:拿到了最外层的锁之后,就可以拿到了后面的锁(这是自动获得的)。举例:拿到了家门口的钥匙,就可以打开家里房间的门。

 synchronized版

package lin.lock;

import java.util.concurrent.TimeUnit;

//Synchronized==========================================================
public class ReenTrantLock_ {
    public static void main(String[] args) {
        Phone phone = new Phone();

        new Thread(()->{
            phone.sms();
        },"A").start();

        /*try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }*/

        new Thread(()->{
            phone.sms();
        },"B").start();


    }
}

class Phone{
    public synchronized void sms(){
        System.out.println(Thread.currentThread().getName()+" sms");
        call();     //这里也有锁
    }

    public synchronized void call(){
        System.out.println(Thread.currentThread().getName()+" call");
    }
}

执行结果: 

 

 lock版

package lin.lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

//Lock锁==========================================================
public class ReenTrantLock_Demo02 {
    public static void main(String[] args) {
        Phone2 phone = new Phone2();

        new Thread(()->{
            phone.sms();
        },"A").start();
        

        new Thread(()->{
            phone.sms();
        },"B").start();


    }
}

class Phone2{
    Lock lock = new ReentrantLock();

    public  void sms(){
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+" sms");
            call();     //这里也有锁
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }

    }

    public  void call(){
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+" call");
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

执行结果: 

 

细节问题:ReentrantLock版,当调用sms()这个方法时,拿到了lock.lock()这把锁,这时又调用了call()方法,此时又拿到call()的lock.lock()锁,然后lock.unlock()解锁,然后才会去执行sms()中的lock.unlock()解锁!(在lock锁中必须要配对,否则就会死锁

3、自旋锁(不断地去循环。遍历,迭代知道成功为止)

什么是自旋锁:是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。

自己写一个基础的自旋锁,小测试:

package lin.lock;

import java.util.concurrent.atomic.AtomicReference;

/*
* 自旋锁
* */
public class SpinLockDemo01 {

    //Thread引用类型,默认的是null
    AtomicReference<Thread> atomicReference = new AtomicReference<>();

    //加锁
    public void myLock(){
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+"------ myLock");

        //自旋锁
        while(!atomicReference.compareAndSet(null,thread)){

        }
    }

    //解锁
    public void myUnLock(){
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+"------ myUnLock");
        atomicReference.compareAndSet(thread,null);
    }
}
package lin.lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class TestSpinLock {
    public static void main(String[] args) {
//        ReentrantLock lock = new ReentrantLock();
//        lock.lock();
//        lock.unlock();

        //底层就是CAS实现的自旋锁
        SpinLockDemo01 lock = new SpinLockDemo01();

        new Thread(()->{
            lock.myLock();
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                lock.myUnLock();
            }
        },"A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            lock.myLock();
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                lock.myUnLock();
            }
        },"B").start();



    }
}

 

 注意:A线程进来之后,会先拿到这个锁,线程B必须等待。然后A线程自旋,等A线程解锁之后,B线程才有资格解锁。(因为A解锁之后才会释放,释放之后B才有机会进去拿到锁,然后再解锁)

4、死锁 

死锁是什么?

所谓死锁: 是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

死锁产生的4个必要条件?

产生死锁的必要条件:

互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用。
请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放。
不剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。
环路等待条件:在发生死锁时,必然存在一个进程--资源的环形链。

死锁的例子

package lin.lock;

import java.util.concurrent.TimeUnit;

public class DeadLockDemo01 {
    public static void main(String[] args) {
        String lockA = "lockA";
        String lockB = "lockB";

        new Thread(new MyThread(lockA,lockB),"T1").start();
        new Thread(new MyThread(lockB,lockA),"T2").start();
    }
}

class MyThread implements Runnable{

    private String lockA;
    private String lockB;

    public MyThread(String lockA, String lockB) {
        this.lockA = lockA;
        this.lockB = lockB;
    }

    @Override
    public void run() {
        synchronized(lockA){
            System.out.println(Thread.currentThread().getName()+"lock:"+lockA+"=>"+lockB);

            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            synchronized (lockB){
                System.out.println(Thread.currentThread().getName()+"lock:"+lockB+"=>"+lockA);
            }
        }
    }
}

结果:

遇到死锁,如何解决问题(因为在控制台中,没有显示任何的异常,只是单纯的卡着程序不懂而已,如何排查错误!!!)

  1.  使用 jps -l 定位进程号(前面的一串数字就是进程号)
  2. 使用 jstack 进程号 找到死锁问题

Java中实现分布式的方法主要有以下几种,每种方法都有其适用场景和优缺点: ### 1. 基于数据库的分布式实现 可以通过数据库的唯一索引来实现分布式。当多个节点尝试插入一条具有唯一索引的记录时,只有一个节点能够成功,其他节点会因为违反唯一性约束而失败,从而实现的获取与释放。这种方法实现简单,但在高并发场景下性能较差,且容易受到数据库单点故障的影响[^4]。 ### 2. 基于Redis的分布式实现 Redis是一种高性能的内存数据库,常用于实现分布式。通过`SETNX`(SET if Not eXists)命令可以实现的获取,结合`EXPIRE`命令设置的过期时间以防止死。此外,使用Redisson等高级客户端库可以简化分布式开发,支持看门狗机制自动续期,确保不会因为执行时间过长而被误释放。 下面是一个使用Jedis客户端实现Redis分布式的基本示例: ```java import redis.clients.jedis.Jedis; public class RedisDistributedLock { private Jedis jedis; private String lockKey; private int expireTime; public RedisDistributedLock(Jedis jedis, String lockKey, int expireTime) { this.jedis = jedis; this.lockKey = lockKey; this.expireTime = expireTime; } public boolean lock() { long expires = System.currentTimeMillis() + expireTime + 1; String expiresStr = String.valueOf(expires); // Set the lock with an expiration to avoid deadlocks if (jedis.setnx(lockKey, expiresStr) == 1) { return true; } String currentValue = jedis.get(lockKey); if (currentValue != null && Long.parseLong(currentValue) < System.currentTimeMillis()) { // It's expired String oldValue = jedis.getSet(lockKey, expiresStr); if (oldValue != null && oldValue.equals(currentValue)) { return true; } } return false; } public void unlock() { jedis.del(lockKey); } } ``` ### 3. 基于ZooKeeper的分布式实现 ZooKeeper是一个高效的分布式协调服务,它可以通过创建临时顺序节点来实现分布式。具体来说,客户端会在ZooKeeper上创建一个临时顺序节点,然后检查自己创建的节点是否是当前目录下最小的节点。如果是,则表示获得了;否则监听比自己序号小的节点,等待它们被删除后再尝试获取[^5]。 ### 4. 其他实现方式 除了上述主流实现方式外,还可以考虑使用其他中间件如Etcd等来实现分布式,这些方案通常也提供了丰富的API和工具来简化的实现过程。 ### 选择合适的实现方式 - **性能需求**:如果对性能有较高要求,推荐使用Redis实现分布式,因为它具有较高的响应速度和吞吐量。 - **可靠性需求**:对于需要高可靠性的场景,可以选择ZooKeeper,它提供了强一致性保证。 - **易用性需求**:若希望快速实现分布式功能,可以考虑使用Redisson等封装好的客户端库,它们提供了更加友好的API和自动管理功能[^4]。 综上所述,选择哪种方式实现分布式取决于具体的业务需求和技术栈,每种方法都有其适用的场景和局限性。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Lin_XXiang

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

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

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

打赏作者

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

抵扣说明:

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

余额充值