>  기사  >  Java  >  자바 프로그래밍 사고 학습 수업(8) 21장 - 동시성

자바 프로그래밍 사고 학습 수업(8) 21장 - 동시성

php是最好的语言
php是最好的语言원래의
2018-08-09 15:01:541327검색

순차 프로그래밍 즉, 프로그램의 모든 항목은 언제든지 한 단계만 수행할 수 있습니다. 동시 프로그래밍, 프로그램은 프로그램의 여러 부분을 병렬로 실행할 수 있습니다.

21.2.1 작업 정의

 스레드가 작업을 구동할 수 있으므로 Runnable 인터페이스에서 제공할 수 있는 작업을 설명하는 방법이 필요합니다. 작업을 정의하려면 Runnable 인터페이스를 구현하고 작업이 명령을 실행할 수 있도록 run() 메서드를 작성하면 됩니다.
클래스가 Runnable에서 파생되면 run() 메서드가 있어야 하지만 이 메서드에는 특별한 것이 없습니다. 내장 함수를 생성하지 않습니다. 스레드 기능. 스레드 동작을 구현하려면 Runnable接口来提供。要想定义任务,只需实现Runnable接口并编写run()方法,使得该任务可以执行你的命令。
当从Runnable导出一个类时,它必须具有run()方法,但是这个方法并无特殊之处——它不会产生任何内在的线程能力。要实现线程行为,你必须显式地将一个任务附着到线程上

21.2.3 使用Executor

  FixedThreadPoolCachedThreadPool  

  • FixedThreadPool, 可以一次性预先执行代价高昂的线程分配,因而也就可以限制线程的数量了。这可以节省时间,因为你不用为每个任务都固定地付出创建线程的开销。在事件驱动的系统中,需要线程的事件处理器,通过直接从池中获取线程,也可以如你所愿地得到服务。你不会滥用可获得的资源,因为FixedThreadPool使用的Thread对象的数量是有界的。

  注意,在任何线程池中,现有线程在可能的情况下,都会被自动复用。

  • 尽管本书将使用CachedThreadPool,但是也应该考虑在产生线程的代码中使用FiexedThreadPoolCachedThreadPool在程序执行过程中通常会创建与所需数量相同的线程,然后在它回收旧线程时停止创建新线程,因此它是合理的Executor的首选。只有当这种方式会引发问题时,你才需要切换到FixedThreadPool

  • SingleThreadExecutor就像是线程数量为1FixedThreadPool。(它还提供了一种重要的并发保证,其他线程不会(即没有两个线程会)被调用。这会改变任务的加锁需求)
    如果向SingleThreadExecutor提交了多个任务,那么这些任务将排队,每个任务都会在下一个任务开始之前运行结束,所有的任务将使用相同的线程。在下面的示例中,你可以看到每个任务都是按照它们被提交的顺序,并且是在下一个任务开始之前完成的。因此,SingleThreadExecutor会序列化所有提交给它的任务,并会维护它自己(隐藏)的悬挂任务队列。

21.2.4 从任务中产生返回值

  Runnable是执行工作的独立任务,但是它不返回任务值。如果你希望任务在完成时能够返回一个值,那么可以实现Callable接口而不是Runnable接口。在Java SE5中引入的Callable是一种具有类型参数的泛型,它的类型参数表示的是从方法call()(而不是run())中返回的值,并且必须使用ExecutorService.submit()方法调用它。

21.2.9 编码的变体

  另一种可能会看到的惯用法是自管理的Runnable

  这与从Thread继承并没有什么特别的差异,只是语法稍微晦涩一些。但是,实现接口使得你可以继承另一个不同的类,而从Thread继承将不行。

  注意,自管理的Runnable是在构造器中调用的。这个示例相当简单,因此可能是安全的,但是你应该意识到,在构造器中启动线程可能会变得很有问题,因为另一个任务可能会在构造器结束之前开始执行,这意味着该任务能够访问处于不稳定状态的对象。这是优选Executor而不是显式地创建Thread对스레드에 작업을 명시적으로 연결

해야 합니다.

21.2.3 Executor

