问题描述
我正在尝试使用两个线程在同一个源上进行操作。我为它设计了一个典型的生产者和消费者问题。与生产者在资源类中设置值的同时,我想与消费者一一获取设置值。我想要的输出应该是这样的:
Producer -> Setting data = 0
Consumer -> Getting data = 0
Producer -> Setting data = 1
Consumer -> Getting data = 1
Producer -> Setting data = 2
Consumer -> Getting data = 2
Producer -> Setting data = 3
Consumer -> Getting data = 3
Producer -> Setting data = 4
Consumer -> Getting data = 4
这是我的资源类:
public class Resource{
private int value;
private boolean current = false;
public synchronized void setValue(int val) {
while(current == true) {
try {
wait();
}catch(Exception ex) {}}
value = val;
current = true;
notifyAll();
}
public synchronized int getValue() {
while(current == false) {
try {
wait();
}catch(Exception ex) {}}
current = false;
notifyAll();
return value;
}
}
还有main方法和Producer,Consumer类:
class Producer extends Thread{
private Resource rs;
public Producer(Resource rs1) {
rs = rs1;
}
public void run() {
for(int i = 0 ; i < 5 ; i++) {
rs.setValue(i);
System.out.println("Producer -> Setting data = " + i);
try {
sleep(100);
}catch(Exception ex){
ex.printstacktrace();
}
}
}
}
class Consumer extends Thread{
private Resource rs;
public Consumer(Resource rs1) {
rs = rs1;
}
public void run() {
int value = 0;
for(int i = 0 ; i < 5; i++) {
value = rs.getValue();
System.out.println("Consumer -> Getting data= " + i);
try {
sleep(100);
}catch(Exception ex) {
ex.printstacktrace();
}
}
}
}
public class Dependent {
public static void main(String[] args) throws IOException {
Resource res = new Resource();
Producer p1 = new Producer(res);
Consumer c1 = new Consumer(res);
p1.start();
c1.start();
}
}
虽然我在资源类中的方法中使用了synchronized、wait和notifyAll关键字,但线程继续工作而不需要彼此等待。我在哪里犯了错误?我在java书中看到过类似这个代码示例的代码示例,好像没有问题。
当我在不添加当前布尔变量的情况下编写时,代码甚至不起作用。这就是为什么我必须通过从书中查看来添加它。不检查当前值,线程不需要同步工作吗?
解决方法
他们确实互相等待,但是线程同步操作比Thread.sleep(100)快很多,,所以你无法分辨。您的测试代码打印 'i' 而不是 'value',这是可疑的。去掉其中一个线程中的 Thread.sleep(100)
(例如,在消费者中),您会发现消费者仍然需要大约半秒才能完成 - 因为每次它都会等待大约 100 毫秒对资源调用 .getValue()
,因为该调用将阻塞(卡在该 wait() 循环中),直到生产者调用 .setValue ,它大约每 100 毫秒执行一次。
您的资源对象“有效”,对于“有效”的某些价值,但设计很差,从核心库(例如 java.util.concurrent.Latch
)重新创建现有的和更好实现的类,并且忽略中断并且会盲目地继续等待。
他们的 API 名称也有点奇怪,因为 get 调用有相当大的副作用。 get
更像是一个 get and clear
操作:在一个 get 操作之后,另一个 get 操作将永远冻结线程,或者至少,直到某个线程设置一个值。
你怎么看?
import java.io.IOException;
class Resource {
private volatile Integer value;
public synchronized void setValue(int val) {
while(value != null && !value.equals(val)) {
try {
wait();
}catch(Exception ex) {}}
value = val;
notifyAll();
}
public synchronized int getValue() {
while(value == null) {
try {
wait();
}catch(Exception ex) {}}
int answer = value;
value = null;
notifyAll();
return answer;
}
}
class Producer extends Thread{
private Resource rs;
public Producer(Resource rs1) {
rs = rs1;
}
public void run() {
for(int i = 0 ; i < 5 ; i++) {
rs.setValue(i);
System.out.println("Producer -> Setting data = " + i);
try {
sleep(100);
}catch(Exception ex){
ex.printStackTrace();
}
}
}
}
class Consumer extends Thread{
private Resource rs;
public Consumer(Resource rs1) {
rs = rs1;
}
public void run() {
for(int i = 0 ; i < 5; i++) {
int value = rs.getValue();
System.out.println("Consumer -> Getting data= " + value);
try {
sleep(100);
}catch(Exception ex) {
ex.printStackTrace();
}
}
}
}
public class Dependent {
public static void main(String[] args) throws IOException {
Resource res = new Resource();
Producer p1 = new Producer(res);
Consumer c1 = new Consumer(res);
p1.start();
c1.start();
}
}
或
class Resource {
private static final int WAIT_VALUE = -1;
private volatile int value = WAIT_VALUE;
public synchronized void setValue(int val) {
while(value > WAIT_VALUE && value != val) {
try {
wait();
}catch(Exception ex) {}}
value = val;
notifyAll();
}
public synchronized int getValue() {
while(value == WAIT_VALUE) {
try {
wait();
}catch(Exception ex) {}}
int answer = value;
value = WAIT_VALUE;
notifyAll();
return answer;
}
}