多线程等待/通知(两个要点)
2019-03-31 本文已影响0人
迷糊小生
public class Add {
private String lock;
public Add(String lock) {
this.lock = lock;
}
public void add() {
synchronized (lock) {
ValueObject.list.add("anyString");
lock.notifyAll();
}
}
}
package other.thread11;
public class Subtract {
private String lock;
public Subtract(String lock) {
this.lock = lock;
}
public void subtract() {
try {
synchronized (lock) {
if(ValueObject.list.size() == 0) {
System.out.println("wait begin ThreadName=" + Thread.currentThread().getName());
lock.wait();
System.out.println("wait end ThreadName=" + Thread.currentThread().getName());
}
ValueObject.list.remove(0);
System.out.println(Thread.currentThread().getName() + " list size:" + ValueObject.list.size());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package other.thread11;
public class ThreadA extends Thread{
private Add p;
public ThreadA(Add p) {
this.p = p;
}
@Override
public void run() {
p.add();
}
}
package other.thread11;
public class ThreadB extends Thread{
private Subtract s;
public ThreadB(Subtract s) {
this.s = s;
}
@Override
public void run() {
s.subtract();
}
}
public class ValueObject {
public static List list = new ArrayList<>();
}
package other.thread11;
public class Test {
public static void main(String[] args) throws InterruptedException {
String lock = new String("");
Add add = new Add(lock);
Subtract sub = new Subtract(lock);
ThreadB threadB = new ThreadB(sub);
threadB.setName("B");
threadB.start();
Subtract sub2 = new Subtract(lock);
ThreadB threadB2 = new ThreadB(sub2);
threadB2.setName("B2");
threadB2.start();
Thread.sleep(1000);
ThreadA threadA = new ThreadA(add);
threadA.setName("A");
threadA.start();
}
}

错误原因:当第一个线程进入到subtract()方法后由于ValueObject.list大小为0而进入了等待状态,并且释放了锁,第二个线程同样因此而进入了等到状态,直至add()方法执行完毕唤醒了之前两个线程,两个线程之一获得了锁对象执行完了subtract()方法,而另外一个线程被唤醒再次得到锁对象时继续运行下面代码,直至移除的时候抛出数组越界异常。

解决方案:将if改为while,这样的话第二个等待线程被唤醒后将再次校验ValueObject.list大小,如果为0的话将再次进入等待状态
package other.thread12;
//消费者
public class Customer {
private String lock;
public Customer(String lock) {
this.lock = lock;
}
public void getValue() {
try {
synchronized (lock) {
if(ValueObject.value.equals("")) {
System.out.println("Customer...." + Thread.currentThread().getName()
+ "waiting....");
lock.wait();
}
System.out.println("get:" + ValueObject.value);
ValueObject.value = "";
lock.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package other.thread12;
//生产者
public class Product {
private String lock;
public Product(String lock) {
this.lock = lock;
}
public void setValue() {
try {
synchronized (lock) {
if(!ValueObject.value.equals("")) {
System.out.println("PRODUCT...." + Thread.currentThread().getName()
+ "waiting....");
lock.wait();
}
String value = System.currentTimeMillis() + "_" + System.nanoTime();
System.out.println("set:" + value);
ValueObject.value = value;
lock.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package other.thread12;
public class ThreadA extends Thread{
private Product product;
public ThreadA(Product product) {
this.product = product;
}
@Override
public void run() {
while (true) {
product.setValue();
}
}
}
package other.thread12;
public class ThreadB extends Thread {
private Customer customer;
public ThreadB(Customer customer) {
this.customer = customer;
}
@Override
public void run() {
while (true) {
customer.getValue();
}
}
}
package other.thread12;
public class ValueObject {
public static String value = "";
}
public class Test {
public static void main(String[] args) {
String lock = new String("");
Product product = new Product(lock);
ThreadA[] threadA = new ThreadA[2];
Customer customer = new Customer(lock);
ThreadB[] threadB = new ThreadB[2];
for (int i = 0; i < 2; i++) {
threadA[i] = new ThreadA(product);
threadA[i].setName("PRODUCT " + (i + 1));
threadB[i] = new ThreadB(customer);
threadB[i].setName("Customer " + (i + 1));
threadA[i].start();
threadB[i].start();
}
}
}

在多个生成者和多个消费者的时候由于notify()方法是随机唤醒一个的缘故,因此会造成大亮线程处于等待状态。
解决方案:notify()替换成notifyAll();