Can we write Threadpool executor service inside an executor service ?
Can anyone suggest how to run parallel tasks inside parallel tasks ?
Suppose there are 10 tasks which need to run in parallel and inside each task I have to run 100 parallel tasks. Any suggestions please
ExecutorService executor1 = Executors.newFixedThreadPool(8);
for (int i = 0; i < 8; i++) {
ExecutorService executor2 = Executors.newFixedThreadPool(115);
for (int j = 0; j < 115; j++) {
Runnable worker = new UpdatecheckerTest(Region.getRegion(Regions.US_EAST_1),"");
executor2.execute(worker);
}
}
executor1.shutdown();
Is this the correct approach ?
This approach will work, but I think the right solution depends on a few other things that you are not mentioning.
Simple Case
if the problem you are trying to solve is very simple, short, not a very big part of your overall system and performance or stability is not much of a concern. i wouldn't even bother with using a Thread pool at all and just use parallel streams
your code could look something like this:
IntStream.range(0,8).().forEach(i -> {
IntStream.range(0,115).parallel().forEach(j -> {
new UpdatecheckerTest(Region.getRegion(Regions.US_EAST_1),"").run();
});
});
Main part of the overall system
If the problem you are trying to solve is really a major part of your system, when i look at what you are describing i actually see a large task which is representing what is happening inside the outer loop (the i loop) and a small tasks which is representing what is happening inside the inner loop (the j loop). If those tasks take up a main role in your system you might want to put those tasks in their own classes to make them more readable, reusable and easier to change later on.
your code could look something like that:
SmallTask.java
import java.text.MessageFormat;
public class SmallTask implements Runnable {
private String identifier;
public SmallTask (String identifier) {
this.identifier = identifier;
}
#Override
public void run() {
System.out.println(String.format(MessageFormat.format("Executing SmallTask with id: {0}", identifier)));
// what ever happens in new UpdatecheckerTest(Region.getRegion(Regions.US_EAST_1),"").run()
}
}
LargeTask.java
import java.text.MessageFormat;
import java.util.stream.IntStream;
public class LargeTask implements Runnable {
private String identifier;
public LargeTask (String identifier) {
this.identifier = identifier;
}
#Override
public void run() {
System.out.println(String.format(MessageFormat.format("Executing LargeTask with id: {0}", identifier)));
IntStream.range(0, 115).parallel().forEach(j -> {
new SmallTask(identifier + "-" + String.valueOf(j)).run();
});
}
}
Main.java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
IntStream.range(0,8).parallel().forEach(i -> {
new LargeTask(String.valueOf(i)).run();
});
}
}
i would even go a step further and say that the large task or what initiates it could be an event in an event driven architecture you could organize your system to have different kinds of events all of which could be executed asynchronously.
Performance and stability matters
If this code runs very frequently in you system then i would consider using a thread poll which allows you to control how many threads are being used and if the threads allocated to run LargeTask are the same as those allocated to run SmallTask or not.
then your code could look something like this:
SmallTask.java
import java.text.MessageFormat;
public class SmallTask implements Runnable {
private String identifier;
public SmallTask (String identifier) {
this.identifier = identifier;
}
#Override
public void run() {
System.out.println(String.format(MessageFormat.format("Executing SmallTask with id: {0}", identifier)));
// what ever happens in new UpdatecheckerTest(Region.getRegion(Regions.US_EAST_1),"").run()
}
}
LargeTask.java
import java.text.MessageFormat;
import java.util.stream.IntStream;
public class LargeTask implements Runnable {
private String identifier;
public LargeTask (String identifier) {
this.identifier = identifier;
}
#Override
public void run() {
System.out.println(String.format(MessageFormat.format("Executing LargeTask with id: {0}", identifier)));
IntStream.range(0, 115).forEach(j -> {
TasksExecutor.getSmallTaskExecutor().execute(new SmallTask(identifier + "-" + String.valueOf(j)));
});
}
}
TasksExecutor.java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TasksExecutor {
private static ExecutorService largeTasksExecutor = Executors.newFixedThreadPool(8);
private static ExecutorService smallTaskExecutor = Executors.newFixedThreadPool(115);
public static ExecutorService getLargeTaskExecutor () {
return largeTasksExecutor;
}
public static ExecutorService getSmallTaskExecutor () {
return smallTaskExecutor;
}
}
Main.java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
IntStream.range(0,8).forEach(i -> {
TasksExecutor.getLargeTaskExecutor().execute(new LargeTask(String.valueOf(i)));
});
}
}
Don't forget to add functionality to close the thread pool if needed.
and maybe add some sort of dependency injection between each task and the specific thread pool that you want to manage it it will give you better flexibility later on
If you want to take it a step further you could instead use a Messaging Framework where you could use different queues to manage all the tasks that need to take place. like ZeroMQ and Kafka
Related
I have two concurrent threads (producer and consumer), and two queues (pending and execution).
This is a sample flow for producer:
"1" - If not duplicate (does not exist in any of queues), push task T1
"3" - If not duplicate (does not exist in any of queues), push task T1
And this is a sample flow for consumer:
"2" - Poll data from "pending" queue
"4" - If found something, push it into "execution" queue and run it in a separate thread.
Notice the numbering above:
If between steps 2 and 4, step 3 happens, it can insert a duplicate because the data is still in-memory and is not pushed into "execution" queue yet.
How can I prevent this? I can not put a lock on both queues because then the "consumer" thread will always keep the lock (it is an always running thread polling for data).
P.S.
This is how my consumer looks like:
while ( true ) {
var nextTask = pending.poll(100, MILLISECOND); //STEP 2
if ( nextTask != null ) {
executeQueue.add(nextTask); //STEP 4
executeInParallel(nextTask);
}
}
This might not be a direct answer to your question. But if you are looking for a solution to queue tasks to process them with a number of threads you should have a look at the Executors from Java.
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class JobQueue {
public static void main(String[] args) {
ThreadPoolExecutor executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
executorService.submit(() -> {
// do someting
return "result";
});
}
}
Update: check queue:
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
public class JobQueue {
public static void main(String[] args) {
ThreadPoolExecutor executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
Runnable task = () -> {
// do someting
};
BlockingQueue<Runnable> queue = executorService.getQueue();
if(!queue.contains(task)) {
executorService.submit(task);
}
}
}
You can synchromize on an Object uning wait() and notify()
import java.util.ArrayDeque;
import java.util.HashSet;
import java.util.Queue;
import java.util.Set;
public class JobQueue {
private static final Object syncMon = new Object();
private static final Queue<Object> pending = new ArrayDeque<>();
private static final Set<Object> executing = new HashSet<>();
public void produce(Object o) {
synchronized (syncMon) {
if(pending.contains(o) || executing.contains(o))
return;
pending.add(o);
syncMon.notifyAll();
}
}
public Object consume() throws InterruptedException {
synchronized (syncMon) {
if(pending.isEmpty())
syncMon.wait();
Object task = pending.poll();
if(task != null) {
executing.add(task);
}
return task;
}
}
public void complete(Object task) {
synchronized (syncMon) {
executing.remove(task);
}
}
}
The syncMon object is not necesarry. You could also use wait() and notify() on the Queue directly.
like pending.notifyAll();
To explain this a litte: If you invoke wait() in a synchronized block the lock is released. So the producer can enter the synchronized while the consumer is waiting. If you call notify() or notifyAll() the waiting thread wakes up and takes the lock back, once the producer has exited the synchronized block.
Update 1: add execution set.
So I'm making an emulator for an online game and I can't seem to think of a good way to deal with lots of tasks running simultaneously. Loading everything on a single thread just doesn't work, obviously.
My idea was to have a main thread that delegates tasks to x amount of worker threads. Once the main thread is done queueing tasks, it signals the workers to start firing tasks and halts untill they have finished. My implementation is as follows:
package com.rs2.engine;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.CountDownLatch;
import java.util.ArrayList;
import com.rs2.util.Timer;
public class Engine implements Runnable {
private ScheduledExecutorService scheduledExecutorService;
private ExecutorService executorService;
private int currentTick;
private ArrayList<Task> tasks;
private Timer timer;
public Engine(int workers) {
this.executorService = Executors.newFixedThreadPool(workers);
this.scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
this.currentTick = 0;
this.tasks = new ArrayList<>(10000);
this.timer = new Timer();
}
public int getCurrentTick() {
return currentTick;
}
public ExecutorService getWorkers() {
return executorService;
}
public void start() {
this.scheduledExecutorService.scheduleAtFixedRate(this, 0, 600, TimeUnit.MILLISECONDS);
}
public void cycle() {
}
public void queueTask(Task task) {
tasks.add(task);
}
public void processQueuedTasks() {
try {
CountDownLatch latch = new CountDownLatch(tasks.size());
for (int i = 0; i < tasks.size(); i++) {
Task t = tasks.get(i);
t.setCountDownLatch(latch);
executorService.submit(t);
}
latch.await();
tasks.clear();
} catch(Exception e) {
e.printStackTrace();
}
}
#Override
public void run() {
timer.reset();
cycle();
currentTick++;
//System.out.println("Cycle time: "+timer.elapsed());
}
}
queueTask() and processQueuedTasks() are only called from the main thread. This Engine is used to deal with any and all tasks that the server needs executing. First it processes networking events (incoming packets), then updating entities and other events. That's why I like to keep it abstract.
Here's the Task class aswell:
package com.rs2.engine;
import java.util.concurrent.CountDownLatch;
public class Task implements Runnable {
private CountDownLatch latch;
public Task() {
}
#Override
public void run() {
execute();
latch.countDown();
}
public void execute() {
}
public void setCountDownLatch(CountDownLatch latch) {
this.latch = latch;
}
}
My questions are as follows:
In the Engine class, is it OK to work with a regular ArrayList in terms of concurrency?
Is there a better way of queueing tasks to the ExecutorService? I feel like it might cause issues if too many tasks are queued to it at the same time.
Are there any engine frameworks I should be looking at before I start reinventing the wheel?
If you are worried that you might be queueing too much tasks in the ExecutorService you can use Semaphore to limit the tasks it can run at a time.
Put it in your processQueuedTasks() method for loop to limit the number of tasks to be run.
https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Semaphore.html
Instead of using a Semaphore to throttle the number of tasks run concurrently, you could set the number of threads in the threadpool. You will only get as many tasks running concurrently as you have threads
Executors.newFixedThreadPool(n);
public void queueTask(Task task) {
As this method is public, it can be called from any thread and so the ArrayList would not be thread safe. Think about what would happen if you tried to queue a task during the time they are submitted to the executor. You have 'borrowed' functionality from a thread pool executor and placed it in your code by having the collection for tasks.
I am working on a streaming java application that is using several long running worker threads. The application receives data, processes it, and then sends it along toward a third party using their SDK. There is an Engine class that recieves data and submits it to Workers. The worker threads will live for as long as the application runs, which could be months if not years.
I have included sample code that represents this key part of this question.
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BarEngine implements Engine
{
static Logger log = LoggerFactory.getLogger(BarEngine.class);
private static final int WORKER_COUNT = 5;
private BlockingQueue<Map<String, Object>> queue;
private FooWorker[] workers = new FooWorker[WORKER_COUNT];
public BarEngine()
{
for (int i = 0; i < WORKER_COUNT; i++)
{
workers[i] = new FooWorker(i, queue);
workers[i].start();
}
}
// From Engine Interface
#Override
public void sendEvent(final Map<String, Object> data)
{
try
{
queue.put(data);
}
catch (InterruptedException e)
{
log.error("Unexpected Exception", e);
}
}
// From Engine Interface
#Override
public void shutDown()
{
// Shuts down engine
}
public static class FooWorker extends Thread
{
static Logger log = LoggerFactory.getLogger(FooWorker.class);
private volatile boolean run = true;
private int id;
private BlockingQueue<Map<String, Object>> queue;
private Client client;
public FooWorker(int id, BlockingQueue<Map<String, Object>> queue)
{
this.id = id;
this.queue = queue;
client = Client.build(id);
}
#Override
public void run()
{
setName("FooWorker-" + id);
while (run)
{
try
{
Map<String, Object> data = queue.poll(5, TimeUnit.SECONDS);
if (null != data)
{
sendEvent(data);
}
}
catch (Throwable e)
{
log.error("Unexpected Exception", e);
}
}
}
private void sendEvent(Map<String, Object> data)
{
try
{
client.submit(data);
}
catch (Throwable e)
{
log.error("Unexpected Exception", e);
}
}
// dummy client classs
public static class Client
{
public void submit(Map<String, Object> data)
{
// submits data
}
public static Client build(int id)
{
// Builds client
return new Client();
}
}
}
}
I have been doing a bit of research, and I have not found a satisfactory answer.
Java Concurrency in Practice : Does not provide much guidance on long running threads.
When should we use Java's Thread over Executor? : Heavily suggest I should ALWAYS use an Executor. Does not cover the application long-life threads per se.
Java Executor and Long-lived Threads : Discusses managing long lived threads with Executor but does not answer if one SHOULD manage long live threads with Executor
My question is: Should I keep these long running Threads bare like this? If not, what should I replace it with (Like ExecutorService or something else)?
Answering your question, if you have threads which has the same lifetime of the application, in my opinion it doesn't matter if you are using a Thread or Executer service (which is again using Threads underneath) as long as you manage the thread's life cycle properly.
From design point of view your application falls in to software category what we called a "middleware". Generally a middleware application should be efficient as well as scalable, both which are essential qualities of such server yet you have ignored both. Your application's threads run busy-wait loops per thread keeping the CPU busy at all time. Even when the incoming load is very low this keeps happening. Which is a not good quality to have for such application.
Alternatively, I'm proposing you to use a ThreadPool implementation such as ThreadPoolExecutor which already have solved what you are trying to accomplish here. ThreadPoolExecutor leverages the functionality of a BlockingQueue if all initially fired up threads are busy at the moment. also it can stop threads if the load is low and fire up again if wanted. I have coded the structure of the design I'm proposing. Take a look at the following code. I assumed that Client is not thread-safe so I'm constructing a Client per thread. If your real client implementation is thread-safe you can use one client across all threads.
import java.util.Map;
import java.util.concurrent.*;
public class BarEngine implements Engine {
private static final int WORKER_COUNT = 5;
private ExecutorService threadPool;
public BarEngine() {
this.threadPool = new ThreadPoolExecutor(1, WORKER_COUNT, 10, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(100));
}
// From Engine Interface
#Override
public void sendEvent(final Map<String, Object> data) {
threadPool.submit(new FooWorker(data));
}
// From Engine Interface
#Override
public void shutDown() {
this.threadPool.shutdown();
// Shuts down engine
}
public static class FooWorker implements Runnable {
private final Client client;
private final Map<String, Object> data;
public FooWorker(Map<String, Object> data) {
client = Client.build(Thread.currentThread().getId());
this.data = data;
}
#Override
public void run() {
try {
if (null != data) {
sendEvent(data);
}
} catch (Throwable e) {
//todo log
}
}
private void sendEvent(Map<String, Object> data) {
try {
client.submit(data);
} catch (Throwable e) {
//todo log
}
}
// dummy client classs
public static class Client {
public void submit(Map<String, Object> data) {
// submits data
}
public static Client build(long id) {
// Builds client
return new Client();
}
}
}
}
Yes, what you posted is exactly something for ExecutorService. Some advices:
There are two main interfaces Executor, ExecutorService.
Executors should be ended if you dont need them via shutdown/shutdownNow methods on ExecutorService interface. If not, you will face memleaks.
Create your ExecutorService via Executors, for example:
Executors.newFixedThreadPool(5);
If you use Executor service, you can push data directly to ExecutorService via Runnable because ExecutorService doing queuing it self and split tasks into its workers...
I have to simulate the activity of a postal office, where an office worker can offer many services like paying bills, send letters etc. I have created a separate application which simulates the incoming clients and store them in the database. The second application has to manage the "clients" from the database and assign to each office worker "clients" that have requested specific tasks. If the office worker is busy then the client should be added in a waiting queue. Each office worker should do the job in a new thread. The problem is that when i run the application only one thread is doing his job. Then the application stops. Anyone has any suggestions on how can i implement this application using multithreading?
Office worker class
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Random;
import ro.csm.database_access.OperationDatabaseAccess;
import ro.csm.database_access.TaskDatabaseAccess;
public class OfficeWorker {
//each office worker has a queue of tasks, a list of supported operations, an office and a thread
private Queue<Task> tasks;
private List<Operation> supportedOperations;
private Thread workingThread;
private Office office;
//constructor
public OfficeWorker(Office office){
this.office = office;
supportedOperations = OperationDatabaseAccess.getAllSupportedOperations(office.getId());
workingThread = new Thread();
workingThread.start();
setTasks(new LinkedList<Task>());
}
//add task to the tasks queue
public void addTask(Task task){
getTasks().add(task);
}
//finish the task
public void work(){
while(true){
Task t = getTasks().poll();
if(t != null){
TaskDatabaseAccess.setTaskCompleted(t.getId());
try {
Thread.sleep(getCompletionTime(t));
workingThread.stop();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//generate a random completion time for each task considering the minimum time and the maximum time allocated for a task
private int getCompletionTime(Task t){
Operation op = OperationDatabaseAccess.getOperationByTaskId(t.getId());
int min = op.getMinTime();
int max = op.getMaxTime();
Random rand = new Random();
return rand.nextInt(max - min + 1) - min;
}
Main application class
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import ro.csm.database_access.OfficeDatabaseAccess;
import ro.csm.database_access.OperationDatabaseAccess;
import ro.csm.database_access.TaskDatabaseAccess;
import ro.csm.models.Office;
import ro.csm.models.OfficeWorker;
import ro.csm.models.Operation;
import ro.csm.models.Task;
public class MainApplication {
//list of available offices
static List<Office> offices = OfficeDatabaseAccess.getOffices();
//list of office workers
static List<OfficeWorker> workers = createOfficeWorker(offices);
public static void main(String[] args) {
while(true){
//get the list of pending tasks
List<Task> tasks = TaskDatabaseAccess.getNotStarted();
for(Task t : tasks){
//for each task set the status in progress
TaskDatabaseAccess.setTaskInProgres(t.getId());
//assign the task to a office worker
assignTaskToOfficeWorker(t);
}
}
}
public static void assignTaskToOfficeWorker(Task t){
//create a list of possible workers
List<OfficeWorker> possibleWorkers = new ArrayList<OfficeWorker>();
//get the supported operations for each office worker
for(OfficeWorker ow : getWorkers()){
List<Operation> supportedOperations = OperationDatabaseAccess.getAllSupportedOperations(ow.getOffice().getId());
//check if the given task is in the list of supported operations
for(Operation op : supportedOperations){
if(op.getId() == t.getIdOperation()){
//if the task can be executed by this office worker then add this worker in the list of possible workers
possibleWorkers.add(ow);
}
}
}
//assign the task to the worker that has the smallest queue tasks
OfficeWorker toBeAssigned = findOWMinTaskInQueue(possibleWorkers);
toBeAssigned.addTask(t);
//update the task with the office number
TaskDatabaseAccess.updateTaskWithOfficeInformation(toBeAssigned.getOffice().getId(), t.getId());
//complete the task
toBeAssigned.work();
}
public static List<OfficeWorker> createOfficeWorker(List<Office> offices){
List<OfficeWorker> workers = new ArrayList<OfficeWorker>();
for(Office office : offices){
OfficeWorker ow = new OfficeWorker(office);
workers.add(ow);
}
return workers;
}
public static OfficeWorker findOWMinTaskInQueue(List<OfficeWorker> workers){
Collections.sort(workers, new Comparator<OfficeWorker>() {
public int compare(final OfficeWorker ow1, final OfficeWorker ow2){
return ((Integer) ow1.getTasks().size()).compareTo((Integer)ow2.getTasks().size());
}
});
return workers.get(0);
}
The problem is that you not doing any multitasking. Your OfficeWorker is just class that creates empty thread and starts it.
Here is basic example:
new Thread(new Runnable() {
public void run() {
//do something
}
}).start();
Please read this docs http://docs.oracle.com/javase/tutorial/essential/concurrency/simple.html to understand more.
I wrote a producer/consumer based program using Java's BlockingQueue. I'm trying to find a way to stop the consumer if all producers are done. There are multiple producers, but only one consumer.
I found several solutions for the "one producer, many consumers" scenario, e.g. using a "done paket / poison pill" (see this discussion), but my scenario is just the opposite.
Are there any best practice solutions?
The best-practice system is to use a count-down latch. Whether this works for you is more interesting.....
Perhaps each producer should register and deregister with the consumer, and when all producers are deregistered (and the queue is empty) then the consumer can terminate too.
Presumably your producers are working in different threads in the same VM, and that they exit when done. I would make another thread that calls join() on all the producers in a loop, and when it exist that loop (because all the producer threads have ended) it then notifies the consumer that it's time to exit. This has to run in another thread because the join() calls will block. Incidentally, rolfl's suggestion of using a count down latch would have the problem, if I understand it correctly.
Alternately, if the producers are Callables, then the consumer can call isDone() and isCanceled() on their Futures in the loop, which won't bock, so it can be used right in the consumer thread.
You could use something like the following, i use registerProducer() and unregisterProducer() for keeping track of the producers. Another possible solution could make use of WeakReferences.
It's worth to mention that this solution will not consume the events that have already been queued when the consumer is shut down, so some events may be lost when shutting down.
You would have to drain the queue if the consumer gets interrupt and then process them.
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
public class TestConsumerShutdown {
private static interface SomeEvent {
String getName();
}
private static class Consumer implements Runnable {
private final BlockingQueue<SomeEvent> queue = new ArrayBlockingQueue<>(10);
private final ExecutorService consumerExecutor = Executors.newSingleThreadExecutor();
private final AtomicBoolean isRunning = new AtomicBoolean();
private final AtomicInteger numberProducers = new AtomicInteger(0);
public void startConsumer() {
consumerExecutor.execute(this);
}
public void stopConsumer() {
consumerExecutor.shutdownNow();
try {
consumerExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public void registerProducer() {
numberProducers.incrementAndGet();
}
public void unregisterProducer() {
if (numberProducers.decrementAndGet() < 1) {
stopConsumer();
}
}
public void produceEvent(SomeEvent event) throws InterruptedException {
queue.put(event);
}
#Override
public void run() {
if (isRunning.compareAndSet(false, true)) {
try {
while (!Thread.currentThread().isInterrupted()) {
SomeEvent event = queue.take();
System.out.println(event.getName());
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
System.out.println("Consumer stopped.");
isRunning.set(false);
}
}
}
}
public static void main(String[] args) {
final Consumer consumer = new Consumer();
consumer.startConsumer();
final Runnable producerRunnable = new Runnable() {
#Override
public void run() {
final String name = Thread.currentThread().getName();
consumer.registerProducer();
try {
for (int i = 0; i < 10; i++) {
consumer.produceEvent(new SomeEvent() {
#Override
public String getName() {
return name;
}
});
}
System.out.println("Produver " + name + " stopped.");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
consumer.unregisterProducer();
}
}
};
List<Thread> producers = new ArrayList<>();
producers.add(new Thread(producerRunnable, "producer-1"));
producers.add(new Thread(producerRunnable, "producer-2"));
producers.add(new Thread(producerRunnable, "producer-3"));
for (Thread t : producers) {
t.start();
}
}
}