我想做一个测试,两个线程,一个线程正在更改值,另一个线程使用一段时间等待第一个线程,然后中断并完成。但问题是等待线程始终在运行,可以&# 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);
}
}
答案 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变得不稳定,看看会发生什么。