生产者 - 消费者多线程同步问题

时间:2014-06-18 09:50:37

标签: java multithreading arraylist synchronization

Hello All(第一篇文章)!

首先我要说我在Java编程方面还是比较新的(C ++是我的强项)

如果已经回答了这个问题,请指出正确的方向,但我的搜索技巧还没有达到我想要的效果。我遇到的问题是我的消费者线程之间的同步,而不是我的生产者/消费者

之间的同步

我的任务包括从生产者线程中查找素数候选者,而不是使用多个消费者线程来检查每个候选人的有效性:

  • 使用多个线程,1个生产者线程将“候选人”放入一个名为 prime_candidates 的arraylist(必须是arraylist),消费者除了从arraylist中获取并检查

  • 消费者检查,如果它是素数,他们将素数放入他们自己的arraylist prime_list

  • 60秒后, prime_list 被写入文件并继续

我已成功将 prime_candidates 与使用者(检查)线程同步。但我不能为我的生活弄清楚如何同步消费者线程产生的ArrayLists。它们都是自己创建的,并且使用我从Producer / Consumer中使用的同步方法不起作用。

生产线

import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Date;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;


public class GenerateThread implements Runnable{

// MAIN ARRAY LIST THAT STORES THE NUMBERS WHICH COULD BE CONSIDERED PRIME CANDIDATES
private final ArrayList<PrimeCandidate> prime_candidates = new ArrayList();

private boolean alive = true;   // MAIN VARIABLE THAT WILL CONTINUE TO RUN THE THREAD 
private boolean toggle = true;  // VARIABLE THAT PAUSES THE PRODUCER

// START: FOR PANEL LAYOUT //
JPanel genPanel = new JPanel();

JPanel lastPane = new JPanel();
JLabel lastOutput = new JLabel();

JPanel queuePane = new JPanel();
JLabel queueOutput = new JLabel();

JLabel headLabel = new JLabel("Generate Thread");
JLabel lastLabel = new JLabel("Last");
JLabel queueLabel = new JLabel("Queue");

JButton pauseButton = new JButton("Pause");
JButton terminateButton = new JButton("Terminate");
// END: FOR PANEL LAYOUT //

// DEFAULT CONSTRUCTOR, DOESN'T REALLY DO MUCH
GenerateThread(){ System.out.println("Creating Generator");}

public JPanel createPanel()
{
    // TO SET THE BLACK BORDER AROUND THE PANELS HOLDING CURRENT SIZE AND 
    // PRIME CANDIDATE
    lastPane.setBorder(BorderFactory.createLineBorder(Color.black));
    queuePane.setBorder(BorderFactory.createLineBorder(Color.black));

    // ADD LISTENER TO TERMINATE THREAD
    terminateButton.addActionListener(new terminateListener());

    // ADD LISTENER TO PAUSE THREAD
    pauseButton.addActionListener(new pauseListener());

    lastPane.add(lastOutput);
    queuePane.add(queueOutput);

    genPanel.add(headLabel);
    genPanel.add(lastLabel);
    genPanel.add(lastPane);
    genPanel.add(queueLabel);
    genPanel.add(queuePane);
    genPanel.add(pauseButton);
    genPanel.add(terminateButton);

    return genPanel;
}


@Override
public void run() 
{
    long i = 3;
    while(alive)
    {
        if(toggle)
        {
            if(i % 2 == 1)
                if(!TestForPrime.isDividableBy3(BigInteger.valueOf(i)))
                {
                    addToArrayList(BigInteger.valueOf(i));
                    lastOutput.setText(BigInteger.valueOf(i).toString());
                    queueOutput.setText(BigInteger.valueOf(prime_candidates.size()).toString());
                }
            i++;
        }
        if(!toggle)continueNotifying();
    }
    while(!alive){continueNotifying();}
}

// ADDS NEW ITEM TO ARRAYLIST AND NOTIFYS ALL THREADS DEPENDENT ON ARRAYLIST
public synchronized void addToArrayList(BigInteger b)
{
        prime_candidates.add(new PrimeCandidate(b, new Date()));
        notify();
}

// CONTINUES NOTIFYING OTHER THREADS WHEN PAUSED OR TERMINATED SINCE THEY WILL
// NOT RUN UNLESS THEY HAVE BEEN GIVEN THE 'GO AHEAD' (NOTIFY())
public synchronized void continueNotifying()
{
    queueOutput.setText(BigInteger.valueOf(prime_candidates.size()).toString());
    notify();
}

// USED BY THE OTHER THREADS TO GET ITEMS FROM THE ARRAY LIST, THREADS DELETE
// THE VARIABLE THAT THEY GRAB, ESSENTIALLY CUTTING THE ARRAY LIST DOWN EVERY
// TIME
public synchronized PrimeCandidate getFromArrayList() throws InterruptedException 
{
      wait(); //Keeps thread from adding more to list while others are grabbing it

      PrimeCandidate returnCandidate = new PrimeCandidate(prime_candidates.get(0));

      prime_candidates.remove(0);

      return returnCandidate;
}

// LISTENER THAT WILL KILL THE THREAD PRIME CANDIDATE PRODUCER
// ALSO GREYS OUT BOTH BUTTONS TO SHOW THREAD IS DEAD
class terminateListener implements ActionListener
{
    @Override
    public void actionPerformed(ActionEvent e)
    { 
        terminateButton.setEnabled(false);
        pauseButton.setEnabled(false);
        alive = false;
    }
}

// LISTENER THAT WILL TEMPORARILY STOP THE PRODUCER
class pauseListener implements ActionListener
{
    @Override
    public void actionPerformed(ActionEvent e) 
    { 
        if(toggle)
            pauseButton.setText("Start");
        if(!toggle)
            pauseButton.setText("Pause");
        toggle = !toggle;
    }
}
}

