教你如何使用Java多线程编程LockSupport工具类

LockSupport类

用于创建锁和其他同步类的基本线程阻塞原语,此类与使用它的每个线程关联一个许可。如果获得许可,将立即返回对park的调用,并在此过程中消耗掉它;否则may会被阻止。调用unpark可使许可证可用(如果尚不可用)。(不过与信号量不同,许可证不会累积。最多只能有一个。)
方法park和unpark提供了有效的阻塞和解阻塞线程的方法,这些线程不会遇到导致已弃用的方法Thread.suspend和Thread.resume无法用于以下问题:由于许可,在调用park的一个线程与试图进行unpark的另一个线程之间的竞争将保留生命力。此外,如果调用者的线程被中断并且支持超时版本,则park将返回。 park方法也可能在其他任何时间出于“无理由”返回,因此通常必须在循环中调用该循环,该循环在返回时会重新检查条件。从这个意义上说,park是对“繁忙等待”的优化,它不会浪费太多的时间,而必须与unpark配对才能生效。
了解了其作用,我们看看相关API。

LockSupport.park()

park():除非有许可,否则出于线程调度目的禁用当前线程。
如果许可证可用,则将其消耗掉,并立即返回呼叫;否则,将立即返回该呼叫。出于线程调度的目的,当前线程将被禁用,并处于休眠状态,直到发生以下三种情况之一:
1.其他一些线程以当前线程为目标调用unpark()方法
2.其他一些线程Threadinterrupt interrupts当前线程
3.虚假地调用返回
源码:

public static void park() {
    UNSAFE.park(false, 0L);
}

LockSupport都是调用的UNSAFE的方法,我们先看看park方法

public class LockSupportMain {
    public static void main(String[] args) {
        System.out.println("begin park!");
        LockSupport.park();
        System.out.println("end park!");
    }
}

运行结果:

我们调用了park方法,导致Main线程一直被阻塞,一直没有结束,因为默认的情况下,调用线程是不持有许可证的,我们如何解决呢?上面提到过三个方式,我们一个一个的验证。

1.调用unpack方法获得许可

unpack():如果给定线程尚不可用,则使它可用。如果线程在park上被阻止,则它将取消阻止。否则,将确保其对park的下一次调用不会阻塞。如果给定线程尚未启动,则不能保证此操作完全无效。
源码:

public static void unpark(Thread thread) {
    if (thread != null)
        UNSAFE.unpark(thread);
}
public class LockSupportMain2 {
    public static void main(String[] args) {
        System.out.println("begin park!");
        LockSupport.unpark(Thread.currentThread());
        LockSupport.park();
        System.out.println("end park!");
    }
}

运行结果:

可以看出,当前的线程main已经释放了,没有进行阻塞,直接运行完成了。
我们创建一个线程有Main线程进行unpark方法将线程在阻塞的情况下,进行运行。

public class LockSupportMain3 {

    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("begin start thread name: " + Thread.currentThread().getName() + " park");
                LockSupport.park();
                System.out.println("end start thread name: " + Thread.currentThread().getName() + " park");
            }
        };

        Thread thread = new Thread(runnable);
        thread.start();
        Thread.sleep(2000);
        System.out.println("main thread call unpark");
        LockSupport.unpark(thread);
    }

}

运行结果:

运行结果可以看出,其他一些线程以当前线程为目标调用unpark()方法可以将线程的park导致阻塞的继续运行。

2.调用中断interrupts方法获得许可

由于park方法不会告诉你何种原因返回,所以调用者需要根据之前调用park方法的原因,再次检查条件是否满足,如果不能满足,就还需要调用park方法

public class LockSupportMain4 {

    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("begin start thread name: " + Thread.currentThread().getName() + " park");
                while (!Thread.currentThread().isInterrupted()) {
                    LockSupport.park();
                }
                System.out.println("end start thread name: " + Thread.currentThread().getName() + " park");
            }
        };

        Thread thread = new Thread(runnable);
        thread.start();
        Thread.sleep(2000);
        //通过interrupt方法 让park阻塞继续运行
        thread.interrupt();
    }

}

运行结果:

由运行可以看出,其他一些线程Threadinterrupt interrupts当前线程是可以将park阻塞的线程继续运行。

parkNanos(long nanos)

parkNanos(long nanos):除非允许使用许可,否则在指定的等待时间内禁用用于线程调度的当前线程。
源码:

public static void parkNanos(long nanos) {
    if (nanos > 0)
        UNSAFE.park(false, nanos);
}
public class LockSupportMain5 {

    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("begin start thread name: " + Thread.currentThread().getName() + " park");
                LockSupport.parkNanos(3_000_000_000L);
                System.out.println("end start thread name: " + Thread.currentThread().getName() + " park");
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
    }
}

运行结果:

三秒之后,阻塞三秒之后继续运行。

park(Object blocker)

park(Object blocker):除非有许可,否则出于线程调度目的禁用当前线程
源码:

public static void park(Object blocker) {
    Thread t = Thread.currentThread();
    setBlocker(t, blocker);
    UNSAFE.park(false, 0L);
    setBlocker(t, null);
}

这里的blocker对象是Thread类中的blocker对象,代码如下:

//提供给java.util.concurrent.locks.LockSupport.park的当前调用的参数。
//由(私有)java.util.concurrent.locks.LockSupport.setBlocker设置使用
//java.util.concurrent.locks.LockSupport.getBlocker进行访问
volatile Object parkBlocker;

parkNanos(Object blocker, long nanos)

源码:

public static void parkNanos(Object blocker, long nanos) {
    if (nanos > 0) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(false, nanos);
        setBlocker(t, null);
    }
}

parkUntil(Object blocker, long deadline)

parkUntil(Object blocker, long deadline):除非指定许可,否则禁用当前线程以进行线程调度,直到指定的期限。
源码:

public static void parkUntil(Object blocker, long deadline) {
    Thread t = Thread.currentThread();
    setBlocker(t, blocker);
    UNSAFE.park(true, deadline);
    setBlocker(t, null);
}

我们使用java API中的例子看看:

public class LockSupportMain6 {
    private final AtomicBoolean locked = new AtomicBoolean(false);
    private final Queue<Thread> waiters
            = new ConcurrentLinkedQueue<Thread>();

    public void lock() {
        boolean wasInterrupted = false;
        Thread current = Thread.currentThread();
        waiters.add(current);

        // 不在队列中时先阻塞或无法获取锁定
        while (waiters.peek() != current ||
                !locked.compareAndSet(false, true)) {
            LockSupport.park(this);
            // 等待时忽略中断 如果park方法是因为被中断而返回,则忽略中断,并且重新设置中断标记,做个标记
            if (Thread.interrupted()) {
                wasInterrupted = true;
            }
        }
        waiters.remove();
        // 退出时重新声明中断状态
        if (wasInterrupted)
        {
            current.interrupt();
        }
    }
    public void unlock() {
        locked.set(false);
        LockSupport.unpark(waiters.peek());
    }
}

有blocker的可以传递给开发人员更多的现场信息,可以查看到当前线程的阻塞对象,方便定位问题