FixedThreadPoolCachedThreadPool 사용

FixedThreadPool 비용이 많이 드는 스레드 할당을 한 번에 미리 수행할 수 있어 스레드 수를 제한할 수 있습니다. 이렇게 하면 각 작업에 대해 스레드를 생성하는 고정 오버헤드를 지불할 필요가 없으므로 시간이 절약됩니다. 이벤트 기반 시스템에서는 스레드가 필요한 이벤트 핸들러를 풀에서 직접 스레드를 가져와서 원하는 대로 제공할 수도 있습니다. FixThreadPool에서 사용하는 Thread 객체의 수가 제한되어 있으므로 사용 가능한 리소스를 남용하지 않습니다.

🎜  모든 스레드 풀에서는 가능하면 기존 스레드가 자동으로 재사용됩니다. 🎜
  • 🎜이 책에서는 CachedThreadPool을 사용하지만 스레드를 생성하는 코드에서는 FiexedThreadPool 사용도 고려해야 합니다. CachedThreadPool은 일반적으로 프로그램 실행 중에 필요한 만큼의 스레드를 생성한 다음 오래된 스레드를 재활용할 때 새 스레드 생성을 중지하므로 Executor 첫 번째 선택에 적합합니다. 이 접근 방식으로 인해 문제가 발생하는 경우에만 FixedThreadPool로 전환해야 합니다. 🎜
  • 🎜SingleThreadExecutor는 스레드 수가 1FixedThreadPool과 같습니다. (또한 다른 스레드(즉, 두 개의 스레드가 호출되지 않음)가 호출되지 않는다는 중요한 동시성 보장을 제공합니다. 이는 작업의 잠금 요구 사항을 변경합니다.)
    SingleThreadExecutor를 묻는 경우 여러 작업이 제출되면 작업이 대기열에 추가되고 다음 작업이 시작되기 전에 각 작업이 완료될 때까지 실행됩니다. 아래 예에서는 각 작업이 제출된 순서대로 완료되고 다음 작업이 시작되기 전에 완료되는 것을 볼 수 있습니다. 따라서 SingleThreadExecutor는 제출된 모든 작업을 직렬화하고 보류 중인 작업의 자체(숨겨진) 대기열을 유지 관리합니다. 🎜
🎜21.2.4 작업에서 반환 값 생성🎜🎜 Runnable은 작업을 수행하는 독립적인 작업이지만 작업 값을 반환하지는 않습니다. 작업이 완료되었을 때 값을 반환하도록 하려면 Runnable 인터페이스 대신 Callable 인터페이스를 구현할 수 있습니다. Java SE5에 도입된 Callable은 유형 매개변수가 있는 일반 유형입니다. 해당 유형 매개변수는 run()이 아닌 call() 메소드를 나타냅니다. 이며 ExecutorService.submit() 메서드를 사용하여 호출해야 합니다. 🎜🎜21.2.9 코딩 변형 🎜🎜   여러분이 볼 수 있는 또 다른 관용구는 자체 관리되는 Runnable입니다. 🎜🎜  이는 Thread에서 상속하는 것과 특별히 다르지 않지만 구문이 약간 더 모호합니다. 그러나 인터페이스를 구현하면 다른 클래스에서 상속할 수 있지만 Thread에서 상속하면 그렇지 않습니다. 🎜🎜  자체 관리형 Runnable이 생성자에서 호출된다는 점에 유의하세요. 이 예제는 매우 간단하므로 안전할 수 있지만 생성자가 끝나기 전에 다른 작업이 실행되기 시작할 수 있으므로 생성자에서 스레드를 시작하면 문제가 될 수 있다는 점을 알아야 합니다. 즉, 작업이 불안정한 상태의 개체에 액세스할 수 있다는 뜻입니다. 이것이 Thread 개체를 명시적으로 생성하는 것보다 Executor를 선호하는 또 다른 이유입니다. 🎜🎜21.2.13 스레드 그룹🎜🎜🎜  스레드 그룹은 스레드 모음을 보유하고 있습니다. 스레드 그룹의 가치는 Joshua Bloch의 말을 인용하여 요약할 수 있습니다. "스레드 그룹은 무시할 수 있는 실패한 시도로 생각하는 것이 가장 좋습니다."

 나처럼 스레드 그룹의 가치를 발견하기 위해 많은 시간과 노력을 쏟았다면 수년 동안 같은 질문을 했지만 이 주제에 대한 Sun의 공식 성명이 없는 이유에 놀랄 수도 있습니다. 나는 또한 Java에서 발생한 다른 변경 사항에 대해서도 여러 번 물었습니다. 이 문제는 노벨 경제학상 수상자인 조셉 스티글리츠(Joseph Stiglitz)의 삶의 철학으로 설명할 수 있습니다. 이를 '확진 헌신 이론'이라고 합니다. 곰.”

