我在Java学习multithreading
。问题陈述是:假设有一个数据结构可以包含数百万个整数,现在我想在这里搜索一个密钥。我想使用2个线程,这样如果任何一个线程找到了密钥,它应该将共享的布尔变量设置为false,并且线程都应该停止进一步处理。
以下是我的尝试:
public class Test implements Runnable{
private List<Integer> list;
private Boolean value;
private int key = 27;
public Test(List<Integer> list,boolean value) {
this.list=list;
this.value=value;
}
@Override
public void run() {
synchronized (value) {
if(value){
Thread.currentThread().interrupt();
}
for(int i=0;i<list.size();i++){
if(list.get(i)==key){
System.out.println("Found by: "+Thread.currentThread().getName());
value = true;
Thread.currentThread().interrupt();
}
System.out.println(Thread.currentThread().getName() +": "+ list.get(i));
}
}
}
}
主要课程是:
public class MainClass {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>(101);
for(int i=0;i<=100;i++){
list.add(i);
}
Boolean value=false;
Thread t1 = new Thread(new Test(list.subList(0, 49),value));
t1.setName("Thread 1");
Thread t2 = new Thread(new Test(list.subList(50, 99),value));
t2.setName("Thread 2");
t1.start();
t2.start();
}
}
我的期望:
两个线程都将随机运行,当任何线程遇到27时,两个线程都将被中断。因此,线程1不应该能够处理所有输入,类似于线程2.
但是,发生了什么:
两个线程都在完成循环,线程2始终在线程1完成后启动。
Please highlight the mistakes
,我还在学习线程。
我的下一个练习题是:逐个访问任何共享资源
答案 0 :(得分:3)
您将整个代码块包装在对象synchronized
下的value
块下。这意味着,一旦执行到达同步块,第一个线程将监视器保持到对象value
并且任何后续线程将阻塞,直到监视器释放 强>
注意整个块:
synchronized (value){
if(value){
Thread.currentThread().interrupt();
}
for(int i=0; i < list.size(); i++){
if(list.get(i) == key){
System.out.println("Found by: "+Thread.currentThread().getName());
value = true;
Thread.currentThread().interrupt();
}
System.out.println(Thread.currentThread().getName() +": "+ list.get(i));
}
}
包含在一个synchronized块中,这意味着只有一个线程可以同时运行该块,与您的目标相反。
在这种情况下,我相信你误解了同步和“共享变量”背后的原理。澄清:
static
- 是用于在对象(即类变量)之间创建变量全局变量的变量修饰符,以便每个对象共享相同的静态变量。
volatile
- 是用于使变量线程安全的变量修饰符。请注意,您仍然可以从不同的线程中访问没有此修饰符的变量(但这很危险并且可能导致竞争条件)。线程对变量的范围没有影响(除非你使用ThreadLocal)。
我想补充一点,你不能把volatile
放在任何地方,并希望代码是线程安全的。我建议您阅读Oracle's guide on synchronization,以便更深入地了解如何建立线程安全性。
在您的情况下,我将删除同步块并将共享布尔值声明为:
private static volatile Boolean value;
此外,您正在尝试执行的任务是为其构建Fork / Join池的任务。我建议{@ 3}}部分Oracle的java教程,看看如何在分而治之的方法中使用Fork / Join池。
答案 1 :(得分:2)
通过将线程的主逻辑包装在同步块中,该块中代码的执行变得互斥。线程1将进入块,获取“值”锁定并运行整个循环,然后返回锁定并允许线程2运行。
如果你只包装标志“value”的检查和设置,那么两个线程应该同时运行代码。
编辑:正如其他人已经讨论过在Test类中使“{value”成为static volatile boolean
而根本不使用synchronized块也可以。这是因为对volatile变量的访问就像它在同步块中一样。
参考:https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html
答案 2 :(得分:1)
你不应该在static
标志上获得锁定 - 这只会确保只有一个线程可以运行。而是将标记volatile
设置为共享,private List<Integer> list;
private int key = 27;
private static volatile boolean found;
public Test(List<Integer> list, boolean value) {
this.list = list;
this.found = value;
}
@Override
public void run() {
for (int i = 0; i < list.size(); i++) {
// Has the other thread found it?
if (found) {
Thread.currentThread().interrupt();
}
if (list.get(i) == key) {
System.out.println("Found by: " + Thread.currentThread().getName());
// I found it!
found = true;
Thread.currentThread().interrupt();
}
System.out.println(Thread.currentThread().getName() + ": " + list.get(i));
}
}
使其无法缓存。
此外,您应该更频繁地检查标志。
0
顺便说一下:你的两个线程都从Facebook IOS SDK
开始,然后走向数组 - 我假设你在这段代码中这样做作为演示,你可以让它们从相反的两端工作,也可以随机走。
答案 3 :(得分:0)
将布尔值设为静态,以便两个线程都可以访问和编辑同一个变量。然后你不需要传入它。然后只要一个线程将其更改为true,第二个线程也将停止,因为它使用相同的值。