synchronized与条件同步

在并发编程中,有这样的需求:当满足某个条件时线程执行同步块中的代码,条件不满足时,让线程在此等待,直至条件满足再执行同步代码块。

java的Object类即提供了一类这样的方法wait(),notifyAll()/notify(),调用wait()方法后,线程A释放对同步代码块的控制并进入休眠状态,

在条件再次满足时,调用notifyAll()/notify()方法唤醒线程A,线程A将被唤醒并重新试图获得同步代码块的控制,在进入同步代码块成功之后,

再次对条件判断。

典型的应用场景是生产者-消费者模式,有一个固定大小的缓冲区存放消息,一个或者多个生产者线程把消息写入缓冲区;一个或者多个消费者从缓冲区获取消息。

如果缓冲区满了,生产者就不能写入消息,并等待。如果缓冲区为空,消费者就不能获取消息,并等待。

我们使用synchronized关键字来同步代码块,由于java中的类都继承自Object类,因此可以在我们的类中调用wait()让线程进入休眠并等待唤醒。

首先创建一个类MessageStorage来管理消息缓冲区,并使用LinkedList队列来作为消息缓冲区:

public class MessageStorage {
    private int maxSize;
    private List<String> messages;

    public MessageStorage(int maxSize) {
        this.maxSize = maxSize;
        messages = new LinkedList<String>();
    }
    public void set(String message){
        synchronized (this){
            while(messages.size() == maxSize){
                try {
                    System.out.print("the message buffer is full now,startinto wait()
");
                    wait();//满足条件时,线程休眠并释放锁。当调用notifyAll()时。线程唤醒并重新获得锁
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
            try{
             Thread.sleep(100);
            }catch (InterruptedExceptione){
                e.printStackTrace();
            }
            messages.add(message);
            System.out.print("add message:"+message+" success
");
            notifyAll();//唤醒休眠的线程
        }
    }
    public String get(){
        String message = null;
        synchronized (this){
            while(messages.size() == 0){
                try {
                    System.out.print("the message buffer is empty now,startinto wait()
");
                    wait();
                }catch (InterruptedExceptione){
                    e.printStackTrace();
                }
            }
            try{
                Thread.sleep(100);
            }catch (InterruptedExceptione){
                e.printStackTrace();
            }
            message =((LinkedList<String>)messages).poll();
            System.out.print("get message:"+message+" success
");
            notifyAll();
        }
        return message;
    }
}


实现一个生产者,向消息缓冲区写入消息:

public class Producer implements Runnable{
    private MessageStorage messageStorage;
    private int index;
    public Producer(MessageStorage messageStorage,int index) {
        this.messageStorage = messageStorage;
        this.index = index;
    }

    public void run(){
        for(int i=0; i<5; i++){
            StringBuffer message = new StringBuffer("thread id:");
            message.append(index);
            message.append(" id:");
            message.append(i);
            messageStorage.set(message.toString());
        }
    }
}


实现一个消费者,从消息缓冲区取数据:

public class Consumer implements Runnable{
    private MessageStorage messageStorage;
    public Consumer(MessageStorage messageStorage) {
        this.messageStorage = messageStorage;
    }

    public void run(){
        for(int i=0; i<5; i++){
            messageStorage.get();
        }
    }
}


测试代码:

public class ThreadMain {
    public static void main(String[] args){
        MessageStorage messageStorage = new MessageStorage(10);
        Thread[] threads = new Thread[10];
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(new Producer(messageStorage,i));//创建多个生产者
            threads[i] = thread;
        }
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(new Consumer(messageStorage));//创建多个消费者
            threads[i+5] = thread;
        }
        for(int i = 0; i < 10; i++){
            Thread thread = threads[i];
            try {
                thread.start();//启动线程
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}

总结:

使用wait()和notifyAll()/notify()方法,便于我们在程序中主动的控制的线程的休眠和唤醒 ,实现更为复杂的逻辑控制要求。另外,我们也可以使用Lock锁来进行代码控制,使用锁的条件Condition的await()和signal()/和signalAll()控制线程的休眠、唤醒,来实现上面的生产者-消费者模型。



原文地址:https://www.cnblogs.com/cl1024cl/p/6205014.html