21.2.14 예외 잡기

 스레드의 특성상 스레드에서 벗어나는 예외는 잡을 수 없습니다. 예외가 작업의 run() 메서드를 벗어나면 이 잘못된 예외를 잡기 위해 특별한 조치를 취하지 않는 한 콘솔로 전파됩니다. run()方法,它就会向外传播到控制台,除非你采取特殊的步骤捕获这种错误的异常。

21.3 共享受限资源

  可以把单线程程序当作在问题域求解的单一实体,每次只能做一件事情。

21.3.1 不正确地访问资源

  因为canceled标志是boolean类型的,所以它是原子性的,即诸如赋值和返回值这样的简单操作在发生时没有中断的可能,因此你不会看到这个域处于在执行这些简单操作的过程中的中间状态。

  有一点很重要,那就是要注意到递增程序自身也需要多个步骤,并且在递增过程中任务可能会被纯种机制挂起——也就是说,在Java中,递增不是原子性的操作。因此,如果不保护任务,即使单一的递增也不是安全的。

21.4 终结任务

21.4.3 中断

  Executor上调用shutdownNow(),它将发送一个interrupt()调用给它启动的所有线程。

  Executor 通过调用submit()而不是excutor()来启动任务,就可以持有该任务的上下文。submit()将返回一个泛型的Future<?>,持有这种Future的关键在于你可以在其上调用cancel(),并因此可以使用它来中断某个特定任务。如果你将true传递给cancel(),那么它就会拥有在该线程上调用interrupt()以停止这个线程的权限。因此,cancel()是一个种中断由Excutor启动的单个线程的方式。

  SleepBlock()是可中断的阻塞,而IOBlockedSynchronizedBlocked是不可中断的阻塞。上面三个类的示例证明I/O和在synchronized块上的等待是不可中断的。无论是I/O还是尝试调用synchronized方法,都不需要任何InterruptedException处理器。
从关于上面三个类的示例的输出中可以看到,你能够中断对sleep()的调用(或者任何要求抛出InterruptedException的调用)。但是,你不能中断试图获取synchronized锁或者试图执行I/O操作的线程。这有点令人烦恼,特别是在妊I/O的任务时,因为这意味着IO具有锁住你的多线程程序的潜在可能。特别是对于基于Web的程序,这更是关乎利害。

  对于这类问题,有一个略显笨拙但是有时确实行之有效的解决方案,即关闭任务在其上发生阻塞的底层资源:

21.5 线程之间的协作

21.5.1 wait()与notifyAll()

  wait()使你可以等待某个条件发生变化,而改变这个条件超出了当前方法的控制能力。通常,这种条件将由另一个任务来改变。你肯定不想在你的任务测试这个条件的同时,不断地进行空循环,这被称为忙等待, 通常是一种不良的周期使用方式。因此wait()会在等等外部世界产生变化的时候将任务挂起,并且只有在notify()notifyAll() 发生时,即表示发生了某些感兴趣的事物,这个任务才会被唤醒并去检查所产生的变化。因此,wait()提供了一种在任务之间对活动同步的方式。

  调用sleep()的时候锁并没有被 释放,调用yield()也属于这种情况,理解这一点很重要。
wait(), notify()以及notifyAll()有一个比较特殊的方面,那就是这些方法是基类Object的一个部分,而不是属于Thread

