• 详解Java多线程编程中LockSupport


    LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。
    LockSupport中的park() 和 unpark() 的作用分别是阻塞线程和解除阻塞线程,而且park()和unpark()不会遇到“Thread.suspend 和 Thread.resume所可能引发的死锁”问题。
    因为park() 和 unpark()有许可的存在;调用 park() 的线程和另一个试图将其 unpark() 的线程之间的竞争将保持活性。

    基本用法
    LockSupport 很类似于二元信号量(只有1个许可证可供使用),如果这个许可还没有被占用,当前线程获取许可并继 续 执行;如果许可已经被占用,当前线 程阻塞,等待获取许可。

    1
    2
    3
    4
    5
    public static void main(String[] args)
    {
       LockSupport.park();
       System.out.println("block.");
    }

    运行该代码,可以发现主线程一直处于阻塞状态。因为 许可默认是被占用的 ,调用park()时获取不到许可,所以进入阻塞状态。

    如下代码:先释放许可,再获取许可,主线程能够正常终止。LockSupport许可的获取和释放,一般来说是对应的,如果多次unpark,只有一次park也不会出现什么问题,结果是许可处于可用状态。

    1
    2
    3
    4
    5
    6
    7
    public static void main(String[] args)
    {
       Thread thread = Thread.currentThread();
       LockSupport.unpark(thread);//释放许可
       LockSupport.park();// 获取许可
       System.out.println("b");
    }

    LockSupport是可不重入 的,如果一个线程连续2次调用 LockSupport .park(),那么该线程一定会一直阻塞下去。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public static void main(String[] args) throws Exception
    {
     Thread thread = Thread.currentThread();
      
     LockSupport.unpark(thread);
      
     System.out.println("a");
     LockSupport.park();
     System.out.println("b");
     LockSupport.park();
     System.out.println("c");
    }

    这段代码打印出a和b,不会打印c,因为第二次调用park的时候,线程无法获取许可出现死锁。

    下面我们来看下LockSupport对应中断的响应性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    public static void t2() throws Exception
    {
     Thread t = new Thread(new Runnable()
     {
      private int count = 0;
     
      @Override
      public void run()
      {
       long start = System.currentTimeMillis();
       long end = 0;
     
       while ((end - start) <= 1000)
       {
        count++;
        end = System.currentTimeMillis();
       }
     
       System.out.println("after 1 second.count=" + count);
     
      //等待或许许可
       LockSupport.park();
       System.out.println("thread over." + Thread.currentThread().isInterrupted());
     
      }
     });
     
     t.start();
     
     Thread.sleep(2000);
     
     // 中断线程
     t.interrupt();
     
      
     System.out.println("main over");
    }

    最终线程会打印出thread over.true。这说明 线程如果因为调用park而阻塞的话,能够响应中断请求(中断状态被设置成true),但是不会抛出InterruptedException 。

    LockSupport函数列表

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // 返回提供给最近一次尚未解除阻塞的 park 方法调用的 blocker 对象,如果该调用不受阻塞,则返回 null。
    static Object getBlocker(Thread t)
    // 为了线程调度,禁用当前线程,除非许可可用。
    static void park()
    // 为了线程调度,在许可可用之前禁用当前线程。
    static void park(Object blocker)
    // 为了线程调度禁用当前线程,最多等待指定的等待时间,除非许可可用。
    static void parkNanos(long nanos)
    // 为了线程调度,在许可可用前禁用当前线程,并最多等待指定的等待时间。
    static void parkNanos(Object blocker, long nanos)
    // 为了线程调度,在指定的时限前禁用当前线程,除非许可可用。
    static void parkUntil(long deadline)
    // 为了线程调度,在指定的时限前禁用当前线程,除非许可可用。
    static void parkUntil(Object blocker, long deadline)
    // 如果给定线程的许可尚不可用,则使其可用。
    static void unpark(Thread thread)


    LockSupport示例
    对比下面的“示例1”和“示例2”可以更清晰的了解LockSupport的用法。
    示例1

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    public class WaitTest1 {
     
      public static void main(String[] args) {
     
        ThreadA ta = new ThreadA("ta");
     
        synchronized(ta) { // 通过synchronized(ta)获取“对象ta的同步锁”
          try {
            System.out.println(Thread.currentThread().getName()+" start ta");
            ta.start();
     
            System.out.println(Thread.currentThread().getName()+" block");
            // 主线程等待
            ta.wait();
     
            System.out.println(Thread.currentThread().getName()+" continue");
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      }
     
      static class ThreadA extends Thread{
     
        public ThreadA(String name) {
          super(name);
        }
     
        public void run() {
          synchronized (this) { // 通过synchronized(this)获取“当前对象的同步锁”
            System.out.println(Thread.currentThread().getName()+" wakup others");
            notify();  // 唤醒“当前对象上的等待线程”
          }
        }
      }
    }

    示例2

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    import java.util.concurrent.locks.LockSupport;
     
    public class LockSupportTest1 {
     
      private static Thread mainThread;
     
      public static void main(String[] args) {
     
        ThreadA ta = new ThreadA("ta");
        // 获取主线程
        mainThread = Thread.currentThread();
     
        System.out.println(Thread.currentThread().getName()+" start ta");
        ta.start();
     
        System.out.println(Thread.currentThread().getName()+" block");
        // 主线程阻塞
        LockSupport.park(mainThread);
     
        System.out.println(Thread.currentThread().getName()+" continue");
      }
     
      static class ThreadA extends Thread{
     
        public ThreadA(String name) {
          super(name);
        }
     
        public void run() {
          System.out.println(Thread.currentThread().getName()+" wakup others");
          // 唤醒“主线程”
          LockSupport.unpark(mainThread);
        }
      }
    }

    运行结果:

    1
    2
    3
    4
    main start ta
    main block
    ta wakup others
    main continue

    说明:park和wait的区别。wait让线程阻塞前,必须通过synchronized获取同步锁。

  • 相关阅读:
    第9课
    FreeRTOS 定时器组
    FMC—扩展外部 SDRAM
    FreeRTOS 事件标志组
    第8课
    FreeRTOS 系统时钟节拍和时间管理
    第七课 线性表的顺序存储结构
    手把手教你调试Linux C++ 代码(一步到位包含静态库和动态库调试)
    Windows GUI代码与Windows消息问题调试利器
    谈谈数据挖掘和机器学习
  • 原文地址:https://www.cnblogs.com/ldq2016/p/9045824.html
Copyright © 2020-2023  润新知