简体   繁体   中英

Java CountDownLatch with Threads

I am looking to learn about using the Java CountDownLatch to control the execution of a thread.

I have two classes. One is called Poller and the other is Referendum . The threads are created in the Referendum class and their run() methods are contained in the Poller class.

In the Poller and Referendum classes I have imported the java countdown latch via import java.util.concurrent.CountDownLatch .

I am mainly looking to understand why and where the the *.countDown(); and *.await(); statements need to be applied and also to understand if I have correctly initialised the countDownLatch within the Poller constructor.

The complete code for the two classes are:

import java.util.concurrent.CountDownLatch;

public class Poller extends Thread
{
private String id;                  // pollster id
private int pollSize;               // number of samples
private int numberOfPolls;          // number of times to perform a poll
private Referendum referendum;      // the referendum (implies voting population)
private int sampledVotes[];         // the counts of votes for or against
static CountDownLatch pollsAreComplete; //the CountDownLatch


/**
 * Constructor for polling organisation.
 * @param r A referendum on which the poller is gathering stats
 * @param id The name of this polling organisation
 * @param pollSize The size of the poll this poller will use
 * @param pollTimes The number of times this poller will conduct a poll
 * @param aLatch The coutn down latch that prevents the referendum results from being published
 */

public Poller(Referendum r, String id, int pollSize, int pollTimes, CountDownLatch aLatch)
{
    this.referendum = r;
    this.id = id;
    this.pollSize = pollSize;
    this.numberOfPolls = pollTimes;
    this.pollsAreComplete = aLatch;
    aLatch = new CountDownLatch(3);

    // for and against votes to be counted
    sampledVotes = new int[2];
}


// getter for numberOfPolls
public int getNumberOfPolls()
{
    return numberOfPolls;
}

@Override
//to use the countdown latch
public void run()
{      
    for (int i = 0; i < getNumberOfPolls(); i++)
    {
        resetVotes();
        pollVotes();
        publishPollResults();
    }
}

// make sure all sampledVotes are reset to zero
protected void resetVotes()
{
    // initialise the vote counts in the poll
    for (int i = 0; i < sampledVotes.length; i++)
    {
        sampledVotes[i] = 0;
    }
}

// sampling the way citizens will vote in a referendum
protected void pollVotes()
{
    for (int n = 0; n < pollSize; n++)
    {
        Citizen c = referendum.pickRandomCitizen();

        //As things stand, pickRandomCitizen can return null
        //because we haven't protected access to the collection
        if (c != null)
        {
            sampledVotes[c.voteFor()]++;
        }
    }
}

protected void publishPollResults()
{
    int vfor = 100 * sampledVotes[Referendum.FOR] / pollSize;

    int vagainst = 100 * sampledVotes[Referendum.AGAINST] / pollSize;

    System.out.printf("According to %-20s \t(", this.id + ":");

    System.out.print("FOR " + vfor);

    try
    {
        Thread.sleep(1000);
    } catch (Exception e)
    {
        e.printStackTrace();
    }

    System.out.println(", AGAINST " + vagainst + ")");
 }
}

And

import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CountDownLatch;  


public class Referendum
{
private List<Citizen> citizens;         //voters
private List<Poller> pollers;           //vote samplers
public static final int FOR = 0;        //index for votes array
public static final int AGAINST = 1;    //index for votes array
private int votes[];                    //for and against votes counters


public Referendum(int population)
{
    citizens = new LinkedList<Citizen>();
    pollers = new LinkedList<Poller>();

    // initialise the referendum with the population
    for (int i = 0; i < population; i++)
    {
        Citizen c = new Citizen(i % 4); //suppose equal party membership
        citizens.add(c);
    }

    votes = new int[2]; //in this example, only For or Against
}

public void addPoller(Poller np)
{
    pollers.add(np);
}

public Citizen removeCitizen(int i)
{
    return citizens.remove(i);
}

public List<Poller> getPollers()
{
    return pollers;
}

public void startPollsWithLatch()
{
   //create some poller threads that use a latch
    addPoller(new Poller(this, "The Daily Day", 100, 3, Poller.pollsAreComplete));
    addPoller(new Poller(this, "Stats people", 100, 3, Poller.pollsAreComplete));
    addPoller(new Poller(this, "TV Pundits", 100, 3, Poller.pollsAreComplete));

    // start the polls
    for (Poller p : pollers)
    {
        p.start();

    }
}



// pick a citizen randomly - access not controlled yet
public Citizen pickRandomCitizen()
{
    //TODO add code to this method for part (b)

    Citizen randomCitizen;
    // first get a random index
    int index = (int) (Math.random() * getPopulationSize());
    randomCitizen = citizens.remove(index);

    return randomCitizen;
}

// Counting the actual votes cast in the referendum
public void castVotes()
{
    for (int h = 0; h < getPopulationSize(); h++)
    {
        Citizen c = citizens.get(h);

        votes[c.voteFor()]++;
    }
}

// tell the size of population
public int getPopulationSize()
{
    return citizens.size();
}

// display the referendum results
public void revealResults()
{
    System.out.println(" **** The Referendum Results are out! ****");

    System.out.println("FOR");
    System.out.printf("\t %.2f %%\n", 100.0 * votes[FOR] / getPopulationSize());

    System.out.println("AGAINST");
    System.out.printf("\t %.2f %%\n", 100.0 * votes[AGAINST] / getPopulationSize());
}

public static void main(String[] args)
{
    // Initialise referendum. The number of people
    // has been made smaller here to reduce the simulation time.
    Referendum r = new Referendum(50000);


    r.startPollsWithLatch();

    r.castVotes();

    // reveal the results of referendum
    r.revealResults();
 }
}