21.3 제한된 리소스 공유

  단일 스레드 프로그램은 문제 영역을 해결하고 한 번에 한 가지 작업만 수행할 수 있는 단일 개체로 간주될 수 있습니다.

21.3.1 리소스에 대한 잘못된 액세스🎜🎜  canceled 플래그가 boolean 유형이기 때문에 원자적입니다. 즉, 간단한 할당 및 반환 값 작업이 중단 없이 발생하므로 이러한 간단한 작업을 수행하는 동안 중간 상태의 도메인은 표시되지 않습니다. 🎜🎜  증분 절차 자체에는 여러 단계가 필요하며 증분 프로세스 중에 순수 메커니즘에 의해 작업이 일시 중단될 수 있다는 점에 유의하는 것이 중요합니다. 즉, Java에서 증분은 원자성 작동이 아닙니다. 따라서 작업을 보호하지 않으면 단 한 번의 증분도 안전하지 않습니다. 🎜

21.4 작업 종료

🎜21.4.3 인터럽트 🎜🎜  Executor에서 shutdownNow()를 호출하면 interrupt() 해당 스레드에 의해 시작된 모든 스레드에 대해 호출됩니다. 🎜🎜  <code>Executor는 작업을 시작하기 위해 excutor() 대신 submit()를 호출하여 작업의 컨텍스트를 보유할 수 있습니다. submit()은 일반 Future<?>를 반환합니다. 이 Future를 유지하는 열쇠는 에서 호출할 수 있다는 것입니다. >cancel()이므로 특정 작업을 중단하는 데 사용할 수 있습니다. cancel()true를 전달하면 해당 스레드에서 interrupt()를 호출하여 중지할 수 있는 권한을 갖게 됩니다. 따라서 cancel()Excutor에 의해 시작된 단일 스레드를 중단하는 방법입니다. 🎜🎜 SleepBlock()은 중단 가능한 차단이고, IOBlockedSynchronizedBlocked는 무중단 차단입니다. 위 세 가지 클래스의 예는 I/O 및 동기화 블록 대기가 중단되지 않음을 증명합니다. I/O나 synchronized 메서드 호출 시도에는 InterruptedException 핸들러가 필요하지 않습니다.
위 세 클래스의 예제 출력에서 ​​볼 수 있듯이 sleep() 호출(또는 InterruptedException을 발생시켜야 하는 다른 호출)을 중단할 수 있습니다. > 전화). 그러나 동기화 잠금을 획득하려고 하거나 I/O 작업을 수행하려고 하는 스레드는 중단할 수 없습니다. 이는 특히 I/O 작업을 수행할 때 약간 짜증나는 일입니다. 이는 IO가 다중 스레드 프로그램을 잠글 가능성이 있다는 것을 의미하기 때문입니다. 특히 웹 기반 프로그램의 경우 이는 중요한 문제입니다. 🎜🎜  이 유형의 문제에 대한 약간 서투르지만 때로는 효과적인 해결책은 작업이 차단된 기본 리소스를 닫는 것입니다. 🎜

21.5 스레드 간 협력

🎜21.5 .1 wait() 및 informAll() 🎜🎜 wait()를 사용하면 특정 조건이 변경될 때까지 기다릴 수 있으며, 이 조건을 변경하는 것은 현재 메서드의 제어 범위를 벗어납니다. 종종 이 조건은 다른 작업에 의해 변경됩니다. 작업이 이 조건을 테스트하는 동안 빈 루프를 계속 수행하고 싶지는 않을 것입니다. 이를 바쁜 대기라고 하며 일반적으로 주기를 잘못 사용하는 것입니다. 따라서 wait()는 외부 세계의 변경을 기다리는 동안 작업을 일시 중지하며 notify() 또는 notifyAll()이 실행될 때만 작업을 일시 중지합니다. 발생한다는 것은 관심 있는 일이 발생했음을 의미하며 작업이 활성화되어 변경 사항을 확인하게 됩니다. 따라서 wait()는 작업 간 활동을 동기화하는 방법을 제공합니다. 🎜🎜  sleep()이 호출될 때 잠금이 해제되지 않습니다. 이는 yield()를 호출할 때도 마찬가지입니다.
wait(), notify()notifyAll()에는 특별한 측면이 있습니다. 즉, 이러한 메서드는 기본 클래스 A입니다. 스레드의 일부가 아닌 객체의 일부입니다. 🎜🎜 신호를 놓쳤습니다. 🎜

