多把锁+ReentrantLock+活锁+死锁

目录

多把锁

 活跃性

 定位死锁

例题:哲学家问题 

活锁

 饥饿

ReentrentLock锁

用法

打断锁 

tryLock()设置超时方法

 哲学家问题优化——ReentrantLock解决死锁

公平锁


 

多把锁

并发度:

同一个时间节点上,可能与服务端进行交互的用户个数;

如何增强并发度:

首先明确,并发度增强无非就是交互个数在一个时间点上变多——>那么我们可以设置多把锁。目的就是同一时间上交互变多;

例子:

package com.example.juc.Multi;

import lombok.extern.slf4j.Slf4j;

/**
 * @author diao 2022/4/10
 */
@Slf4j(topic = "c.TestMultiLock")
public class TestMultiLock {
    public static void main(String[] args) {
         BigRoom room = new BigRoom();
        /**
         *room中的sleep与study方法锁资源是不一致的
         * 有利于增强并发度,不过容易发生死锁
         */
        //1.小明线程睡觉
         new Thread(()->{
             try {
                 room.sleep();
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         },"小明").start();

         //2.小花线程学习
        new Thread(()->{
            try {
                room.study();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"小花").start();
    }
}
@Slf4j(topic = "c.BigRoom")
class BigRoom{

    //1.提供两种房间,一个睡觉的一个学习的,两个业务分开
    private final Object studyRoom=new Object();
    private final Object bedRoom=new Object();

    public void sleep() throws InterruptedException {
        synchronized (bedRoom){
            log.debug("睡2秒");
            Thread.sleep(2000);
        }
    }

    public void study() throws InterruptedException {
        synchronized (studyRoom){
            log.debug("学习1s");
            Thread.sleep(1000);
        }
    }
}


 活跃性

死锁:相互调用对方的同步资源,但是锁资源又都没释放

例子:

package com.example.juc.Multi;

import lombok.extern.slf4j.Slf4j;

/**
 * @author diao 2022/4/10
 */
@Slf4j(topic = "c.DeadLock")
public class DeadLock {
    public static void main(String[] args) {
        testDead();
    }

    public static void testDead(){
        //1.锁资源
        Object A=new Object();
        Object B=new Object();

        Thread t1=new Thread(()->{
            synchronized (A){
                log.debug("即将lock B");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (B){
                    log.debug("lock B完成");
                }
            }
        },"t1");
        t1.start();

        Thread t2=new Thread(()->{
           synchronized (B){
               log.debug("即将lock A");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               synchronized (A){
                   log.debug("log A完成");
               }
           }
        });
        t2.start();
    }
}

 定位死锁

jps:列出执行进程

jstack 进程号找到信息

 或者用jconsle(可视化)


例题:哲学家问题 

一方等待另一方释放资源——>而另一方也在等待其他线程释放资源;

package com.example.juc.Multi;

import lombok.extern.slf4j.Slf4j;

/**
 * @author diao 2022/4/10
 */

@Slf4j(topic = "c.TestDeadLock2")
public class TestDeadLock2 {
    public static void main(String[] args) {
        //0.创建5个筷子对象
         Chopstick chopstick1 = new Chopstick("1");
         Chopstick chopstick2 = new Chopstick("2");
         Chopstick chopstick3 = new Chopstick("3");
         Chopstick chopstick4 = new Chopstick("4");
         Chopstick chopstick5 = new Chopstick("5");

         new People("小明",chopstick1,chopstick2).start();
         new People("小花",chopstick2,chopstick3).start();
         new People("小强",chopstick3,chopstick4).start();
         new People("小黑",chopstick4,chopstick5).start();
         new People("小安",chopstick5,chopstick1).start();

    }

}
@Slf4j(topic = "c.People")
class People extends Thread{
    //1.两个锁资源:左边筷子和右边筷子
    Chopstick left;
    Chopstick right;
    final String name;

    public People(String name,Chopstick left, Chopstick right) {
        this.left = left;
        this.right = right;
        this.name=name;
    }

    //2.重写Thread的run方法
    @Override
    public void run() {
        while(true){
            //2.1一直循环,集齐左右筷子即可触发eat
            log.debug("{}拿筷子",name);
            synchronized (left){
                log.debug("{}拿左筷子",name);
                synchronized (right){
                    log.debug("{}拿右筷子",name);
                    try {
                        eat();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    private static void eat() throws InterruptedException {
        log.debug("eating...");
        //当前线程吃了后,其他线程竞争拿筷子
        sleep(1000);
    }
}

class Chopstick{
    String name;

    public Chopstick(String name) {
        this.name = name;
    }
}

当每个人都拿一根筷子的时候就放下,deadLock


活锁

简单来说,就是一个造,一个解决——>导致一直解决不了,比如说20个数,一个线程减,一个线程加,就跟永动机一样;

package com.example.juc.Multi;

import lombok.extern.slf4j.Slf4j;

import static java.lang.Thread.sleep;

/**
 * @author diao 2022/4/10
 */
@Slf4j(topic = "c.LiveLockTest")
public class LiveLockTest {
    //1.锁资源
    static volatile int count=10;
    static final Object lock=new Object();

    public static void main(String[] args) {
        //2.加减线程
        new Thread(()->{
           //2.1当减到count<=0结束
           while(count>0){
               try {
                   sleep(20);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               count--;
               log.debug("当前count{}",count);
           }
        },"t1").start();

        new Thread(()->{
           while(count<20){
               try {
                   sleep(20);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               count++;
               log.debug("当前count{}",count);
           }
        },"t2").start();
    }
}

 饥饿

 我们可以按照顺序加锁,而不是一个线程先获取A,另一个获取B,然后第一个线程再获取B;

我们这个顺序加锁就可以直接先获取AB,就不会出现死锁了;


ReentrentLock锁

可中断:持有锁的线程一直不释放资源的情况下,处于阻塞状态的等待线程可以放弃等待;

支持多个条件变量:意思就是支持多个waitSet(多个条件)——>多个休息室

回顾一下:进入休息室的条件就是拥有锁资源,但是又放弃了,所以得在获取锁资源的条件下——>ReentrentLock特别在于不同条件会放在不同的waitSet中——>有利于减少唤醒次数;

共同点:支持可重入;

用法

 

package com.example.juc.ReengtrantLock;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.locks.ReentrantLock;

/**
 * @author diao 2022/4/10
 */
@Slf4j(topic = "c.ReentrantLock")
public class ReentrantLockTest {

    //1.锁资源
    private static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {
        //2.上锁
        lock.lock();
        try {
            log.debug("进入主方法");
            m1();
        } finally {
            lock.unlock();
        }
    }

    public static void m1() {
        lock.lock();
        try {
            log.debug("进入m1方法");
            m2();
        } finally {
            lock.unlock();
        }
    }

    public static void m2(){
        lock.lock();
        try {
           log.debug("进入m2方法");
        } finally {
            lock.unlock();
        }
    }
}

打断锁 

lock.lockInterruptibly():当获取不到锁资源,就暂停往下执行

t1.interrupt():打断t1线程,结束等待的意思,如果t1线程获取不到锁资源,就会直接退出等待,抛出异常;——>停止无限期的等待,比如死锁

这两个方法时配合起来用的;

 

 


tryLock()设置超时方法

package com.example.juc.ReengtrantLock;

import lombok.extern.slf4j.Slf4j;

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

/**
 * @author diao 2022/4/10
 */
@Slf4j(topic = "c.Test21")
public class Test21 {
    private static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            log.debug("尝试获得锁");
            try {
                //会等待指定时间,如果在指定时间得到锁资源,则向下执行
                if(!lock.tryLock(5, TimeUnit.SECONDS)){
                    log.debug("获取不到锁");
                    return;
                }
            } catch (InterruptedException e) {
                //因为lock.tryLock()也是可以被打断的
                e.printStackTrace();
                log.debug("获取不到锁");
                return;
            }

            try {
                log.debug("获得到锁");
            } finally {
                lock.unlock();
            }
        }, "t1");

        //主线程先获取锁
        lock.lock();
        log.debug("主线程获取到锁");

        t1.start();
    }
}

可以发现:

锁对象.tryLock()——>可以设置锁的等待时间,如果在指定时间内都没有得到锁资源就会不往下执行了;

 作用:避免无休止的等待,类似于保护性暂停模式;


 哲学家问题优化——ReentrantLock解决死锁

思路:当一个人只有一根筷子,而另一跟筷子在别人手里时,我们会释放掉当前手中的筷子谦让给别人——>目的:减少饥饿,让每个人都吃得上饭

package com.example.juc.ReengtrantLock;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.locks.ReentrantLock;

/**
 * @author diao 2022/4/10
 */
@Slf4j(topic = "c.TestDeadLock3")
public class ReentrantLockTest3 {
    public static void main(String[] args) {
        //0.创建5个筷子对象
        Chopstick chopstick1 = new Chopstick("1");
        Chopstick chopstick2 = new Chopstick("2");
        Chopstick chopstick3 = new Chopstick("3");
        Chopstick chopstick4 = new Chopstick("4");
        Chopstick chopstick5 = new Chopstick("5");

        new People("小明",chopstick1,chopstick2).start();
        new People("小花",chopstick2,chopstick3).start();
        new People("小强",chopstick3,chopstick4).start();
        new People("小黑",chopstick4,chopstick5).start();
//         new People("小安",chopstick5,chopstick1).start();
        //将锁对象换位置,但是会导致某些线程饥饿,一直获取不到锁资源
        new People("小安",chopstick1,chopstick5).start();

    }
}

/**
 * 线程People
 */
@Slf4j(topic = "c.People")
class People extends Thread{
    final String name;
    Chopstick left;
    Chopstick right;

    public People(String name, Chopstick left, Chopstick right) {
        this.name = name;
        this.left = left;
        this.right = right;
    }

    @Override
    public void run() {
        while(true){
            //1.尝试获得左筷子
            if(left.tryLock()){
                try {
                  //2.尝试获得右手筷子
                  if(right.tryLock()){
                      try {
                          eat();
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }finally {
                          //3.有得话eat完就给放了
                          right.unlock();
                      }
                  }
                }finally {
                    //4.第二个if判断失败,释放左手筷子
                    left.unlock();
                }
            }
        }
    }

    public static void eat() throws InterruptedException {
        log.debug("eating...");
        //线程eat后需要思考1s
        sleep(1000);
    }
}

/**
 * 把筷子当做锁对象,而不用synchronized
 * 继承ReentrantLock即可
 */
class Chopstick extends ReentrantLock {
    String name;

    public Chopstick(String name) {
        this.name = name;
    }
}


公平锁

什么叫不公平: 当你多个线程再等待队列里面进行等待时,获取锁资源,多个竞争一个,而不是按照进入队列时间来竞争,所以是不公平的;

ReentrantLock:可以通过构造方法设置公平还是不公平

公平锁:先进先得;——>解决饥饿问题;——>缺点:会降低并发度

但是我们可以用tryLock()方法来解决;

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Fairy要carry

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

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

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

打赏作者

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

抵扣说明:

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

余额充值