如何在以下代码中实现线程安全性

时间:2018-04-22 10:43:26

标签: java multithreading thread-safety

我正在阅读有关线程安全性和synchronized关键字的信息,但我很难弄清楚它是如何正确实现的。

我的情况是Thread A 将数据保存到BufferThread B 读取数据并保存到数据库。

如何使用以下代码实现线程安全? 通过线程安全,我的意思是Thread B不会开始直到Thread完成它的工作,同样适用于Thread A。

public class Main {
    public static void main(String args[]) throws InterruptedException {

        LinkedBlockingQueue<Document> queue = new LinkedBlockingQueue<>();
        ProducerThread mProducer = new ProducerThread(queue);
        ConsumerThread mConsumer = new ConsumerThread(queue);
        new Thread(mProducer).start();
        new Thread(mConsumer).start();
    }
}

-

public class ProducerThread implements Runnable {
    private LinkedBlockingQueue<Document> queue;

    public ProducerThread(LinkedBlockingQueue<Document> queue) {
        this.queue = queue;
    }
    @Override
    public void run() {
        while(true) {
            Timestamp timestamp = new Timestamp(System.currentTimeMillis());
            Document document = new Document("timeAdded", timestamp);
            try {
                queue.put(document);
                System.out.println("Document added " + document.toString());
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

-

public class ConsumerThread implements Runnable {
    private LinkedBlockingQueue<Document> queue;
    private Database mDatabase;

    public ConsumerThread(LinkedBlockingQueue<Document> queue) {
        this.queue = queue;
    }


    @Override
    public void run() {
            try {
                Document doc;
                mDatabase = Database.getInstance();
                if (Client.isAlive()) {
                    while (queue.take() != null) {
                        mDatabase.insert(queue.take());
//                        Thread.sleep(10);
                        System.out.println("Document consumed " + queue.take().toString());
                        if (!Client.isAlive()) {
                            wait();
                            Client.reconnect();
                        }
                    }
                } else {
                    wait();
                }
            } catch(InterruptedException | IllegalMonitorStateException e){
                e.printStackTrace();
            }
    }
}

我得到以下输出

> Document added{{timeAdded=2018-04-22 13:20:27.88}} 
> Document{{timeAdded=2018-04-22 13:20:27.88}} Document added
> Document{{timeAdded=2018-04-22 13:20:28.881}} Document added
> Document{{timeAdded=2018-04-22 13:20:29.882}} Document added
> Document{{timeAdded=2018-04-22 13:20:30.882}} Consumed
> Document{{timeAdded=2018-04-22 13:20:30.882}} Document added
> Document{{timeAdded=2018-04-22 13:20:31.883}} Document added
> Document{{timeAdded=2018-04-22 13:20:32.883}} Consumed
> Document{{timeAdded=2018-04-22 13:20:33.884}} Document added
> Document{{timeAdded=2018-04-22 13:20:33.884}} Document added
> Document{{timeAdded=2018-04-22 13:20:34.885}} Document added
> Document{{timeAdded=2018-04-22 13:20:35.885}} Document added
> Document{{timeAdded=2018-04-22 13:20:36.886}} Consumed
> Document{{timeAdded=2018-04-22 13:20:36.886}} Document added

1 个答案:

答案 0 :(得分:1)

我不知道这是否有帮助,但我会写一个名为 ProductionProcess 的第三类或类似的东西......

简短的附加说明: ProductionClass 包含存储单个 Document 对象的队列。 Producer 线程开始生成&#34;生成&#34;对象只要队列(当前大小为10)已满。每当此队列中的一个插槽空闲时,因为 Consumer 线程删除了一个 Document 对象。 Producer 线程接收信号并开始生成&#34;再次记录对象,直到队列已满。完整的源代码应该是线程安全的。

public class ProductionProcess
{
private static final int CAPACITY;
private final Queue QUEUE;

private final Lock LOCK;
private final Condition BUFFER_FULL;
private final Condition BUFFER_EMPTY;

static
{
    CAPACITY = 10;
}

ProductionProcess()
{
    this.QUEUE = new LinkedList <Document> ();
    this.LOCK = new ReentrantLock();

    this.BUFFER_FULL = this.LOCK.newCondition();
    this.BUFFER_EMPTY = this.LOCK.newCondition();
}

public void produce() throws InterruptedException
{
    this.LOCK.lock();

    try
    {
        while(ProductionProcess.CAPACITY == this.QUEUE.size())
        {
            System.out.println(Thread.currentThread().getName() + " : Buffer is full, waiting!");
            this.BUFFER_FULL.await();
        }

        Timestamp timestamp = new Timestamp(System.currentTimeMillis());
        Document document = new Document("timeAdded", timestamp);

        if(true == this.QUEUE.offer(document))
        {
            System.out.printf("Added to queue: " + document);
            this.BUFFER_EMPTY.signalAll();
        }
    }
    finally
    {
        this.LOCK.unlock();
    }
}

public void receive() throws InterruptedException
{
    this.LOCK.lock();

    try
    {
        Database mDatabase = Database.getInstance();

        while(0 == this.QUEUE.size())
        {
            System.out.println(Thread.currentThread().getName() + " : Buffer is empty, waiting!");
            this.BUFFER_EMPTY.await();
        }

        Document mDocument = (Document) this.QUEUE.poll());

        if(null != mDocument)
        {
            mDatabase.insert(mDocument);
            System.out.printf("Consumed from queue: " + document);
            System.out.println(Thread.currentThread().getName() + " : Signalling that buffer may be empty now");
            this.BUFFER_FULL.signalAll();
        }
    }
    finally
    {
        this.LOCK.unlock();
    }
}
}

之后使用它就像这样...

ProductionProcess process = new ProductionProcess();

Runnable runnableProducer = new Runnable() {
    @Override public void run()
    {
        while(true)
        {
        try
        {
            process.produce();
            Thread.sleep(1000);
        }
        catch(InterruptedException exc)
        {
            exc.printStackTrace();
        }
        }
    }
}

Runnable runnableConsumer = new Runnable() {
    @Override public void run()
    {
        while(true)
        {
        try
        {
            process.receive();
            Thread.sleep(1000);
        }
        catch(InterruptedException exc)
        {
            exc.printStackTrace();
        }
        }
    }
}

new Thread(runnableProducer).start();
new Thread(runnableConsumer).start();

我没有对它进行广泛的测试,但它应该可行。如果它不起作用只是评论它... 此外,如果你能说德语,请注意这不是我的代码link