21.5.2 inform() 및 informAll()

  Java의 스레딩 메커니즘에 대한 논의에서 혼란스러운 설명이 있습니다. notifyAll()은 "다음 등의 모든 작업"을 깨울 것입니다. 이는 wait() 상태의 모든 작업이 프로그램의 어느 곳에서나 notifyAll()에 대한 호출에 의해 활성화된다는 의미입니까? 이것이 사실이 아님을 보여주는 예가 있습니다. 실제로 특정 잠금에 대해 notifyAll()이 호출되면 이 잠금을 기다리는 작업만 활성화됩니다. notifyAll()将唤醒“所有下在等等的任务”。这是否意味着在程序中任何地方,任何处于wait()状态中的任务都将被任何对notifyAll()的调用唤醒呢?有示例说明情况并非如此——事实上,当notifyAll()因某个特定锁而被调用时,只有等待这个锁的任务才会被唤醒。

21.6 死锁

  由Edsger Dijkstrar提出的哲学家就餐问题是一个经典的死锁例证。

  要修正死锁问题,你必须明白,当以下四个条件同时满足时,就会发生死锁:

  • 互斥条件。任务使用的资源中至少有一个是不能共享的。这里,一根Chopstick一次就只能被一个Philosopher使用。

  • 至少有一个任务它必须持有一个资源且正在等待获取一个当前被别的任务持有的资源。也就是说,要发生死锁,Philosopher必须拿着一根Chopstick并且等待另一根。

  • 资源不能被任务抢占,任务必须把资源释放当作普通事件。Philosopher很有礼貌,他们不会从其他Philosopher那里抢占Chopstick。

  • 必须有循环等待,这时,一个任务等待其他任务所持有的资源,后者又在等待另一个任务所持有的浆,这样一直下去,直到有一个任务在等待第一个任务所持有的资源,使得大家都被锁住。在DeadlockingDiningPhilosophers.java中,因为每个Philosopher都试图先得到右边的Chopstick,然后得到左边的Chopstick,所以发徨了循环等待。

  所以要防止死锁的话,只需破坏其中一个即可。防止死锁最容易的方法是破坏第4个条件。

21.7 新类库中的构件

21.7.1 CountDownLatch

  适用场景:它被用来同步一个或多个任务,强制它们等待由其他任务执行的一组操作完成。即一个或多个任务需要等待,等待到其它任务,比如一个问题的初始部分,完成为止。

  你可以向CountDownLatch对象设置一个初始值,任何在这个对象上调用wait()的方法都将阻塞,直到这个计数值到达0.其他因结束其工作时,可以在访对象上调用countDown()来减小这个计数值。CountDownLatch被设计为只解发一次,计数值不能被重置。如果你需要能够重置计数值的版本,则可以使用CyclicBarrier

  调用countDown()的任务在产生这个调用时并没有被阻塞,只有对await()的调用会被阻塞,直至计数值到达0

  CountDownLatch的典型用法是将一个程序分为n个互相独立的可解决任务,并创建值为nCountDownLatch。当每个任务完成时,都会在这个锁存器上调用countDown()。等待问题被解决的任务在这个锁存器上调用await(),将它们自己挂起,直至锁存器计数结束。

21.7.2 CyclicBarrier

  适用于这样的情况:你希望创建一组任务,它们并行地执行工作,然后在进行下一下步骤之前等待,直至所有任务都完成(看起来有些像Join())。它使得所有的并行任务都将在栅栏处列队,因此可以一致地向前移动。

  例如程序赛马程序:HorseRace.java

