了解Java中的多线程

时间:2015-04-27 07:52:53

标签: java multithreading concurrency

我在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,我还在学习线程。

我的下一个练习题是:逐个访问任何共享资源

4 个答案:

答案 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,第二个线程也将停止,因为它使用相同的值。