线程通信主要通过共享对字段和对象的访问来发生。 尽管这种通信方式非常高效,但它易于出现诸如线程干扰和内存一致性之类的错误。 同步是一种有助于防止此类错误的工具。
但是,同步不是免费提供的,并且在访问当前由另一个线程持有的锁或对象时会引入延迟。 等待中的线程无法使用该对象,直到另一个线程释放该对象上的锁。 这种情况称为线程争用。 它还可能导致死锁和活锁。
在本文中,我们将探讨Java提供的用于处理线程同步的不同选项。
同步要点
Java提供了一系列机制来处理基本线程同步和协调。 它支持通过同步方法和同步语句进行细粒度的对象访问同步。 基本线程协调可以通过受保护的块来完成。 所有提到的机制都是围绕获取和释放对象的固有锁定而构建的。
内在锁
每个Java对象都有一个关联的固有锁。 需要对对象的字段进行独占访问的线程必须在访问对象之前获取对象的锁,然后在完成后释放固有的锁。 其他尝试访问该对象的线程将阻塞,直到持有锁的线程将其释放为止。
同步方法
当线程调用同步方法时 ,它获取该方法对象的内在锁 ,并在方法返回时释放它。 即使该方法由于未捕获的异常而返回,也将释放该锁定。 如果以静态方法完成,则线程将获取与该类关联的类对象的锁。
同步语句
提供更细粒度的同步机制。 同步语句必须指定提供内部锁的对象。 在分离的锁对象上进行同步可以提供字段同步,而无需强制方法调用之间进行同步。
守卫的块
如前所述,受保护的块为线程协调提供了支持。 受保护的块是每个Java对象的一部分,可以使用wait
, notify
和notifyAll
方法构造。
wait
方法挂起当前线程。 当线程调用wait时,它必须拥有对象的固有锁,这就是为什么wait调用通常包装在同步方法或语句中的原因。 调用wait方法将挂起线程执行并释放锁。在某个时刻,另一个线程将获取对象的固有锁,并调用
notifyAll
来通知所有线程等待发生重要事件。 在第二个线程释放锁之后,等待的线程将重新获取该锁,并通过从等待调用中返回来恢复执行。
Notify
唤醒单个线程。 无法指定唤醒的具体线程,因此,仅在我们不关心哪个线程被唤醒时才有用。
Java同步器
Java还提供了五个用于通用特殊用途同步的类。
CountDownLatch
CountDownLatch类允许一个或多个线程等待,直到其他线程中的一组操作完成。 用计数编号初始化。
await
方法将阻塞,直到计数达到零为止。
countDown
方法减少计数。
当await方法返回时,将释放所有等待线程,并且随后的await
调用将立即返回。 计数无法重置。
信号
信号量用于限制线程对特定资源的访问。 初始化具有许多许可证。
acquire
方法将一直阻塞,直到获得许可并获得许可为止。
release
方法添加许可,释放阻止获取者。
请注意,调用release不一定必须由称为Acquisition的线程进行。 信号量可以是公平的,也可以是不公平的 。 如果公平,则线程以FIFO方式获取许可。
尽管起初它看上去与CountDownLatch类似,但其目的却完全不同。
循环屏障
CyclicBarrier建立在各方概念的周围。 它允许线程等待彼此到达一个公共的障碍点。
await
方法将阻塞,直到各方到达为止。 它的行为与CountDownLatch的逆过程相同。 N等待之后,它继续。
它支持每个障碍点运行一次的可选可运行对象。 在最后一个聚会到达之后,但在释放之前。 它通常用于更新线程之间的共享状态。 它是循环的,因为它可以在线程释放后重用 。
交换者
Exchanger是两个线程可以交换信息的同步点。
线程将阻塞,直到其对方显示其信息为止。 双方都发生相同的行为。
移相器
Phaser是一个可重用的屏障,类似于CountDownLatch和CyclirBarrier ,但更加灵活。
在Phaser中,创建时注册方的数量不是固定的。 双方可以在通过任何注册时register
或bulkRegister
方法。 双方可以在抵达时注销arriveAndDeregister
。
它提供了几种同步方法。 arriveAndAwaitAdvance
方法的行为与CycleBarrier await
方法的行为相同。 arrive
和arrive
并arriveAndDeregister
记录到达,但不要阻塞。 awaitAdvance
阻塞,直到各方到达为止。
它可以终止 ,强制所有同步方法返回。 可以通过forceTermination
方法强制forceTermination
。
它还提供了监视其状态的支持。 值得注意的是,同步方法只能由注册方调用,而状态可以由任何调用者监视。 监视方法包括getRegisteredParties
和getArrivedParties
等。
结论
多线程绝对不是一个简单的问题,但是使用某些语言提供的工具可以更轻松地解决多线程问题。 就个人而言,我不需要每天使用所有工具,但是我认为有必要知道它们的存在以及如何提供帮助。
翻译自: https://www.javacodegeeks.com/2016/08/the-java-syncrhonisers.html