21.7.3 DelayQueue

  DelayQueue是一个无界的BlockingQueue(同步队列),用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。这种队列是有序的,即队头对象是最先到期的对象。如果没有到期的对象,那么队列就没有头元素,所以poll()将返回null(也正因为此,我们不能将null放置到这种队列中)。如上所述,DelayQueue

21.6 교착 상태

  Edsger Dijkstrar가 제안한 식사 철학자 문제는 교착 상태의 전형적인 예입니다.

  교착 상태 문제를 해결하려면 다음 네 가지 조건이 동시에 충족될 때 교착 상태가 발생한다는 점을 이해해야 합니다.

  • 상호 배타적 조건. 작업에 사용되는 리소스 중 하나 이상을 공유할 수 없습니다. 여기서 젓가락은 한 번에 한 명의 철학자만 사용할 수 있습니다. 🎜
  • 🎜적어도 하나의 작업은 리소스를 보유해야 하며 현재 다른 작업이 보유하고 있는 리소스를 획득하기를 기다리고 있습니다. 즉, 교착 상태가 발생하려면 철학자는 젓가락 하나를 잡고 다른 젓가락을 기다려야 합니다. 🎜
  • 🎜리소스는 작업에 의해 선점될 수 없으며 작업은 리소스 해제를 일반적인 이벤트로 처리해야 합니다. 철학자는 예의바르며 다른 철학자에게서 젓가락을 가져가지 않습니다. 🎜
  • 🎜 이때 한 작업은 다른 작업이 보유한 자원을 기다리고 있고, 후자는 다른 작업이 보유한 펄프를 기다리고 있습니다. 첫 번째 작업이 보유한 리소스를 기다리는 동안 모든 사람이 잠깁니다. DeadlockingDiningPhilosophers.java에서는 각 Philosopher가 먼저 오른쪽에 있는 젓가락을 가져오려고 시도한 다음 왼쪽에 있는 젓가락을 가져오려고 시도하므로 루프 대기가 발생합니다. 🎜
🎜  따라서 교착 상태를 방지하려면 그중 하나만 파괴하면 됩니다. 교착상태를 방지하는 가장 쉬운 방법은 조건 4를 위반하는 것입니다. 🎜

21.7 새 클래스 라이브러리의 구성 요소

🎜21.7.1 CountDownLatch🎜🎜  적용 가능한 시나리오: 하나 이상의 작업을 동기화하여 다른 작업이 수행한 일련의 작업이 완료될 때까지 기다리도록 하는 데 사용됩니다. 작업. 즉, 하나 이상의 작업은 문제의 초기 부분과 같은 다른 작업이 완료될 때까지 기다려야 합니다. 🎜🎜  CountDownLatch 개체에 초기 값을 설정할 수 있습니다. 이 개체에서 wait()를 호출하는 모든 메서드는 카운트 값이 0에 도달할 때까지 차단됩니다. 다른 요소가 작업을 완료하면 countDown( ) 액세스 개체에 대해 카운트 값을 줄입니다. CountDownLatch는 한 번만 내보내도록 설계되었으며 카운트 값은 재설정될 수 없습니다. 카운트를 재설정하는 버전이 필요한 경우 CyclicBarrier를 사용할 수 있습니다. 🎜🎜  이 호출이 이루어질 때 countDown()을 호출하는 작업은 차단되지 않습니다. 카운트 값이 0에 도달할 때까지 <code>await()에 대한 호출만 차단됩니다. . 🎜🎜   CountDownLatch의 일반적인 사용법은 프로그램을 n개의 독립적인 해결 가능한 작업으로 나누고 값이 n >CountDownLatch인 를 생성하는 것입니다. 코드>. 각 작업이 완료되면 이 래치에서 <code>countDown()이 호출됩니다. 문제가 해결되기를 기다리는 작업은 이 래치에서 await()를 호출하여 래치 카운트가 끝날 때까지 스스로를 일시 중단합니다. 🎜🎜21.7.2 CyclicBarrier🎜🎜  작업을 병렬로 수행하는 일련의 작업을 생성한 후 다음 단계로 진행하기 전에 모든 작업이 완료될 때까지 기다리려는 상황에 적합합니다(조금 Join()과 유사함). 이로 인해 모든 병렬 작업이 펜스에 대기하게 되므로 균일하게 앞으로 이동합니다. 🎜🎜  예를 들어, 경마 프로그램: HorseRace.java🎜🎜21.7.3 DelayQueue🎜🎜  DelayQueue는 무제한 BlockingQueue(동기화 대기열)입니다. Delayed 인터페이스의 객체 구현. 객체가 만료될 때만 대기열에서 제거될 수 있습니다. 이 대기열은 순서가 지정되어 있습니다. 즉, 헤드 개체가 가장 먼저 만료됩니다. 만료된 객체가 없으면 대기열에 헤드 요소가 없으므로 poll()null을 반환합니다(이 때문에 null을 전달할 수 없습니다). 코드> 코드> 이 대기열에 배치됨). 위에서 언급했듯이 DelayQueue는 우선순위 대기열의 변형이 됩니다. 🎜🎜21.7.4 PriorityBlockingQueue🎜🎜  이는 읽기 작업을 차단하는 매우 기본적인 우선순위 대기열입니다. 이 큐의 차단 특성은 필요한 모든 동기화를 제공하므로 여기서 명시적인 동기화가 필요하지 않다는 점에 유의해야 합니다. 큐에서 읽을 때 이 큐에 요소가 있는지 여부에 대해 걱정할 필요가 없습니다. 대기열 요소가 없으면 리더가 직접 차단됩니다. 🎜

