假设有这样的代码。它的结果是:[Synchronization] [in Java] [useful]。如果对象Caller在没有单独线程的情况下运行(即没有extends Thread和没有方法start()),那么结果将采用不同的顺序:[同步] [有用] [在 Java 中]。为什么会这样?请给出详细的答复。
class CallMe {
void call(String msg) {
System.out.print("[" + msg );
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("]");
}
}
class Caller extends Thread {
String msg;
CallMe target;
Caller(CallMe target, String msg) {
this.target = target;
this.msg = msg;
start();
}
public void run() {
synchronized (target) {
target.call(msg);
}
}
}
public class Main {
public static void main(String[] args) {
CallMe callMe = new CallMe();
new Caller(callMe, "Синхронизация");
new Caller(callMe, "в Java");
new Caller(callMe, " полезная");
}
}
在方法中设置 200 毫秒的延迟
run(),并在方法中main()添加 100 毫秒的循环 - 您可以获得更多选择。因为无法预测哪个线程将
synchronized首先进入块,即使您按顺序运行它们也是如此。类用于同步线程
他们有不同的目标和方法。这取决于具体的任务。
信号量- 通常用于限制使用资源时的线程数。使用计数器限制访问,如果其值大于零,则允许访问线程,并且计数器值递减。如果计数器为零,则当前线程被阻塞,直到另一个线程释放资源。为了获得访问权限,该方法用于
acquire()释放 -release()。工作成果:
同时,只有两个线程可以捕获信号量(使用
acquire())方法,其余线程排队,直到其中一个线程使用release().CountDownLatch - 允许线程等待,直到在其他线程中执行的一定数量的操作完成后,线程使用
await(). 所需操作的数量在创建对象时设置,之后在调用方法时减少countDown()。一旦计数器达到 0,等待线程就被解除阻塞。工作成果:
主线程创建 6 个线程并等待每个线程完成准备工作。
CyclicBarrier - 用于在某一点同步给定数量的线程。当方法被调用时,
await()线程被阻塞。一旦指定数量的线程被阻塞,锁就会同时从它们中释放。工作成果:
尽管有些线程较早完成准备,有些较晚,但它们同时启动,因为锁是同时释放的。
锁定- 界面。它是一种高级线程同步机制,提供了比同步块更大的灵活性。由于
Lock这是一个接口,要使用它,您必须创建其实现之一的对象。首先,创建一个类型的对象
Lock,然后对该对象调用一个方法lock()并将其捕获。另一个线程尝试调用同一对象上的方法lock()将导致该线程阻塞,直到持有该类型对象的线程Lock使用unlock(). 方法调用后,unlock()类型对象被释放,其他线程可以抓取,和同步块Lock的主要区别是:Lock同步块不保证保持线程访问临界区的顺序;
没有办法通过超时退出同步块;
同步块必须完全包含在一种方法中,而它们
Lock可以在一种方法中捕获并在另一种方法中释放。