I've received a task about java.util.concurrent package of java. I've made it almost totally, but there is some bug or mistake. When the queue is empty and operator waits for 5 seconds method poll should retrieve null and pass it to the operator and operator goes home. But it doesn't happen. It retrieves null but doesn't pass it to the operator. Sorry for my English.)
public class Client extends Thread {
public CountDownLatch latch=new CountDownLatch(1);
private boolean waiting;
private final Random random=new Random();
public boolean isWaiting() {
return waiting;
}
public void setWaiting(boolean isWaiting) {
this.waiting = isWaiting;
}
private static final Logger LOGGER;
static {
LOGGER = Logger.getLogger(Client.class);
new DOMConfigurator().doConfigure("log4j.xml",
LogManager.getLoggerRepository());
LOGGER.setLevel(Level.INFO);
}
private int limitTime=new Random().nextInt(5000);
public void run(){
ClientQueue.enqueueClient(this);
while(waiting){
if (random.nextBoolean()){
try {
latch.await(5, TimeUnit.SECONDS);
if (!waiting) return;
ClientQueue.removeFromQueue(this);
reportTiredToWait();
sleep(random.nextInt(1000)+500);
ClientQueue.enqueueClient(this);
reportDecidedToCallAgain();
} catch (InterruptedException e) {
LOGGER.info("Exception");
}
}
}
}
public Client(String name) {
super(name);
this.waiting=true;
}
private void reportTiredToWait(){
LOGGER.info("Client "+getName()+" was tired to wait and decided to hang up");
}
private void reportDecidedToCallAgain(){
LOGGER.info("Client "+getName()+" decided to call again");
}
#Override
public String toString() {
return "Client "+getName();
}
}
public class ClientQueue {
private static final Logger LOGGER;
static {
LOGGER = Logger.getLogger(ClientQueue.class);
new DOMConfigurator().doConfigure("log4j.xml",
LogManager.getLoggerRepository());
LOGGER.setLevel(Level.INFO);
}
private static ClientQueue instance;
private BlockingQueue<Client> queue;
public static void printQueue(){
System.out.println("LIST OF CLIENTS:");
for (Client client :ClientQueue.getInstance().queue){
System.out.println("CLIENT "+client.getName());
}
System.out.println("END OF LIST OF CLIENTS:");
}
private static ClientQueue getInstance()
{
if ( instance == null )
{
instance = new ClientQueue();
}
return instance;
}
private ClientQueue()
{
this.queue = new LinkedBlockingQueue<Client>();
}
public static void enqueueClient(Client client){
getInstance().queue.add(client);
reportClientEnqueued(client.getName());
}
public static void removeFromQueue(Client client){
ClientQueue.getInstance().queue.remove(client);
reportClientDeletedFromQueue(client.getName());
}
public static Client pollFirst(long time, TimeUnit timeUnit) throws InterruptedException{
Client client=null;
client = getInstance().queue.poll(time, timeUnit);
if (client!=null){
reportClientRetrievedFromQueue(client.getName());
}
return client;
}
private static void reportClientEnqueued(String name){
LOGGER.info("Client "+name+" was put on the waiting list");
}
private static void reportClientDeletedFromQueue(String name){
LOGGER.info("Client " +name+" was deleted from waiting list");
}
private static void reportClientRetrievedFromQueue(String name){
LOGGER.info("Client " +name+" was retrieved from waiting list");
}
}
public class Operator extends Thread{
private static final Logger LOGGER;
static {
LOGGER = Logger.getLogger(Operator.class);
new DOMConfigurator().doConfigure("log4j.xml",
LogManager.getLoggerRepository());
LOGGER.setLevel(Level.INFO);
}
private boolean running;
public Operator(String name){
super(name);
running= true;
}
#Override
public void run() {
while (running){
Client client=null;
try {
client = ClientQueue.pollFirst(5, TimeUnit.SECONDS);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
if (client!=null){
String clientName=client.getName();
reportOperatorReceivedCall(clientName);
try {
client.setWaiting(false);
client.latch.countDown();
sleep(10000);
reportOperatorFinishedConversation(clientName);
} catch (InterruptedException e) {
LOGGER.error(e);
}
} else{
reportOperatorFinishedHisWork();
running=false;
}
}
}
private void reportOperatorReceivedCall(String name){
LOGGER.info("Operator "+getName()+" received call from Client "+name);
}
private void reportOperatorFinishedConversation(String name){
LOGGER.info("Operator "+getName()+" finished conversation with Client "+name);
}
private void reportOperatorFinishedHisWork(){
LOGGER.info("Operator "+getName()+" finished his work for today, he is too tired and decided to go home.");
}
}
public class Main {
public static void main(String[] args) {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(10);
LinkedList<Client> clientList = new LinkedList<Client>();
clientList.add(new Client("Vasya"));
clientList.add(new Client("Tanya"));
clientList.add(new Client("Petya"));
clientList.add(new Client("Kolya"));
clientList.add(new Client("Elena"));
clientList.add(new Client("Anna"));
for(int i = 0; i < clientList.size(); i++) {
executor.schedule(clientList.get(i), i, TimeUnit.SECONDS);
}
LinkedList<Operator> operatorList = new LinkedList<Operator>();
operatorList.add(new Operator("Bob"));
operatorList.add(new Operator("Sandra"));
operatorList.add(new Operator("John"));
for(int i = 0; i < operatorList.size(); i++) {
executor.schedule(operatorList.get(i), 500, TimeUnit.MILLISECONDS);
}
}
}
You have an extra semicolon in ClientQueue.pollFirst. Here it is corrected:
public static Client pollFirst(long time, TimeUnit timeUnit) throws InterruptedException{
Client client=null;
client = getInstance().queue.poll(time, timeUnit);
if (client!=null) { // removed semicolon from this line
reportClientRetrievedFromQueue(client.getName());
}
return client;
}
Related
I run the code in IntelliJ IDEA and it would not stop. I use exec.shutdownNow() instead and it stops successfully. So, I think is the problem of generator, but I can't figure out what problem it is.
The main method:
public static void main(String[] args) {
PrimeProducer generator = new PrimeProducer(new ArrayBlockingQueue<>(10));
ExecutorService exec = Executors.newFixedThreadPool(1);
exec.execute(generator);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
generator.cancel();
}
//generator.get();
exec.shutdown();
}
And the PrimeProducer class:
public class PrimeProducer extends Thread{
private final BlockingQueue<BigInteger> queue;
PrimeProducer(BlockingQueue<BigInteger> queue){
this.queue = queue;
}
#Override
public void run() {
try {
BigInteger p = BigInteger.ONE;
while(!Thread.currentThread().isInterrupted()){
queue.put(p = p.nextProbablePrime());
}
} catch (InterruptedException e) {
}
}
public void cancel(){interrupt();}
public void get(){
for(BigInteger i : queue){
System.out.println(i.toString());
}
}
}
The latest version that still can't work:
public class PrimeProducer implements Runnable {
private final BlockingQueue<BigInteger> queue;
PrimeProducer(BlockingQueue<BigInteger> queue){
this.queue = queue;
}
#Override
public void run() {
try {
BigInteger p = BigInteger.ONE;
while(!Thread.currentThread().isInterrupted()){
queue.put(p = p.nextProbablePrime());
}
} catch (InterruptedException e) {
}
}
public void cancel(){Thread.currentThread().interrupt();}
public synchronized void get(){
for(BigInteger i : queue){
System.out.println(i.toString());
}
}
}
You have PrimeProducer extend Thread, but you don't actually use it as a thread, you only use it as a Runnable, which is executed on a thread of the thread pool. The cancel has no effect, because it calls the interrupt() of a different thread than the one that is actually running. When you then call exec.shutdown(), the thread pool will interrupt its threads to signal them they should end, when then means PrimeProducer.run will stop.
Instead, use a volatile boolean to end your run (and implement Runnable instead of extending Thread):
public class PrimeProducer implements Runnable {
private final BlockingQueue<BigInteger> queue;
private volatile boolean cancelled;
PrimeProducer(BlockingQueue<BigInteger> queue) {
this.queue = queue;
}
#Override
public void run() {
try {
BigInteger p = BigInteger.ONE;
while (!cancelled) {
queue.put(p = p.nextProbablePrime());
}
} catch (InterruptedException e) {
}
}
public void cancel() {
cancelled = true;
}
public void get() {
for (BigInteger i : queue) {
System.out.println(i.toString());
}
}
}
I have written performance tests of single producer single consumer scenario against ArrayBlockingQueue and SubmissionPublisher. I am surprised that SubmissionPublisher performs far better than ArrayBlockingQueue (or my tests are wrong).
Both use array as the buffer, so why is the result so different?
Benchmark Mode Cnt Score Error Units
ArrayBlockingQueuePerfTest.runBenchmark ss 5 16.772 ± 0.538 s/op
SubmissionPublisherPerfTest.runBenchmark ss 5 8.138 ± 0.061 s/op
Test for ArrayBlockingQueue
#State(Scope.Benchmark)
public class ArrayBlockingQueuePerfTest {
private BlockingQueue<Event> queue;
private CountDownLatch latch;
private ExecutorService executor;
private TestRunnable testRunnable;
private Future<?> future;
public static void main(String[] args) throws Exception {
org.openjdk.jmh.Main.main(new String[] { ArrayBlockingQueuePerfTest.class.getCanonicalName() });
}
#Benchmark
#BenchmarkMode(Mode.SingleShotTime)
#Fork(value = 1)
#Warmup(iterations = 5)
#Measurement(iterations = 5)
public void runBenchmark() throws Exception {
for (int i=0; i<100_000_000; ++i) {
queue.put(new Event(i));
}
latch.await();
}
#Setup(Level.Trial)
public void setupAll() {
queue = new ArrayBlockingQueue<>(16384);
executor = Executors.newSingleThreadExecutor();
testRunnable = new TestRunnable(queue);
future = executor.submit(testRunnable);
}
#Setup(Level.Iteration)
public void setup() {
latch = new CountDownLatch(100_000_000);
testRunnable.reset(latch);
}
#TearDown(Level.Trial)
public void tearDown() {
future.cancel(true);
executor.shutdown();
}
static class TestRunnable implements Runnable {
private final BlockingQueue<Event> queue;
private CountDownLatch latch;
public TestRunnable(BlockingQueue<Event> queue) {
this.queue = queue;
}
public void reset(CountDownLatch latch) {
this.latch = latch;
}
#Override
public void run() {
while (true) {
try {
queue.take();
latch.countDown();
} catch (InterruptedException e) {
break;
}
}
}
}
}
Test for SubmissionPublisher
#State(Scope.Benchmark)
public class SubmissionPublisherPerfTest {
private ExecutorService executor;
private SubmissionPublisher<Event> publisher;
private TestSubscriber subscriber;
private CountDownLatch latch;
public static void main(String[] args) throws Exception {
org.openjdk.jmh.Main.main(new String[] { SubmissionPublisherPerfTest.class.getCanonicalName() });
}
#Benchmark
#BenchmarkMode(Mode.SingleShotTime)
#Fork(value = 1)
#Warmup(iterations = 5)
#Measurement(iterations = 5)
public void runBenchmark() throws Exception {
for (int i=0; i<100_000_000; ++i) {
publisher.submit(new Event(i));
}
latch.await();
}
#Setup(Level.Trial)
public void setupAll() {
executor = Executors.newFixedThreadPool(1);
publisher = new SubmissionPublisher<>(executor, 16384);
subscriber = new TestSubscriber();
publisher.subscribe(subscriber);
}
#Setup(Level.Iteration)
public void setup() {
latch = new CountDownLatch(100_000_000);
subscriber.reset(latch);
}
#TearDown(Level.Trial)
public void tearDownAll() {
executor.shutdown();
}
static class TestSubscriber implements Flow.Subscriber<Event> {
private CountDownLatch latch;
private Flow.Subscription subscription;
public void reset(CountDownLatch latch) {
this.latch = latch;
}
#Override
public void onSubscribe(Flow.Subscription subscription) {
(this.subscription = subscription).request(1);
}
#Override
public void onNext(Event item) {
this.subscription.request(1);
latch.countDown();
}
#Override
public void onError(Throwable throwable) {}
#Override
public void onComplete() {}
}
}
The Event object
public class Event {
private int value;
public Event() {
}
public Event(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}
```
package programs;
public class TestThreads {
public static void main(String[] args) {
ThreadOne t1 = new ThreadOne();
ThreadTwo t2 = new ThreadTwo();
Thread one = new Thread(t1);
Thread two = new Thread(t2);
one.start();
two.start();
}
}
class Accum{
private static Accum a = new Accum();
private int counter = 0;
private Accum() {
}
public static Accum getAccum() {
return a;
}
public void updateCounter(int add) {
counter +=add;
}
public int getCount() {
return counter;
}
}
class ThreadOne implements Runnable{
Accum a = Accum.getAccum();
#Override
public void run() {
for(int x=0;x<98;x++) {
a.updateCounter(1000);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("one "+ a.getCount());
}
}
class ThreadTwo implements Runnable{
Accum a = Accum.getAccum();
#Override
public void run() {
for(int x=0;x<99;x++) {
a.updateCounter(1);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("two "+ a.getCount());
}
}
```
The expected output should be as below
One 98098
Two 98099
But I am getting values same for both One and Two.
Is this expected or both should result in different values?
When it comes to thread priorities eventhough the jvm scheduler takes the role to choose the turn of which thread should execute first, what about the results of this program where two void run programs of for loop with 98 and 99 which should result in two different values else the same
This is because shared data is NOT synchronized.
To resolve this problem, use synchronized on methods those touch the shared data, to make Accum class Thread-Safe.
example:
class Accum{
private static Accum a = new Accum();
private int counter = 0;
private Accum() {
}
public static Accum getAccum() {
return a;
}
public synchronized void updateCounter(int add) {
counter +=add;
}
public synchronized int getCount() {
return counter;
}
}
Maybe make the methods and fields in the Accum class static, because then it is saved in a general place and not an instance...
Code:
class Accum{
private static Accum a = new Accum();
private static int counter = 0;
private Accum() {
}
public static Accum getAccum() {
return a;
}
public static void updateCounter(int add) {
counter +=add;
}
public static int getCount() {
return counter;
}
}
class ThreadTwo implements Runnable{
#Override
public void run() {
for(int x=0;x<99;x++) {
Accum.updateCounter(1);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("two "+ Accum.getCount());
}
}
class ThreadOne implements Runnable{
#Override
public void run() {
for(int x=0;x<98;x++) {
Accum.updateCounter(1000);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("one "+ Accum.getCount());
}
}
I am not very experienced in Java yet, so this might not work...
[EDIT] I tested it and I believe it works
DefaultRunners are producers
and OrderTaker is a consumer
They both share a OrderQueue.
Currently, I use the variable isDone to indicate if a game is finished.
Once each round is done, I want to make it repeat again and again.
However, in my current implementation it will only run once.
How could I solve it?
public class OrderQueue {
public synchronized void pushOrder(Order order) throws InterruptedException {
if (isDone) {
wait();
} else {
runnersQueue.addLast(order);
notifyAll();
}
}
public void pullOrder() {
try {
if (runnersQueue.size() == 0) {
} else if (isDone) {
wait();
} else {
handleOrder(runnersQueue.pop());
}
} catch (InterruptedException e) {
}
}
In my main class
while(true){
enterYesToStart();
DefaultRunners dfltRunner = new DefaultRunners(queue);
OrderTaker taker = new OrderTaker(queue);
taker.run();
System.out.println("This round is finished"); # never reach to this line
}
Here's the full source code for the example
https://gist.github.com/poc7667/d98e3bf5b3b470fcb51e00d9a0d80931
I've taken a look at your code snippets and the problem is fairly obvious.
The main thread runs the OrderTaker runnable. The main thread is stuck in an eternal loop as the while statement cannot complete unless it throws an exception. (Note that the same is true for your ThreadRunner runnable.)
This means that the main thread i still pulling orders while the race is already done.
The OrderTaker should exit it's while loop while once the race is done. I guess that there are multiple ways achieve this, but one way is use a shared variable.
I took your code and adapted it into a working example.
import java.util.*;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class RaceApp {
public static void main(String[] args) throws InterruptedException {
final RaceUpdateManager queue = new RaceUpdateManager();
for (int i = 0; i < 3; i++) {
queue.reset();
List<Thread> threads = Arrays.asList(
new Thread(new Runner("Tortoise", 0, 10, queue)),
new Thread(new Runner("Hare", 90, 100, queue))
);
for (Thread thread : threads) {
thread.start();
}
RaceUpdatesProcessor processor = new RaceUpdatesProcessor(queue);
processor.run();
System.out.println("Game finished");
}
}
private static class RaceUpdateManager {
private static final int TOTAL_DISTANCE = 300;
//thread-safe implementation for queue so no external syncrhonization is required when adding/removing updates
private final Deque<RaceUpdate> runnersQueue = new ConcurrentLinkedDeque<>();
//lock used to sync changes to runnersRecords and done variables
private final ReadWriteLock raceStatusLock = new ReentrantReadWriteLock();
private final Map<String, Integer> runnersRecords = new HashMap<>();
private volatile boolean raceDone = false;//volatile keyword guarantees visibility of changes to variables across threads
public boolean isRaceDone() {
return raceDone;
}
//updates can by added simultaneously (read lock)
public void register(RaceUpdate raceUpdate) throws InterruptedException {
Lock readLock = raceStatusLock.readLock();
readLock.lock();
try {
if (!raceDone) {
runnersQueue.addLast(raceUpdate);
}//ignore updates when the race is done
} finally {
readLock.unlock();
}
}
//but they need to be processed in order (exclusive write lock)
public void processOldestUpdate() {
Lock writeLock = raceStatusLock.writeLock();
writeLock.lock();
try {
RaceUpdate raceUpdate = runnersQueue.poll();
if (raceUpdate != null) {
handleUpdate(raceUpdate);
}
} finally {
writeLock.unlock();
}
}
private void handleUpdate(RaceUpdate raceUpdate) {
Integer distanceRun = runnersRecords.merge(
raceUpdate.runner, raceUpdate.distanceRunSinceLastUpdate, (total, increment) -> total + increment
);
System.out.printf("%s: %d\n", raceUpdate.runner, distanceRun);
if (distanceRun >= TOTAL_DISTANCE) {
raceDone = true;
System.out.printf("Winner %s\n", raceUpdate.runner);
}
}
public void reset() {
Lock writeLock = raceStatusLock.writeLock();
writeLock.lock();
try {
runnersQueue.clear();
runnersRecords.clear();
raceDone = false;
} finally {
writeLock.unlock();
}
}
}
public static class Runner implements Runnable {
private final String name;
private final int rest;
private final int speed;
private final RaceUpdateManager queue;
private final Random rand = new Random();
public Runner(String name, int rest, int speed, RaceUpdateManager queue) {
this.name = name;
this.rest = rest;
this.speed = speed;
this.queue = queue;
}
#Override
public void run() {
while (!queue.isRaceDone()) {
try {
if (!takeRest()) {
queue.register(new RaceUpdate(this.name, this.speed));
}
Thread.sleep(100);
} catch (InterruptedException e) {
//signal that thread was interrupted and exit method
Thread.currentThread().interrupt();
return;
}
}
}
private boolean takeRest() {
return rand.nextInt(100) < rest;
}
}
public static class RaceUpdatesProcessor implements Runnable {
private final RaceUpdateManager queue;
public RaceUpdatesProcessor(RaceUpdateManager queue) {
this.queue = queue;
}
#Override
public void run() {
while (!queue.isRaceDone()) {
try {
queue.processOldestUpdate();
Thread.sleep(50);
} catch (InterruptedException e) {
//signal that thread was interrupted and exit method
Thread.currentThread().interrupt();
return;
}
}
}
}
public static class RaceUpdate {
public final String runner;
public final int distanceRunSinceLastUpdate;
public RaceUpdate(String runner, int distanceRunSinceLastUpdate) {
this.runner = runner;
this.distanceRunSinceLastUpdate = distanceRunSinceLastUpdate;
}
}
}
I am having a trouble to share a resource with three threads...
public class Subject{
int i;
boolean valueSet1 = false;
boolean valueSet2 = true;
boolean valueSet3 = true;
void put(int i){
while(valueSet1){
try{
wait();
}catch(InterruptedException e){
System.out.println("Producer thread interrupted");
}
}
System.out.println("Producer thread wakesup");
valueSet1=true;
valueSet2=false;
this.i=i;
System.out.println("Put: "+i);
notify();
}
void makesquare(){
int a;
while(valueSet2){
try{
System.out.println("Convertor thread goin to sleep");
wait();
}catch(InterruptedException e){
System.out.println("Convertor thread interrupted");
}
}
System.out.println("Convertor thread wakesup");
valueSet2 = true;
valueSet3=false;
a = this.i;
this.i = a*a;
System.out.println("Made: "+i);
notify();
}
void get(){
while(valueSet3){
try{
System.out.println("Consumer thread goin to sleep");
wait();
}catch(InterruptedException e){
System.out.println("Consumer thread interrupted");
}
}
System.out.println("Consumer thread wakesup");
valueSet3 = true;
valueSet1 = false;
System.out.println("Got: "+i);
notify();
}
}
class Producer implements Runnable{
Subject q;
Thread t;
String msg;
Producer(Subject q, String msg){
this.q=q;
this.msg = msg;
t = new Thread(this, this.msg);
}
#Override
public void run(){
int i=2;
while(true){
synchronized(q){
q.put(i++);
}
}
}
}
class Consumer implements Runnable{
Subject q;
Thread t;
String msg;
Consumer(Subject q,String msg){
this.q = q;
this.msg = msg;
t = new Thread(this, this.msg);
}
#Override
public void run(){
while(true){
synchronized(q){
q.get();
}
}
}
}
class Convertor implements Runnable{
Subject q;
Thread t;
String msg;
Convertor(Subject q, String msg){
this.q=q;
this.msg = msg;
t = new Thread(this, this.msg);
}
#Override
public void run(){
while(true){
synchronized(q){
q.makesquare();
}
}
}
}
There are three threads in the program. One thread produces while another makes square of the produced quantity. While the last thread consumes the squared product.They all share the same object.
public class Thread3way {
public static void main(String[] args) {
Subject q = new Subject();
Producer P = new Producer(q, "producer");
Convertor Cv = new Convertor(q, "convertor");
Consumer Cs = new Consumer(q, "consumer");
P.t.start();
Cv.t.start();
Cs.t.start();
}
}
Your wait loop is not quite correct, you need to keep looping until your condition is true before you do any modifications. I'd do it like this (using a State enum to make things clearer):
public class Subject {
static enum State { EMPTY, WAITING_TO_SQUARE, WAITING_TO_GET };
State state;
int value;
public synchronized void put(int i) {
while (state != State.EMPTY) {
try {
wait();
}catch(InterruptedException e) {
System.out.println("Put interrupted");
}
}
value = i;
state = State.WAITING_TO_SQUARE;
}
}
As a commenter pointed out, you don't need to synchronize twice, either synchronized(q) around the Subject calls, or declare your methods synchronized. You don't need both.
No need to use Runnables explicitly, just make Producer, Convertor, and Consumer Threads directly. Then you don't need the t fields, and you can start the threads like P.start().
Sounds to me like you're doing some kind of pipeline with 3 stages. Why not enjoy the benefits of ExecutorService:
class ConvertorTask implements Runnable {
private int number;
private static ExecutorService consumer = Executors.newSingleThreadExecutor();
public ConvertorTask(int number) {
this.number = number;
}
public void run() {
consumer.submit(new ConsumerTask(number * number));
}
}
class ConsumerTask implements Runnable {
private int number;
public ConsumerTask(int number) {
this.number = number;
}
public void run() {
System.out.println(number);
}
}
class Producer implements Runnable {
private ExecutorService convertor = Executors.newSingleThreadExecutor();
public void run() {
int i = 0;
while(true) {
convertor.submit(new ConvertorTask(i++));
}
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new Producer());
t.start();
}
}