JUC基础-0601

6 多线程锁

6.1 锁的八个问题演示

class Phone {

    public static synchronized void sendSMS() throws Exception {
        //停留4秒
        TimeUnit.SECONDS.sleep(4);
        System.out.println("------sendSMS");
    }

    public synchronized void sendEmail() throws Exception {
        System.out.println("------sendEmail");
    }

    public void getHello() {
        System.out.println("------getHello");
    }
}
  1. 标准访问,先打印短信还是邮件

    1. ------sendSMS
    2. ------sendEmail
  2. 停 4 秒在短信方法内,先打印短信还是邮件

    1. ------sendSMS
    2. ------sendEmail
  3. 新增普通的 hello 方法,是先打短信还是 hello

    1. ------getHello
    2. ------sendSMS
  4. 现在有两部手机,先打印短信还是邮件

    1. ------sendEmail
    2. ------sendSMS
  5. 两个静态同步方法,1 部手机,先打印短信还是邮件

    1. ------sendSMS
    2. ------sendEmail
  6. 两个静态同步方法,2 部手机,先打印短信还是邮件

    1. ------sendSMS
    2. ------sendEmail
  7. 1 个静态同步方法,1 个普通同步方法,1 部手机,先打印短信还是邮件

    1. ------sendEmail
    2. ------sendSMS
  8. 1 个静态同步方法,1 个普通同步方法,2 部手机,先打印短信还是邮件

    1. ------sendEmail
    2. ------sendSMS

分析:两点:

  1. 是否用的同一把锁
  2. 锁的范围是怎样的?

6.2 总结

  1. 两个线程的创建之间间隔了100ms,则短信线程先创建,邮件后创建
  2. sleep不会释放锁,两个线程一起等待4秒后,顺序和1一样
  3. hello不同步的,SMS等待4秒,所以先Hello
  4. 两部手机,两个同步监视器,互相不干扰,SMS睡4秒,所以先email
  5. 两个静态方法,锁是静态类的Class,所以顺序和1一样,因为同一把锁
  6. 两个静态方法,两部手机,但是锁还是一个,和5一样
  7. 一个锁是Class,一个锁是this,互相不影响,所以email快,SMS睡4秒所以慢
  8. 两把锁,几个手机都一样,锁不同,顺序和7一样。

具体表现为以下 3 种形式。
对于普通同步方法,锁是当前实例对象。
对于静态同步方法,锁是当前类的
Class 对象。
对于同步方法块,锁是
Synchonized 括号里配置的对象

6.3 公平锁和非公平锁

例如:ReentrantLock的无参构造,就是一个非公平锁,会出现一个线程消耗所有资源,其他线程饿死的问题。

//	非公平锁演示
private Lock lock =  new ReentrantLock();
//	或这样也是非公平锁
private Lock lock =  new ReentrantLock(false);

//	公平锁
private Lock lock =  new ReentrantLock(true);

公平锁与非公平锁优缺点:

  1. 非公平锁:
    1. 效率高
    2. 线程饿死
  2. 公平锁:
    1. 阳光普照
    2. 效率相对低
  3. 想要效率高:选非公平锁
  4. 想要公平:公平锁

公平锁与非公平锁源码实现

  1. 非公平锁:占据锁之后之间进行后续操作
static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    /**
     * Performs lock.  Try immediate barge, backing up to normal
     * acquire on failure.
     */
    final void lock() {
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }

    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}
  1. 公平锁:里面有一个acquires 参数,并且判断时先执行hasQueuedPredecessors()方法,相当于先礼貌的问一句,这里有人吗;没人就继续操作,有人就会排队。
static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;

    final void lock() {
        acquire(1);
    }

    /**
     * Fair version of tryAcquire.  Don't grant access unless
     * recursive call or no waiters or is first.
     */
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
}

public final boolean hasQueuedPredecessors() {
        // The correctness of this depends on head being initialized
        // before tail and on head.next being accurate if the current
        // thread is first in queue.
        Node t = tail; // Read fields in reverse initialization order
        Node h = head;
        Node s;
        return h != t && ((s = h.next) == null || s.thread != Thread.currentThread());
}

6.4 可重入锁(递归锁)

synchronized(隐式)和Lock(显示)都是可重入锁

synchronized的加锁和释放都是自动的,所以是隐式的;Lock是显式的。

简单讲:如果一个线程拿到了锁,那么所有需要这个锁的地方,他都可以进入了

可重入锁,又称为递归锁,是一种递归无阻塞的同步机制。"可重入"的意思是如果一个线程已经持有了一个锁,那么这个线程可以再次获取同一把锁而不会被锁阻塞。这个特性可以在递归或者需要多次访问同步资源的场合简化编程。

在Java中,synchronized和ReentrantLock都是可重入锁。比如:

public class Example {
    public synchronized void method1() {
        method2();
    }

    public synchronized void method2() {
        // do something
    }
}

在这个例子中,一个线程进入method1方法后,得到了对象锁,然后它可以再次进入method2方法而不会阻塞,因为它已经持有了对象锁。

同样的,使用ReentrantLock也能达到类似的效果:

import java.util.concurrent.locks.ReentrantLock;

public class Example {
    private final ReentrantLock lock = new ReentrantLock();

    public void method1() {
        lock.lock();
        try {
            method2();
        } finally {
            lock.unlock();
        }
    }

    public void method2() {
        lock.lock();
        try {
            // do something
        } finally {
            lock.unlock();
        }
    }
}

在这个例子中,线程进入method1方法后,获取了锁,然后它可以进入method2方法并再次获取锁,而不会被阻塞。需要注意的是,每次调用lock()方法,引用计数就加1,每次调用unlock()方法,引用计数就减1。只有当引用计数为0时,锁才真正被释放。

这种锁的好处是,同一个线程可以多次获取同一把锁,避免了死锁。缺点是可能导致锁保持得过久,从而影响系统性能。

LocK可重入锁演示:

//Lock演示可重入锁
        Lock lock = new ReentrantLock();
        //创建线程
        new Thread(()->{
            try {
                //上锁
                lock.lock();
                System.out.println(Thread.currentThread().getName()+" 外层");

                try {
                    //上锁
                    lock.lock();
                    System.out.println(Thread.currentThread().getName()+" 内层");
                }finally {
                    //释放锁
                    lock.unlock();
                }
            }finally {
                //释放做
                lock.unlock();
            }
        },"t1").start();

在这段代码里面,如果不释放锁,后面的会获取不到锁。

6.5 死锁

  1. 定义:两个或者两个以上进程在执行过程中,因为争夺资源而造成一种互相等待的现象,如果没有外力干涉,他们无法再执行下去
  2. 产生死锁原因:
    1. 系统资源不足
    2. 进程运行推进顺序不合适
    3. 资源分配不当
  3. 验证死锁命令
    1. jps:ps -ef
    2. jstack :jvm自带堆栈工具
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值