消费者线程

import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedWriter;  
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Date;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;

public class CheckThread  implements Runnable{

private final GenerateThread generated;
private ArrayList<Primes> prime_list = new ArrayList();
//private CopyOnWriteArrayList<Primes> prime_list = new CopyOnWriteArrayList();
private final String threadName;
private boolean alive = true;
private boolean toggle = true;
private Date timeFetched;
private Date timeFound;
private Primes currentPrime;

// START: FOR PANEL LAYOUT //
JPanel genPanel = new JPanel();

JPanel lastPane = new JPanel();
JLabel lastOutput = new JLabel();

JPanel queuePane = new JPanel();
JLabel queueOutput = new JLabel();

JLabel headLabel = new JLabel("Generate Thread");
JLabel lastLabel = new JLabel("Last");
JLabel queueLabel = new JLabel("In Work");

JButton pauseButton = new JButton("Pause");
JButton terminateButton = new JButton("Terminate");
// END: FOR PANEL LAYOUT //

CheckThread(GenerateThread gen, String name, int priority)
{
    generated = gen;
    threadName = name;
    headLabel.setText(name);
    System.out.println("Creating | " + name);

    if(priority == 0)
    {
       System.out.println(name + " Is writing thread");
        final Timer t = new Timer(10000, new writeToFileTimer());
        t.start();
    }
}

public JPanel createPanel()
{
   lastPane.setBorder(BorderFactory.createLineBorder(Color.black));
   lastPane.setMinimumSize(new Dimension(200,100));
   queuePane.setBorder(BorderFactory.createLineBorder(Color.black));


   terminateButton.addActionListener(new terminateListener());

   pauseButton.addActionListener(new pauseListener());

   lastPane.add(lastOutput);
   queuePane.add(queueOutput);

   genPanel.add(headLabel);
   genPanel.add(lastLabel);
   genPanel.add(lastPane);
   genPanel.add(queueLabel);
   genPanel.add(queuePane);
   genPanel.add(pauseButton);
   genPanel.add(terminateButton);

   lastOutput.setIgnoreRepaint(true);
   return genPanel;
}
 class writeToFileTimer implements ActionListener
 {
    @Override
    public void actionPerformed(ActionEvent event)
    {
       try{ writeToFile();}catch (InterruptedException e) { System.out.println("WTF");}
    }
 }

@Override
public void run() 
{
    while(alive)
    {
        try 
        {
            if(toggle)
            {
                PrimeCandidate recieved = new PrimeCandidate(generated.getFromArrayList());
                timeFetched = new Date();
                //queueOutput.setText(recieved.getCandidate().toString());
                if(TestForPrime.isPrime(recieved.getCandidate()))
                {
                    lastOutput.setText(recieved.getCandidate().toString());
                    timeFound = new Date();
                    String temp = "" + prime_list.size();
                    queueOutput.setText(temp);
                    addToArrayList(new Primes(recieved.getCandidate(),recieved.getTimeStamp(), timeFetched));
                }
            }
        } catch (InterruptedException e) {}
    }
}

class terminateListener implements ActionListener
{
    @Override
    public void actionPerformed(ActionEvent e) 
    { 
        terminateButton.setEnabled(false);
        pauseButton.setEnabled(false);
        alive = false;
    }
}

class pauseListener implements ActionListener
{
    @Override
    public void actionPerformed(ActionEvent e) 
    { 
        if(toggle)
            pauseButton.setText("Start");
        if(!toggle)
            pauseButton.setText("Pause");
        toggle = !toggle;
    }
}

// ADDS NEW ITEM TO ARRAYLIST AND NOTIFYS ALL THREADS DEPENDENT ON ARRAYLIST 
public synchronized void addToArrayList(Primes primeFound)
{
        prime_list.add(primeFound);
}

// USED BY THE OTHER THREADS TO GET ITEMS FROM THE ARRAY LIST, THREADS DELETE
// THE VARIABLE THAT THEY GRAB, ESSENTIALLY CUTTING THE ARRAY LIST DOWN EVERY
// TIME
public synchronized void writeToFile() throws InterruptedException
{
    wait(); 
    try 
    {
        File statText = new File("prime_numbers.txt");

        FileOutputStream is = new FileOutputStream(statText);

        OutputStreamWriter osw = new OutputStreamWriter(is);    

        Writer writ = new BufferedWriter(osw);
        while(!prime_list.isEmpty())
        {
            writ.append(prime_list.get(0).toString());
            prime_list.remove(0);
        }
        writ.close();
        headLabel.setText(threadName);

    } catch (IOException e) {System.err.println(" failed to write to Test.txt");}
    notify();
}   
}