21.7.5 ScheduledExecutor를 사용한 실내 온도 조절기

 “온실 제어 시스템”은 동시성 문제로 간주될 수 있으며, 원하는 각 온실 이벤트는 예정된 시간에 실행되는 작업입니다.
ScheduledThreadPoolExecutor가 이 문제를 해결할 수 있습니다. 그 중, Schedule()은 작업을 한 번 실행하는 데 사용되고, ScheduleAtFixedRate()는 지정된 시간마다 작업을 반복적으로 실행하는 데 사용됩니다. 두 메소드 모두 DelayTime 매개변수를 수신합니다. 실행 가능한 개체는 미래의 특정 시점에 실행되도록 설정할 수 있습니다. ScheduledThreadPoolExecutor可以解决这种问题。其中schedule()用来运行一次任务,scheduleAtFixedRate()每隔规定的时间重复执行任务。两个方法接收delayTime参数。可以将Runnable对象设置为在将来的某个时刻执行。

21.7.6 Semaphre

21.8  仿真

21.8.1 银行出纳员

21.8.2 饭店仿真

  BlockingQueue: 同步队列,当第一个元素为空或不可用时,执行.take()时,等待(阻塞、Blocking)。

  SynchronousQueue

21.7.6 Semaphre

21.8 Simulation

21.8.1 Bank Teller

21.8.2 Restaurant Simulation

BlockingQueue: 동기 대기열, 첫 번째 요소가 비어 있거나 사용할 수 없는 경우 실행 시 . take(), 대기(차단, 차단).

SynchronousQueue: 내부 용량이 없는 차단 대기열이므로 각 put()은 take()를 기다려야 하고 그 반대도 마찬가지입니다(즉, 각 take()는 put을 기다려야 합니다) ()). 이는 마치 누군가에게 물건을 건네주는 것과 같습니다. 물건을 놓을 테이블이 없으므로, 그 사람이 손을 뻗어 물건을 받을 준비가 되어 있는 경우에만 작업할 수 있습니다. 이 경우, 동기식 대기열은 언제든지 하나의 요리만 제공될 수 있다는 개념을 강화하기 위해 식당 앞에 설정된 위치를 나타냅니다.

 이 예제에서 관찰해야 할 매우 중요한 점 중 하나는 대기열을 사용하여 작업 간 통신에 따른 관리 복잡성입니다. 이 단일 기술은 제어를 반전시켜 동시 프로그래밍 프로세스를 크게 단순화합니다. 작업은 서로 직접 간섭하지 않고 대신 대기열을 통해 서로 개체를 보냅니다. 수신 작업은 개체를 처리하고 개체에 메시지를 보내는 대신 메시지로 처리합니다. 가능할 때마다 이 기술을 따르면 강력한 동시 시스템을 구축할 가능성이 크게 높아집니다.


