线程始终在运行,无法停止

时间:2016-10-28 04:33:24

标签: java

我想做一个测试,两个线程,一个线程正在更改值,另一个线程使用一段时间等待第一个线程,然后中断并完成。但问题是等待线程始终在运行,可以&# 39;停止了。另一个问题是,当我打开" System.out.println(i +" run");"的代码时,所有线程都可以正常工作,它很奇怪

import java.util.Date;

public class ThreadTestTwo {
    public int a = 0, b = 0,c = 0;

public static void main(String[] args) {
    System.out.println(new Date()+"start");
    for (int i = 0; i < 100000; i++) {
        new ThreadTestTwo().start(i);
        if(i % 100000 == 0){
            System.out.println(i/100000);
        }
    }
    System.out.println(new Date()+"finish");
}

public void start(final int i){
    Thread readThread = new Thread(){
        @Override
        public void run() {
            while (true) {
                if(c == 1){
                    b = a;
//                      System.out.println(i+", set b "+a);
                    break;
                }
//                  System.out.println(i + " run");
            }
        }
    };
    Thread writeThread = new Thread(){
        @Override
        public void run() {
            a = 1;
            c = 1;
        }
    };
    writeThread.setName("mywrite");
    readThread.setName("myread");
    System.out.println(i+" start");
    writeThread.start();
    readThread.start();

    try {
        writeThread.join();
        readThread.join();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println(i+" end");
    if(b != 1)
        throw new RuntimeException("b = "+b);
}

}

3 个答案:

答案 0 :(得分:1)

除非变量被标记为易失性或者需要使用同步或显式锁定处理事务,否则不保证一个线程的写入被另一个线程看到

在你的情况下,a,b,c是多个线程访问的实例变量,读者线程缓存了这些值,因此它没有看到写入线程的刷新值。

请参阅以下链接了解更多详情: https://docs.oracle.com/javase/tutorial/essential/concurrency/atomic.html

答案 1 :(得分:0)

我建议你阅读更多有关Threads的内容。这是来自O'really的有趣文件:http://chimera.labs.oreilly.com/books/1234000001805/ch09.html

至于您的实现,您应该知道读者线程可能看不到线程对一个变量的修改。要解决此问题,请使用synchronised获取和设置,访问同步块内的变量,或使用AtomicReference。您还可以使用Lock,例如ReantrantLock

另外,如果你有两个线程,其中第一个线程正在等待第二个线程的输入,你可以使用wait()块内的synchronized作为第一个,这样第二个就可以了notify()第一个完成工作的时候。

这样的事情:

import java.util.Date;

public class ThreadTestTwo {

    private int a = 0, b = 0,c = 0;
    private final Object lock = new Object(); 
    //Any object is good as a lock, and for a simple case as this it's fine.
     //This object will work as a monitor for the synchronized blocks.

    public void start(final int i){
        Thread readThread = new Thread(){
            @Override
            public void run() {

                synchronized ( lock ) {
                    try {
                        while( c != 1 ) {
                            lock.wait();
                        }
                    }
                    catch ( InterruptedException ex ) {
                        //Exception handling
                    }

                    b = a;
                }

                //System.out.println(i + " run");

            }
        };

        Thread writeThread = new Thread(){
            @Override
            public void run() {

                synchronized ( lock ) {
                    a = 1;
                    c = 1;
                    lock.notify();
                }

            }
        };

        writeThread.setName("mywrite");
        readThread.setName("myread");
        System.out.println(i+" start");
        writeThread.start();
        readThread.start();

        System.out.println(i+" end");
    }

    public static void main(String[] args) {
        System.out.println(new Date()+"start");
        for (int i = 0; i < 100000; i++) {
            new ThreadTestTwo().start(i);
            if(i % 100000 == 0){
                System.out.println(i/100000);
            }
        }
        System.out.println(new Date()+"finish");
    }
}

我会说这个方法你不需要join()。但是如果想要在第一个线程完成后等待第二个线程启动,则必须在启动它之前使用join()。像这样:

    writeThread.start();
    try {
        writeThread.join();
    }
    catch ( InterruptedException ex ) {
        //Exception handling
    }


    readThread.start();
    try {
        readThread.join();
    }
    catch ( InterruptedException ex ) {
        //Exception handling
    }

但是如果您使用join()用于此特定情况,我会说您不需要任何同步的块或条件,因为第二个线程只会在死后才开始第一个。像这样:

public void start(final int i){
    Thread readThread = new Thread(){
        @Override
        public void run() {
            b = a;
            //System.out.println(i + " run");

        }
    };

    Thread writeThread = new Thread(){
        @Override
        public void run() {
            a = 1;
            c = 1;

        }
    };

    writeThread.setName("mywrite");
    readThread.setName("myread");
    System.out.println(i+" start");


    writeThread.start();
    try {
        writeThread.join();
    }
    catch ( InterruptedException ex ) {
        //Exception handling
    }


    readThread.start();
    try {
        readThread.join();
    }
    catch ( InterruptedException ex ) {
        //Exception handling
    }

    System.out.println(i+" end");
}

我希望我有所帮助。

祝你有个愉快的一天。 :)

答案 2 :(得分:-1)

使用简单的int作为线程之间的信号并不是一个好主意,因为它不是线程安全的。

因此,请尝试使用AtomicInteger,或者让你的int变得不稳定,看看会发生什么。