到此这篇关于教你如何使用Java多线程编程-LockSupport工具类的文章就介绍到这了,更多相关使用java的LockSupport工具类内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java中内核线程理论及实例详解

    1.概念 内核线程是直接由操作系统内核控制的,内核通过调度器来完成内核线程的调度并负责将其映射到处理器上执行.内核态下的线程执行速度理论上是最高的,但是用户不会直接操作内核线程,而是通过内核线程的接口--轻量级进程来间接的使用内核线程.这种轻量级进程就是所谓的线程. 2.优点 由于内核线程的支持,每一个线程都是一个独立的单元,因此就算某一个线程挂掉了,也不会导致整个进程挂掉. 3.缺点 这种实现方式也存在局限性.由于是基于内核线程实现的,所以当涉及到线程的操作时(创建.运行.切换等)就涉及到系统

  • 浅谈JAVA 线程状态中可能存在的一些误区

    BLOCKED 和 WAITING 的区别 BLOCKED 和 WAITING 两种状态从结果上来看,都是线程暂停,不会占用 CPU 资源,不过还是有一些区别的 BLOCKED 等待 Monitor 锁的阻塞线程的线程状态,处于阻塞状态的线程正在等待 Monitor 锁进入 synchronized   Block 或者 Method ,或者在调用 Object.wait 后重新进入同步块/方法.简单的说,就是线程等待 synchronized 形式的锁时的状态 下面这段代码中, t1 在等待

  • Java多线程面试题(面试官常问)

    进程和线程 进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的.系统运行一个程序即是从一个进程从创建.运行到消亡的过程.在Java中,当我们启动main函数时其实就是启动了一个JVM的进程,而mian函数所在的线程就是这个进程中的一个线程,称为主线程. 线程是比进程更小的执行单位.一个进程在其执行的过程中可以产生多个线程.与进程不同的是同类的多个线程共享进程的堆和方法区资源,但每个线程都有自己的程序计数器.虚拟机和本地方法栈,所以系统在产生一个线程,或在各个线程之间切换工作是,

  • Java多线程下载网图的完整案例

    Java多线程下载网图案例 此案例依赖--文件操作工具类(FileUtils) 使用 apache 的commons-io包下的FileUtilsimportorg.apache.commons.io.FileUtils; 下载commons-io包 官方API文档 点击即可下载,然后导入IDEA的库中或者项目中. 导包 首先创建一个下载器 步骤: 1.新建一个download类 2.在类中建立一个下载方法 下载方法需要接收2个变量,一个是url下载地址,一个是name文件名称 3.在下载方法中

  • Java线程数究竟设多少合理

    需求缘起 Web-Server通常有个配置,最大工作线程数,后端服务一般也有个配置,工作线程池的线程数量,这个线程数的配置不同的业务架构师有不同的经验值,有些业务设置为CPU核数的2倍,有些业务设置为CPU核数的8倍,有些业务设置为CPU核数的32倍. "工作线程数"的设置依据是什么,到底设置为多少能够最大化CPU性能,是本文要讨论的问题. 一些共性认知 在进行进一步深入讨论之前,先以提问的方式就一些共性认知达成一致. 1.提问:工作线程数是不是设置的越大越好? 回答:肯定不是的. 一

  • 基于Java网络编程和多线程的多对多聊天系统

    1.前言 程序实现基于星型结构(服务器接收来自各个客户端发送的信息,然后将信息传递给其他客户端界面并在其他客户端界面显示发送的信息) 2.类图 3.代码 客户端代码: package netProgram; import java.io.IOException; import java.net.Socket; import java.net.SocketAddress; public class Client implements ScreenInputInterface{ private So

  • java的多线程高并发详解

    1.JMM数据原子操作 read(读取)∶从主内存读取数据 load(载入):将主内存读取到的数据写入工作内存 use(使用):从工作内存读取数据来计算 assign(赋值):将计算好的值重新赋值到工作内存中 store(存储):将工作内存数据写入主内存 write(写入):将store过去的变量值赋值给主内存中的变量 lock(锁定):将主内存变量加锁,标识为线程独占状态 unlock(解锁):将主内存变量解锁,解锁后其他线程可以锁定该变量 2.来看volatile关键字 (1)启动两个线程

  • Java 用两个线程交替打印数字和字母

    前一段时间听马士兵老师讲课,讲到某公司的一个面试,两个线程,其中一个线程输出ABC,另一个线程输出123,如何控制两个线程交叉输出1A2B3C,由于本人多线程掌握的一直不是很好,所以听完这道题,个人感觉收获良多,这是一个学习笔记.这道题有多种解法,不过有些属于纯炫技,所以只记录常见的三种解法.首先看第一种 1. park 和 unpark package cn.bridgeli.demo;   import com.google.common.collect.Lists;   import ja

  • Java利用线程工厂监控线程池的实现示例

    ThreadFactory 线程池中的线程从哪里来呢?就是ThreadFoctory public interface ThreadFactory { Thread newThread(Runnable r); } Threadfactory里面有个接口,当线程池中需要创建线程就会调用该方法,也可以自定义线程工厂 public class ThreadfactoryText { public static void main(String[] args) { Runnable runnable=

  • Java结束线程的三种方法及该如何选择

    java常用的结束一个运行中的线程的方法有3中:使用退出标志,使用interrupt方法,使用stop方法. 1.使用退出标志 即在线程内部定义一个bool变量来判断是否结束当前的线程: public class ThreadSafe extends Thread { public volatile boolean exit = false; public void run() { while (!exit){ //do work } } public static void main(Stri

  • Java线程实现时间动态显示

    本文实例为大家分享了Java线程实现时间动态显示的具体代码,供大家参考,具体内容如下 代码如下: import javax.swing.*; import java.awt.*; import java.util.Date; public class Test1 { public static void main(String[] args) { JFrame frame = new JFrame("我的窗口"); frame.setBounds(200,200,400,400); J

  • Java并发编程之线程之间的共享和协作

    一.线程间的共享 1.1 ynchronized内置锁 用处 Java支持多个线程同时访问一个对象或者对象的成员变量 关键字synchronized可以修饰方法或者以同步块的形式来进行使用 它主要确保多个线程在同一个时刻,只能有一个线程处于方法或者同步块中 它保证了线程对变量访问的可见性和排他性(原子性.可见性.有序性),又称为内置锁机制. 对象锁和类锁 对象锁是用于对象实例方法,或者一个对象实例上的 类锁是用于类的静态方法或者一个类的class对象上的 类的对象实例可以有很多个,但是每个类只有

  • JAVA多线程抢红包的实现示例

    大体思路 红包的分发见JAVA作业--红包分发. 而抢红包要解决的是线程问题. 其实比较简单,设定好人数,每个人一个线程,每个线程执行一遍,有红包就抢,没有红包就抢不到,所以run函数中只要判断现在还有没有红包就可以了. 代码实现 import java.util.Random; import java.util.Scanner; public class Main { public static void main(String[] args) { int person_num, red_po

  • Java多线程之线程池七个参数详解

    ThreadPoolExecutor是JDK中的线程池实现,这个类实现了一个线程池需要的各个方法,它提供了任务提交.线程管理.监控等方法. 下面是ThreadPoolExecutor类的构造方法源码,其他创建线程池的方法最终都会导向这个构造方法,共有7个参数:corePoolSize.maximumPoolSize.keepAliveTime.unit.workQueue.threadFactory.handler. public ThreadPoolExecutor(int corePoolS

  • Java实现UDP多线程在线咨询

    本文实例为大家分享了Java实现UDP多线程在线咨询,供大家参考,具体内容如下 1.发送的线程 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetSocketAddress; import jav

随机推荐