21.8.3 작업 배포

    21.9 성능 조정
  • 21.9.1 뮤텍스 기술 비교

  • "마이크로 벤치마킹"의 위험: 이 용어는 일반적으로 문맥을 벗어나 격리된 기능의 성능 테스트를 의미합니다. 물론 "잠금이 동기화되는 것보다 빠릅니다"와 같은 주장을 확인하기 위한 테스트를 작성해야 하지만 이러한 테스트를 작성할 때 컴파일 및 런타임 시 실제로 어떤 일이 발생하는지 알아야 합니다.
  •  이 점에서는 컴파일러와 런타임 시스템이 다르기 때문에 정확히 무슨 일이 일어날지 알기는 어렵지만 컴파일러가 결과의 가능성을 예측하는 것을 방지해야 합니다.

  •   일반적으로 Lock을 사용하는 것이 동기화를 사용하는 것보다 훨씬 더 효율적이며 동기화의 오버헤드가 너무 많이 변하는 것 같지만 Lock은 상대적으로 일관됩니다.
동기화 키워드를 절대 사용하면 안 된다는 뜻인가요? 여기서 고려해야 할 두 가지 요소가 있습니다:

첫째, 상호 배타적 메서드의 메서드 본문 크기입니다.

두 번째, 동기화된 키워드로 생성된 코드는 Lock에서 요구하는 "lock-try/finally-unlock" 관용구로 생성된 코드보다 훨씬 더 읽기 쉽습니다.

  코드는 작성된 것보다 훨씬 더 많이 읽혀집니다. 프로그래밍을 할 때는 컴퓨터와 소통하는 것보다 다른 사람과 소통하는 것이 훨씬 더 중요하므로 코드의 가독성이 중요합니다. 따라서 동기화된 키워드로 시작하고 성능 조정 중에 이를 Lock 개체로 바꾸는 것이 실질적인 의미가 있습니다.

21.9.2 잠금 없는 컨테이너

이러한 잠금 없는 창에 대한 일반적인 전략은 다음과 같습니다. 판독기가 완료된 수정의 결과만 볼 수 있는 한 컨테이너 수정은 읽기 작업과 동시에 발생할 수 있습니다. 수정은 컨테이너 데이터 구조 일부의 별도 복사본(때때로 전체 데이터 구조의 복사본)에서 수행되며 이 복사본은 수정 프로세스 중에 표시되지 않습니다. 수정이 완료되어야 수정된 구조가 기본 데이터 구조와 자동으로 교환되어 독자가 수정 내용을 볼 수 있습니다. 🎜🎜  낙관적 잠금 🎜🎜  잠금이 없는 컨테이너에서 주로 읽는 동안 잠금을 획득하고 해제하는 오버헤드가 제거되므로 동기화된 컨테이너보다 훨씬 빠릅니다. 잠금이 없는 컨테이너에 소수의 쓰기를 수행해야 하는 경우에도 마찬가지이지만 "소량"으로 간주되는 것은 무엇입니까? 이것은 매우 흥미로운 질문입니다. 🎜🎜21.11 요약🎜🎜 스레드의 또 다른 이점은 무거운 프로세스 컨텍스트 스위치(수천 개의 명령어) 대신 가벼운 실행 컨텍스트 스위치(약 100개의 명령어)를 제공한다는 것입니다. 주어진 프로세스 내의 모든 스레드는 동일한 메모리 공간을 공유하기 때문에 경량 컨텍스트 전환은 프로그램의 실행 순서와 지역 변수만 변경합니다. 프로세스 스위치(무거운 컨텍스트 스위치)는 모든 메모리 공간을 변경해야 합니다. 🎜🎜관련 기사: 🎜

Java 프로그래밍 사고 학습 수업(6) 19장 - 열거 유형

Java 프로그래밍 사고 학습 수업(7) 20장 - 주석

위 내용은 자바 프로그래밍 사고 학습 수업(8) 21장 - 동시성의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.