java.util.concurrent,第2部分

    技术2024-04-05  86

    并发集合通过提供线程安全的,经过良好调整的数据结构,使并发编程更加容易。 但是,在某些情况下,开发人员需要更进一步,考虑调整和/或限制线程执行。 鉴于java.util.concurrent的全部要点是简化多线程编程,您可能希望该程序包包含同步实用程序—确实如此。

    本文是第1部分的后续文章,介绍了几种同步结构,这些结构比核心语言原语(监视器)的级别更高,但程度不高,以至于它们被埋藏在Collection类中。 一旦知道它们的用途,使用这些锁和大门非常简单。

    关于本系列

    所以您认为您了解Java编程吗? 事实是,大多数开发人员从头开始学习Java平台,仅学习足够的知识即可完成工作。 在本系列中,Ted Neward深入挖掘了Java平台的核心功能,以发现鲜为人知的事实,这些事实可以帮助您解决最棘手的编程难题。

    1.信号量

    在某些企业系统中,开发人员需要针对特定​​资源限制打开请求(线程/操作)的数量并不少见-实际上,限制有时可以通过减少针对特定资源的争用量来提高系统的吞吐量。资源。 虽然当然可以尝试手动编写节流代码,但是使用semaphore类会更容易,它可以为您节流,如清单1所示:

    清单1.使用信号量进行调节
    import java.util.*;import java.util.concurrent.*; public class SemApp { public static void main(String[] args) { Runnable limitedCall = new Runnable() { final Random rand = new Random(); final Semaphore available = new Semaphore(3); int count = 0; public void run() { int time = rand.nextInt(15); int num = count++; try { available.acquire(); System.out.println("Executing " + "long-running action for " + time + " seconds... #" + num); Thread.sleep(time * 1000); System.out.println("Done with #" + num + "!"); available.release(); } catch (InterruptedException intEx) { intEx.printStackTrace(); } } }; for (int i=0; i<10; i++) new Thread(limitedCall).start(); } }

    即使此示例中的10个线程正在运行(您可以通过对运行SemApp的Java进程执行jstack进行验证),但只有三个SemApp处于活动状态。 其他七个被搁置,直到其中一个信号量计数被释放。 (实际上, Semaphore类支持一次获取和释放多个许可证 ,但这在这种情况下是没有意义的。)

    2. CountDownLatch

    如果Semaphore是旨在允许线程一次“进入”的并发类(也许唤起了流行夜总会里蹦蹦跳跳的记忆),则CountDownLatch是赛马的起点。 此类将所有线程搁置在一起,直到满足特定条件为止,届时它将立即释放所有线程。

    清单2. CountDownLatch:让我们开始比赛吧!
    import java.util.*; import java.util.concurrent.*; class Race { private Random rand = new Random(); private int distance = rand.nextInt(250); private CountDownLatch start; private CountDownLatch finish; private List<String> horses = new ArrayList<String>(); public Race(String... names) { this.horses.addAll(Arrays.asList(names)); } public void run() throws InterruptedException { System.out.println("And the horses are stepping up to the gate..."); final CountDownLatch start = new CountDownLatch(1); final CountDownLatch finish = new CountDownLatch(horses.size()); final List<String> places = Collections.synchronizedList(new ArrayList<String>()); for (final String h : horses) { new Thread(new Runnable() { public void run() { try { System.out.println(h + " stepping up to the gate..."); start.await(); int traveled = 0; while (traveled < distance) { // In a 0-2 second period of time.... Thread.sleep(rand.nextInt(3) * 1000); // ... a horse travels 0-14 lengths traveled += rand.nextInt(15); System.out.println(h + " advanced to " + traveled + "!"); } finish.countDown(); System.out.println(h + " crossed the finish!"); places.add(h); } catch (InterruptedException intEx) { System.out.println("ABORTING RACE!!!"); intEx.printStackTrace(); } } }).start(); } System.out.println("And... they're off!"); start.countDown(); finish.await(); System.out.println("And we have our winners!"); System.out.println(places.get(0) + " took the gold..."); System.out.println(places.get(1) + " got the silver..."); System.out.println("and " + places.get(2) + " took home the bronze."); } } public class CDLApp { public static void main(String[] args) throws InterruptedException, java.io.IOException { System.out.println("Prepping..."); Race r = new Race( "Beverly Takes a Bath", "RockerHorse", "Phineas", "Ferb", "Tin Cup", "I'm Faster Than a Monkey", "Glue Factory Reject" ); System.out.println("It's a race of " + r.getDistance() + " lengths"); System.out.println("Press Enter to run the race...."); System.in.read(); r.run(); } }

    注意清单2中的CountDownLatch两个目的:首先,它同时释放所有线程,以模拟比赛的开始; 但是后来,另一个闩锁模拟了比赛的结束,从本质上讲,“主”线程可以打印出结果。 对于具有更多注释的比赛,您可以在比赛的“转弯”和“中途”点添加CountDownLatch es,因为马越过了距离的四分之一,一半和四分之三。

    3.执行者

    清单1和清单2中的示例都具有相当令人沮丧的缺陷,因为它们迫使您直接创建Thread对象。 这是麻烦的秘诀,因为在某些JVM中,创建Thread是一项重量级的操作,与重用现有Thread相比创建新Thread要好得多。 但是,在其他JVM中,情况恰恰相反: Thread非常轻巧,最好在需要时使用new Thread 。 当然,如果墨菲按照他的方式行事(他通常会这样做),那么对于您最终在其上进行部署的平台而言,使用的任何一种方法都是完全错误的。

    JSR-166专家组(请参阅参考资料 )在某种程度上预见了这种情况。 他们没有让Java开发人员直接创建Thread ,而是引入了Executor接口,该接口是用于创建新线程的抽象。 如清单3所示, Executor允许您创建线程,而不必自己new Thread对象:

    清单3.执行器
    Executor exec = getAnExecutorFromSomeplace(); exec.execute(new Runnable() { ... });

    使用Executor的主要缺点与我们在所有工厂遇到的缺点相同:工厂必须来自某个地方。 不幸的是,与CLR不同,JVM没有附带标准的VM级线程池。

    Executor类确实是获得Executor实例的常用场所,但是它只有new方法(例如,创建新的线程池)。 它没有预先创建的实例。 因此,如果您想在整个代码中创建和使用Executor实例,则需要您一个人做。 (或者,在某些情况下,您将能够使用所选容器/平台提供的实例。)

    ExecutorService,随时为您服务

    Executor接口虽然不必担心Thread的来源而有用,但它缺少Java开发人员可能期望的某些功能,例如能够启动旨在产生结果的线程并在非线程中等待的功能。阻止时尚,直到结果可用。 (这是台式机应用程序中的常见需求,在该应用程序中,用户将执行需要访问数据库的UI操作,但如果操作时间过长,则可能希望在操作完成之前取消该操作。)

    为此,JSR-166专家创建了一个更为有用的抽象,即ExecutorService接口,该接口将线程启动工厂建模为可以集体控制的服务。 例如,与为每个任务调用一次execute()相比, ExecutorService可以采用任务集合并返回表示每个任务的未来结果的期货列表 。

    4. ScheduledExecutorServices

    与ExecutorService接口一样,某些任务需要以计划的方式完成,例如以确定的间隔或在特定的时间执行给定的任务。 这是ScheduledExecutorService的省,该省扩展了ExecutorService 。

    如果您的目标是创建一个每五秒钟“ ping”一次的“心跳”命令,那么ScheduledExecutorService将使它像清单4一样简单:

    清单4. ScheduledExecutorService按时执行“ ping”
    import java.util.concurrent.*; public class Ping { public static void main(String[] args) { ScheduledExecutorService ses = Executors.newScheduledThreadPool(1); Runnable pinger = new Runnable() { public void run() { System.out.println("PING!"); } }; ses.scheduleAtFixedRate(pinger, 5, 5, TimeUnit.SECONDS); } }

    那个怎么样? 无需烦恼线程,无需烦恼用户想要取消心跳的操作,也无需明确将线程标记为前台或后台。 只需将所有那些调度详细信息留给ScheduledExecutorService 。

    顺便说一句,如果用户确实想要取消心跳,则scheduleAtFixedRate调用的返回结果将是ScheduledFuture实例,该实例不仅在有结果的情况下环绕结果,而且还具有cancel方法来关闭已调度的操作。

    5.超时方法

    在阻塞操作周围设置具体超时(从而避免死锁)的能力是java.util.concurrent库相对于其较早的并发表亲(例如,用于监视的监视器)的一大进步。

    这些方法几乎总是以int / TimeUnit对重载,表明对方法进行搁置并将控制权返回给程序之前应等待多长时间。 开发人员需要做更多的工作-如果不获得锁,您将如何恢复? -但是结果几乎总是更正确:更少的死锁和更多的安全生产代码。 (欲了解更多有关编写产品代码,看到迈克尔·尼加德的发布吧!在相关主题 。)

    结论

    java.util.concurrent软件包包含许多漂亮的实用程序,它们远远超出了Collections的范围,尤其是在.locks和.atomic软件包中。 深入研究,您还将发现有用的控件结构,例如CyclicBarrier等。

    像Java平台的许多方面一样,您无需费劲查找可以非常有用的基础结构代码。 每当您编写多线程代码时,请记住本文和上一篇文章中讨论的实用程序。


    翻译自: https://www.ibm.com/developerworks/java/library/j-5things5/index.html

    相关资源:微信小程序源码-合集6.rar
    Processed: 0.014, SQL: 9