我的java程序中有两个线程,一个是主线程,另一个线程是主线程中产生的线程A.现在我想要主线程启动线程A并等到线程A在run方法中执行了部分代码,线程A应该挂起自己。然后主线程应该开始运行,运行几行代码,然后线程A应该从它停止的地方开始,反之亦然。这应该发生n次。 我正在尝试如下:
线程A类:
public class ThreadA implements Runnable {
boolean suspended = false;
boolean stopped = false;
synchronized void stop() {
stopped = true;
suspended = false;
notify();
}
synchronized void suspend() {
suspended = true;
}
synchronized void resume() {
suspended = false;
notify();
}
void job() throws InterruptedException {
for (int i = 0; i < 5; i++)
synchronized (this) {
System.out.println("performing job.");
suspend();
while (suspended) {
notify();
suspended = false;
}
}
}
@Override
public void run() {
try {
job();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
MainThread:
public class MainThread {
public static void main(String[] args) throws InterruptedException {
ThreadA a1=new ThreadA();
Thread t1=new Thread(a1);
synchronized (t1) {
t1.start();
for (int i = 0; i < 5; i++) {
t1.wait();
System.out.println("perform some action");
a1.resume();
}
}
}
}
预期产出:
performing job.
perform some action
performing job.
perform some action
performing job.
perform some action
performing job.
perform some action
performing job.
perform some action
实际输出:
performing job.
performing job.
performing job.
performing job.
performing job.
perform some action
我不知道为什么即使我在作业方法中发出了notify()信号,整个for循环也会在线程A中执行。
答案 0 :(得分:2)
你有两个错误。
首先是您正在同步和通知不同的对象。尝试这个修改后的main,我将synchronized(t1)更改为synchronized(a1),将t1.wait()更改为a1.wait()。
public static void main(String[] args) throws InterruptedException {
ThreadA a1=new ThreadA();
Thread t1=new Thread(a1);
synchronized (a1) { // CHANGED FROM t1 to a1
t1.start();
for (int i = 0; i < 5; i++) {
a1.wait(); // CHANGED FROM t1 to a1
System.out.println("perform some action");
a1.resume();
}
}
}
第二个错误是在job()方法中,它调用notify()而不是wait()。这是一个固定版本:
void job() throws InterruptedException {
for (int i = 0; i < 5; i++)
synchronized (this) {
System.out.println("performing job.");
suspend();
while (suspended) {
notify();
suspended = false;
wait(); // ADDED
}
}
}
我的测试运行的输出是
performing job.
perform some action
performing job.
perform some action
performing job.
perform some action
performing job.
perform some action
performing job.
perform some action
答案 1 :(得分:1)
这是更简化的方式
public class TwoThread {
public static void main(String[] args) throws InterruptedException {
ThreadA a1 = new ThreadA();
Thread t1 = new Thread(a1);
synchronized (a1) {
t1.start();
for (int i = 0; i < 5; i++) {
a1.wait();
System.out.println("perform some action " + i);
a1.notify();
}
}
}
}
public class ThreadA implements Runnable {
boolean suspended = false;
boolean stopped = false;
void job() throws InterruptedException {
for (int i = 0; i < 5; i++)
synchronized (this) {
System.out.println("performing job. " + i);
notify();
wait();
}
}
public void run() {
try {
job();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
答案 2 :(得分:0)
如果一个线程等待而另一个线程正在等待,那么没有理由同步多个线程。可以使用Executors以较少的工作获得相同的结果,并且仍然感觉有人正在玩线程。
public class Main {
public static void main(String[] args) {
ExecutorService executor = Executors.newSingleThreadExecutor();
for(int i = 0; i<5;i++) {
executor.submit(new PrintTask("performing job."));
executor.submit(new PrintTask("perform some action"));
}
executor.shutdown();
}
private static class PrintTask implements Runnable {
private String string;
public PrintTask(String string) {
this.string = string;
}
@Override
public void run() {
System.out.println(string);
}
}
}
答案 3 :(得分:0)
仅在两个线程之间进行通信:
您只需要 VarHandle 屏障(在 Java 中)。 Java VarHandle 唯一的要求是有序的内存访问
这是一篇相当不错的文章。 Synchronizing without Locks and Concurrent Data Structures
在我的日常工作中,我使用了 Dekker 和 Peterson 算法的变体 用于 Web 请求的异步多线程处理、共享连接池和从云应用程序收集日志,对性能影响较小 与单线程无竞争性能相比。
有时,我必须使用 setOpaque 和 getOpaque, 使用 VarHandle.loadLoadFence() 和 VarHandle.storeStoreFence() 确保有序的内存访问,这就是您所需要的。 在我看来,弱 CAS 是我会走的更远, 与其他任何我认为违反多核 CPU 架构的行为一样。
但是,除非您对实际情况有深入的了解 您正在使用的硬件和内存排序结构 在微指令层面使用,建议你使用 标准 Java 并发锁,因为它们是最好的 并且最适合通用解决方案。
为了实现 10 倍于传统 CAS 算法的性能提升, 您需要对内存中的所有共享对象进行非常稳定的布局 并严格定义哪些线程可以读取 并且可以写入每个变量以及按什么顺序。 您需要考虑对 CPU 缓存的副作用 所有内存加载和存储,然后得到这些 在特定平台上发挥您的优势 你的目标。你最终会得到 相当复杂的算法,但性能无与伦比。
您应该探索 LMAX Disruptor 库 因为它拥有实现许多这些概念的开源库 像环形缓冲区和单线程可以写入每个变量。 LMAX Disruptor User Guide 然而,我仍然认为这是并发的保守方法。 我目前的标准是拥有可以容忍赛车的算法 如果检测到赛车状况,则丢弃数据并重复处理。 我使用状态嵌入计数器、索引、标志和散列, 检测线程冲突并选择将放弃的线程 并使用另一个内存结构进行操作。 然而,由于线程集中的内存结构和 优化的引用共享很少发生(比如百万分之一)
但是,如果您对 CPU 缓存操作有很好的了解 以及您可以获得 CPU 的任何专门平台指令 缓存以发挥您的优势并执行读取和写入, 并在核心之间共享缓存线,作为副作用 您的指令,而无需您明确发出 执行此操作的命令。
顺便说一句,NodeJS (V8) 引擎试图通过使用单线程事件循环将事件分发到所有其他 I/O 和实用程序库线程来最小化争用和锁定,因此它们不必相互竞争访问事件和共享内存。 如您所见,NodeJS 取得了巨大的成功, 如果我们谈论的是特殊用途的算法 你可以更进一步。 NodeJS architecture
祝您阅读愉快。