LockSuport原理

LockSuport

1. 几种让线程等待和唤醒的方法

方式1: 使用Object中的wait()方法让线程等待, 使用Object中的notify()方法唤醒线程

方式2: 使用JUC包中Condition的await()方法让线程等待,使用signal()方法唤醒线程

方式3: LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程

Object类中的wait和notify方法实现线程等待和唤醒:

private static void synchronizedWaitNotify() {
    new Thread(() -> {
        synchronized (objectLock){
            System.out.println(Thread.currentThread().getName()+"\t"+"------come in");
            try {
                objectLock.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"\t"+"------被唤醒");
        }
    },"A").start();

    new Thread(() -> {
        synchronized (objectLock)
        {
            objectLock.notify();
            System.out.println(Thread.currentThread().getName()+"\t"+"------通知");
        }
    },"B").start();
}
 

存在两种异常:

1: t1线程等待3秒钟,3秒钟后t2线程唤醒t1线程继续工作, 则会出现先进线程B,线程A会出现永久阻塞

2: 去掉同步代码块会报错

package com.hhf.study.juc;

/**
 * 要求: t1线程等待3秒钟,3秒钟后t2线程唤醒t1线程继续工作 则会出现先进线程B(即先调用notify),线程A会出现永久阻塞
 
 *  以下异常情况:
 *   2 wait方法和notify方法,两个都去掉同步代码块后看运行效果
 *      2.1 异常惰况
 *        Exception in thread "t1" java.Lang.ILlegalLNonitorStateException at java.lang.Object.wait(Native Method)
 *        Exception in thread "t2" java.lang.ILlegalWonitorStateException at java.lang.Object.notify(Native Method)
 *
 *      2.2 结论
 *     Object类中的wait、notify、notifyALlL用于线程等待和唤醒的方法,都必须在synchronized内部执行(必须用到关键字synchronized)
 *
 */

public class LockSupportDemo {

    static Object objectLock = new Object();
    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (objectLock){
                System.out.println(Thread.currentThread().getName()+"\t"+"------come in");
                try {
                    objectLock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"\t"+"------被唤醒");
            }
        },"A").start();

        new Thread(() -> {
            synchronized (objectLock)
            {
                objectLock.notify();
                System.out.println(Thread.currentThread().getName()+"\t"+"------通知");
            }
        },"B").start();


    }
}
 

Condition接口中的await后signal方法实现线程的等待和唤醒

同样会出现上述情况

LockSupport类中的park等待和unpark唤醒

LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。

LockSupport类使用了一种名为Permit(许可)的概念来做到阻塞和唤醒线程的功能,每个线程都有一个许可(permit),permit只有两个值1和零,默认是零。
可以把许可看成是一种(0,1)信号量(Semaphore),但与Semaphore不同的是,许可的累加上限是1。

permit默认是0,所以一开始调用park()方法,当前线程就会阻塞,直到别的线程将当前线程的permit设置为1时,park方法会被唤醒,然后会将permit再次设置为0并返回。

调用unpark(thread)方法后,就会将thread线程的许可permit设置成1(注意多次调用unpark方法,不会累加,permit值还是1)会自动唤醒thread线程,
即之前阻塞中的LockSupport.park()方法会立即返回。

/**
  LockSupport:俗称 锁中断
          以前的两种方式:
                 1.以前的等待唤醒通知机制必须synchronized里面有一个wait和notify
                2.lock里面有await和signal
            这上面这两个都必须要持有锁才能干,
 LockSupport它的解决的痛点
            1。LockSupport不用持有锁块,不用加锁,程序性能好,
            2。先后顺序,不容易导致卡死
 */
Thread t1 = new Thread(() -> {

    System.out.println(Thread.currentThread().getName() + "\t ----begi"+System.currentTimeMillis());
    LockSupport.park();//阻塞当前线程
    System.out.println(Thread.currentThread().getName() + "\t ----被唤醒"+System.currentTimeMillis());
}, "t1");
t1.start();
LockSupport.unpark(t1);
System.out.println(Thread.currentThread().getName()+"\t 通知t1...");
 

之前错误的先唤醒后等待,LockSupport照样支持

  Thread t1 = new Thread(() -> {
        try { TimeUnit.SECONDS.sleep(5L); }catch (Exception e) {e.printStackTrace();}
        System.out.println(Thread.currentThread().getName() + "\t ----begi"+System.currentTimeMillis());
        LockSupport.park();//阻塞当前线程
        System.out.println(Thread.currentThread().getName() + "\t ----被唤醒"+System.currentTimeMillis());
    }, "t1");
    t1.start();
    try { TimeUnit.SECONDS.sleep(1); }catch (Exception e) {e.printStackTrace();}
    LockSupport.unpark(t1);
    System.out.println(Thread.currentThread().getName()+"\t 通知t1...");
}

LockSupport是用来创建锁和其他同步类的基本线程阻塞原语
LockSupport是一个线程阻塞工具类,所有的方法都是静态方法,可以让线程在任意位置阻塞,阻塞之后也有对应的唤醒方法。归根
结底,LockSupport调用的Unsafe中的native代码。

LockSupport提供park()和unpark()方法实现阻塞线程和解除线程阻塞的过程
LockSupport和每个使用它的线程都有一个许可(permit)关联。permit相当于1,0的开关,默认是0,调用一次unpark就加1变成1,
调用一次park会消费permit,也就是将1变成o,同时park立即返回。
如再次调用park会变成阻塞(因为permit为零了会阻塞在这里,一直到permit变为1),这时调用unpark会把permit置为1。
每个线程都有一个相关的permit, permit最多只有一个,重复调用unpark也不会积累凭证。

形象的理解
线程阻塞需要消耗凭证(permit),这个凭证最多只有1个。
当调用park方法时

如果有凭证,则会直接消耗掉这个凭证然后正常退出;
如果无凭证,就必须阻塞等待凭证可用;
而unpark则相反,它会增加一个凭证,但凭证最多只能有1个,累加无效。

源码分析

/**
    为线程调度目的禁用当前线程,除非
    许可证是可用的。
    <p>如果许可证是可用的,那么它被消费和调用
    立即返回;否则当前线程将被禁用
    用于线程调度,并处于休眠状态,直到以下三种情况之一
    事情发生了:
    调用@link #unpark unpark
    调用@linkplain Thread#interrupt interrupts
    The call spuriously (that is, for no reason) returns.没有理由的返回
*/
public static void park() {
    UNSAFE.park(false, 0L);
}
/**
    为给定线程提供许可(如果有的话)
	尚未提供。如果线程被阻塞
	{@code park}那么它将解除阻塞。否则,它的下一个调用
	to {@code park}保证不会阻塞。这个操作
	是否保证有任何效果,如果给定
	线程尚未启动。
*/
public static void unpark(Thread thread) {
    if (thread != null)
        UNSAFE.unpark(thread);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值