我知道代码编写错误但是当我执行此任务时,我不断碰壁并从头开始重写我的代码并最终停止了关怀。有谁知道如何在消费者线程中轻松同步我的所有prime_list Arraylist。如果您没有注意到我构建了每个数组列表,那么我可以简单地将每个线程/面板添加到主框架中。它在获取和检查质数方面完美无缺,但我目前的头痛是写一个文件所有线程一起产生的。我可以将每个线程单独写入文件,但最终每次都会相互写入

1 个答案:

答案 0 :(得分:0)

如果我理解正确,您需要每60秒将所有消费者的累积素数写入文件。

现在您使用prime_list作为实例变量,因此多个线程将拥有自己的prime_list。您可以将prime_list标记为static,并且访问这些的方法也是静态的。在这些方法中,代码必须在synchronized块中。

这不是最好的设计。如果我从头开始写作,我将不会使用这个设计,但它会对你有用。

import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedWriter;  
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Date;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;

public class CheckThread  implements Runnable{

private final GenerateThread generated;
private static ArrayList<Primes> prime_list = new ArrayList();
//private CopyOnWriteArrayList<Primes> prime_list = new CopyOnWriteArrayList();
private final String threadName;
private boolean alive = true;
private boolean toggle = true;
private Date timeFetched;
private Date timeFound;
private Primes currentPrime;

// START: FOR PANEL LAYOUT //
JPanel genPanel = new JPanel();

JPanel lastPane = new JPanel();
JLabel lastOutput = new JLabel();

JPanel queuePane = new JPanel();
JLabel queueOutput = new JLabel();

JLabel headLabel = new JLabel("Generate Thread");
JLabel lastLabel = new JLabel("Last");
JLabel queueLabel = new JLabel("In Work");

JButton pauseButton = new JButton("Pause");
JButton terminateButton = new JButton("Terminate");
// END: FOR PANEL LAYOUT //

CheckThread(GenerateThread gen, String name, int priority)
{
    generated = gen;
    threadName = name;
    headLabel.setText(name);
    System.out.println("Creating | " + name);

    if(priority == 0)
    {
       System.out.println(name + " Is writing thread");
        final Timer t = new Timer(10000, new writeToFileTimer());
        t.start();
    }
}

public JPanel createPanel()
{
   lastPane.setBorder(BorderFactory.createLineBorder(Color.black));
   lastPane.setMinimumSize(new Dimension(200,100));
   queuePane.setBorder(BorderFactory.createLineBorder(Color.black));


   terminateButton.addActionListener(new terminateListener());

   pauseButton.addActionListener(new pauseListener());

   lastPane.add(lastOutput);
   queuePane.add(queueOutput);

   genPanel.add(headLabel);
   genPanel.add(lastLabel);
   genPanel.add(lastPane);
   genPanel.add(queueLabel);
   genPanel.add(queuePane);
   genPanel.add(pauseButton);
   genPanel.add(terminateButton);

   lastOutput.setIgnoreRepaint(true);
   return genPanel;
}
 class writeToFileTimer implements ActionListener
 {
    @Override
    public void actionPerformed(ActionEvent event)
    {
       try{ writeToFile();}catch (InterruptedException e) { System.out.println("WTF");}
    }
 }

@Override
public void run() 
{
    while(alive)
    {
        try 
        {
            if(toggle)
            {
                PrimeCandidate recieved = new PrimeCandidate(generated.getFromArrayList());
                timeFetched = new Date();
                //queueOutput.setText(recieved.getCandidate().toString());
                if(TestForPrime.isPrime(recieved.getCandidate()))
                {
                    lastOutput.setText(recieved.getCandidate().toString());
                    timeFound = new Date();
                    String temp = "" + prime_list.size();
                    queueOutput.setText(temp);
                    addToArrayList(new Primes(recieved.getCandidate(),recieved.getTimeStamp(), timeFetched));
                }
            }
        } catch (InterruptedException e) {}
    }
}

class terminateListener implements ActionListener
{
    @Override
    public void actionPerformed(ActionEvent e) 
    { 
        terminateButton.setEnabled(false);
        pauseButton.setEnabled(false);
        alive = false;
    }
}

class pauseListener implements ActionListener
{
    @Override
    public void actionPerformed(ActionEvent e) 
    { 
        if(toggle)
            pauseButton.setText("Start");
        if(!toggle)
            pauseButton.setText("Pause");
        toggle = !toggle;
    }
}

// ADDS NEW ITEM TO ARRAYLIST AND NOTIFYS ALL THREADS DEPENDENT ON ARRAYLIST 
public static void addToArrayList(Primes primeFound)
{
   synchronized(prime_list) { // Added this
       prime_list.add(primeFound);
   }
}

// USED BY THE OTHER THREADS TO GET ITEMS FROM THE ARRAY LIST, THREADS DELETE
// THE VARIABLE THAT THEY GRAB, ESSENTIALLY CUTTING THE ARRAY LIST DOWN EVERY
// TIME
public static void writeToFile() throws InterruptedException
{
    synchronized(prime_list) { // Added this
    try 
    {
        File statText = new File("prime_numbers.txt");

        FileOutputStream is = new FileOutputStream(statText);

        OutputStreamWriter osw = new OutputStreamWriter(is);    

        Writer writ = new BufferedWriter(osw);
        for(int i=0;i<prime_list.size();i++) {
            writ.append(prime_list.get(i));
        }
        prime_list.clear();
        writ.close();
        headLabel.setText(threadName);

    } catch (IOException e) {System.err.println(" failed to write to Test.txt");}
    }
}   
}