In a nutshell... All threads must execute the publishPollResults(); statement BEFORE the revealResults(); is executed.

OK,

Now, if the publishPollResults must be done by all before the reavelResults, then simply you need to wait for the proper count in your reaveal method. But to do so, the latch must be also shared with the referendum object not only with Pollers.

so, let the referendum creates the latch and pass it to the pollers:

public class Referendum
{
  CountDownLatch pollsAreComplete;
  ...

    public void startPollsWithLatch()
    {
        pollsAreComplete = new CountDownLatch(3); //create new latch to know when the voting is done
       //create some poller threads that use a latch
        addPoller(new Poller(this, "The Daily Day", 100, 3, pollsAreComplete)); //pass it to pollers
        addPoller(new Poller(this, "Stats people", 100, 3, pollsAreComplete));
        addPoller(new Poller(this, "TV Pundits", 100, 3, pollsAreComplete));

        // start the polls
        for (Poller p : pollers)
        {
            p.start();
        }
    }

    public void revealResults()
    {

    pollsAreComplete.await(); //we can pass this line only if the latch count went to 0

    System.out.println(" **** The Referendum Results are out! ****");
    ....
    }

}

so the Pollers should share the latch. You are using static variable which is OKish but you want be able to use the Pollers with different referendums. So it is betther, that have it is a instance field and pass it in constructor (you kind of started with constructor but then you passed the value to static variable which makes no sense (and it actaully was always null).

public class Poller extends Thread
{
    ...
    private CountDownLatch pollsAreComplete; //the CountDownLatch shared with referendum

    public Poller(Referendum r, String id, int pollSize, int pollTimes, CountDownLatch aLatch)
    {
        ...
        this.pollsAreComplete = aLatch;
    }

    public void run()
    {      
        for (int i = 0; i < getNumberOfPolls(); i++)
        {
            resetVotes();
            pollVotes();
            publishPollResults();
        }
        pollsAreComplete.countDown(); //voting is finished, let the referendum publish the results.
    }

}

So once the Poller finished its work it lowers the latch, and when all do it the referendum can continue and print the results.

Mind you all Poller thread will publish their results 3 times (as they have for loop) and only when all 3 are cycles are down they will signal the referendum.

If you wanted the 3 separate phases of referendum it will be very difficult to achieve if with latch as it cannot be reset once it's been down to 0.

If I understood correctly, you want all threads to execute before the results are shown. This requires a single CountDownLatch instance in the Referendum class that is passed to the constructor of each Poller thread. Each Poller calls countdown() on the latch once it ends the poll, and Referendum calls await() to sleep until the latch countdown reaches zero:

class Referendum {
  private CountDownLatch latch;
  public CountDownLatch getLatch() {
    return latch;
  }

  // ...

  public void startVotesWithLatch() {
    // You don't need to pass the latch in constructor,
    // as you can retrieve it from the referendum object passed
    addPoller(new Poller(this, "Stats people", 100, 3));
    // Add other pollers
    // Start all pollers
    for (Poller p : pollers) {
      p.start();
    }
    // Wait for all pollers to finish
    latch.await();
  }
}

And in the Poller class remove the latch variable as it is not needed, then in the publishPollResults() method:

public void publishPollResults() {
  // Everything stays the same here, except we decrease the latch
  // when finished...
  referendum.getLatch().countDown();
}

Note however that this type of synchronization is quite simple and does not necessarily require a CountDownLatch , you can simply spawn your Poller threads and then call join() on the main thread (this will pause the main thread until the child threads finish execution).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM