Synchronize Three Threads

谁说胖子不能爱 提交于 2019-11-30 15:16:18

As the others already mentioned, CyclicBarrier is not exactly the best tool for the task.

I also share the opinion that the solution is to chain the threads and let always one thread set the go for the next one.

Here goes an implementation using Semaphore:

import java.util.concurrent.BrokenBarrierException; 
import java.util.concurrent.Semaphore;

public class PrintNumbersWithSemaphore implements Runnable {

private final Semaphore previous;

private final Semaphore next;

private final int[] numbers;

public PrintNumbersWithSemaphore(Semaphore previous, Semaphore next, int[] numbers) {
    this.previous = previous;
    this.next = next;
    this.numbers = numbers;
}

@Override
public void run() {

    for (int i = 0; i < numbers.length; i++) {
        wait4Green();

        System.out.println(numbers[i]);

        switchGreen4Next();
    }
}

private void switchGreen4Next() {
        next.release();
}

private void wait4Green() {
    try {
        previous.acquire();
    } catch (InterruptedException e) {
        e.printStackTrace();
        throw new RuntimeException(e);
    }
}

static public void main(String argv[]) throws InterruptedException, BrokenBarrierException {
    Semaphore sem1 = new Semaphore(1);
    Semaphore sem2 = new Semaphore(1);
    Semaphore sem3 = new Semaphore(1);
    sem1.acquire();
    sem2.acquire();
    sem3.acquire();
    Thread t1 = new Thread(new PrintNumbersWithSemaphore(sem3, sem1, new int[] { 1, 4, 7 }));
    Thread t2 = new Thread(new PrintNumbersWithSemaphore(sem1, sem2, new int[] { 2, 5, 8 }));
    Thread t3 = new Thread(new PrintNumbersWithSemaphore(sem2, sem3, new int[] { 3, 6, 9 }));
    t1.start();
    t2.start();
    t3.start();
    sem3.release();

    t1.join();
    t2.join();
    t3.join();
}

}

Here goes another one, in my opinion quite cumbersome implementation using CyclicBarrier:

import java.util.concurrent.BrokenBarrierException; 
import java.util.concurrent.CyclicBarrier;

public class PrintNumbersWithCyclicBarrier implements Runnable {

private final CyclicBarrier previous;

private final CyclicBarrier next;

private final int[] numbers;

public PrintNumbersWithCyclicBarrier(CyclicBarrier previous, CyclicBarrier next, int[] numbers) {
    this.previous = previous;
    this.next = next;
    this.numbers = numbers;
}

@Override
public void run() {

    for (int i = 0; i < numbers.length; i++) {
        wait4Green();

        System.out.println(numbers[i]);

        switchRed4Myself();

        switchGreen4Next();
    }
}

private void switchGreen4Next() {
    try {
        next.await();
    } catch (Exception e) {
        e.printStackTrace();
        throw new RuntimeException(e);
    }
}

private void switchRed4Myself() {
    previous.reset();
}

private void wait4Green() {
    try {
        previous.await();
    } catch (Exception e) {
        e.printStackTrace();
        throw new RuntimeException(e);
    }
}

static public void main(String argv[]) throws InterruptedException, BrokenBarrierException {
    CyclicBarrier cb1 = new CyclicBarrier(2);
    CyclicBarrier cb2 = new CyclicBarrier(2);
    CyclicBarrier cb3 = new CyclicBarrier(2);
    Thread t1 = new Thread(new PrintNumbersWithCyclicBarrier(cb3, cb1, new int[] { 1, 4, 7 }));
    Thread t2 = new Thread(new PrintNumbersWithCyclicBarrier(cb1, cb2, new int[] { 2, 5, 8 }));
    Thread t3 = new Thread(new PrintNumbersWithCyclicBarrier(cb2, cb3, new int[] { 3, 6, 9 }));
    t1.start();
    t2.start();
    t3.start();
    cb3.await();

    t1.join();
    t2.join();
    t3.join();
}

}

Was the requirement to use a single CyclicBarrier? My suggestion is:

  1. For each ThreadOne instance assign two CyclicBarriers
  2. You should create a cyclic graph such that

    ThreadOne_1 -> ThreadOne_2 -> ThreadOne_3 -> ThreadOne_1 -> etc...

  3. To achieve (2) you would need to share the parent's CyclicBarrier with the child's and than the last task should share it's CB with the first Thread's child.

To answer your questions:

Tried going through the documents , but not very clear what exactly does await() do ...

Await will suspend itself until N number of threads have invoked await on the barrier. So if you define new CyclicBarrier(3) than once 3 threads invoke await the barrier will allow threads to continue.

When to use reset()

You don't need to, it will auto trip the barrier once the number of threads arive

Here is a way of doing it which works for an arbitrary number of threads using synchronized, wait and notifyAll.
A turn variable controls which is the thread that has to execute. Such thread executes the task, increases turn (in modulo number of threads), notifeis all threads and goes to a while loop awaiting until it is its turn again.

public class Test2 {

    final static int LOOPS = 10;
    final static int NUM_TREADS = 3;

    static class Sequenced extends Thread {

        static int turn = 0;
        static int count = 0;
        static Object lock = new Object();
        final int order;

        public Sequenced(int order) {
            this.order = order;
        }

        @Override
        public void run() {
            synchronized (lock) {
                try {
                    for (int n = 0; n < LOOPS; ++n) {
                        while (turn != order) {
                            lock.wait();
                        }
                        ++count;
                        System.out.println("T" + (order + 1) + " " + count);
                        turn = (turn + 1) % NUM_TREADS;
                        lock.notifyAll();
                    }
                } catch (InterruptedException ex) {
                    // Nothing to do but to let the thread die.
                }
            }
        }
    }

    public static void main(String args[]) throws InterruptedException {
        Sequenced[] threads = new Sequenced[NUM_TREADS];
        for (int n = 0; n < NUM_TREADS; ++n) {
            threads[n] = new Sequenced(n);
            threads[n].start();
        }
        for (int n = 0; n < NUM_TREADS; ++n) {
            threads[n].join();
        }
    }

}

As threads should be given equal weightage i.e first after second after third. The following works.

public class MultiThreadSynchronize {

static int index = 0;
static Object lock = new Object();

static class NumberPrinter extends Thread {
    private final int remainder;
    private final int noOfThreads;
    private int value;

    public NumberPrinter(String name, int remainder, int noOfThreads, int value) {
        super(name);
        this.remainder = remainder;
        this.noOfThreads = noOfThreads;
        this.value = value;
    }

    @Override
    public void run() {
        while (index < 20) {
            synchronized (lock) {
                while ((index % noOfThreads) != remainder) {. // base condition where all threads except one waits.
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                index++;
                System.out.println(getName() + " " + value);
                value += 3;
                lock.notifyAll();
            }
        }
    }
}

public static void main(String[] args) {
    NumberPrinter numberPrinter1 = new NumberPrinter("First Thread", 0, 3, 1);
    NumberPrinter numberPrinter2 = new NumberPrinter("Second Thread", 1, 3, 2);
    NumberPrinter numberPrinter3 = new NumberPrinter("Third Thread", 2, 3, 3);
    numberPrinter1.start(); numberPrinter2.start(); numberPrinter3.start();
}
}

I have coded one three thread application using events

#include <iostream>
#include <Windows.h>
#include <atomic>
using namespace std;


HANDLE firstThreadEvent = CreateEvent(NULL, true, true, NULL);
HANDLE secondThreadEvent = CreateEvent(NULL, true, false, NULL);
HANDLE thirdThreadEvent = CreateEvent(NULL, true, false, NULL);
std::atomic<int> m_int = 1;

DWORD WINAPI firstThreadFun(LPVOID lparam)
{
    while (1)
    {
        ::WaitForSingleObject(firstThreadEvent,INFINITE);
        cout << "By first thread " << m_int << std::endl;
        m_int++;
        ResetEvent(firstThreadEvent);
        SetEvent(secondThreadEvent);

    }
}
DWORD WINAPI secondThreadFun(LPVOID lparam)
{
    while (1)
    {
        ::WaitForSingleObject(secondThreadEvent, INFINITE);
        cout << "By second thread "<< m_int << std::endl;
        m_int++;
        ResetEvent(secondThreadEvent);
        SetEvent(thirdThreadEvent);

    }

}
DWORD WINAPI thirdThreadFun(LPVOID lparam)
{
    while (1)
    {
        ::WaitForSingleObject(thirdThreadEvent, INFINITE);
        cout << "By third thread " << m_int << std::endl;
        m_int++;
        ResetEvent(thirdThreadEvent);
        SetEvent(firstThreadEvent);

    }
}



int main()
{
    HANDLE hnd[3];
    hnd[0] = CreateThread(NULL, 0, &firstThreadFun, NULL, 0, NULL);
    hnd[1] = CreateThread(NULL, 0, &secondThreadFun, NULL, 0, NULL);
    hnd[2] = CreateThread(NULL, 0, &thirdThreadFun, NULL, 0, NULL);

    WaitForMultipleObjects(3, hnd, true, INFINITE);
    CloseHandle(hnd[0]);
    CloseHandle(hnd[1]);
    CloseHandle(hnd[2]);
    return 0;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!