序列中的线程

时间:2016-05-16 08:16:06

标签: java multithreading thread-synchronization

我正在尝试学习如何编写一个程序,在线程的帮助下按顺序执行一组给定的任务。例如,编写一个程序,其中有3个不同的线程打印1111 ...,22222 ...,333333 ......,这样输出将是1,2,3,1,2,3,1,2,3 ......?或者例如2个线程一个是打印奇数和其他偶数,但输出应按顺序打印 - 即一个偶数然后是奇数。

我想学习如何编写类似的程序,其中不同的线程同时打印不同的东西,输出应该按顺序打印。

编写这些程序的基本概念是什么?我们可以使用ThreadPools / Executors吗?对于例如我们可以使用

ExecutorService exectorService = Executors.newFixedThreadPool(3);

我们可以使用Future,FurtureTask,Callable,执行,提交......?我知道这些概念,但我无法连接点以解决上述情况。

请指导我如何使用多线程/并发编写这些程序。

我使用wait()/ notifyAll()编写了一个程序。以下是该计划。我没有执行消费者,因为我在最后打印整个序列。此外,我将队列的容量限制为15.所以我基本上打印奇数/偶数范围直到15。

public class ProduceEven implements Runnable {

private final List<Integer> taskQueue; 
private final int           MAX_CAPACITY;

public ProduceEven (List<Integer> sharedQueue, int size) {
    this.taskQueue = sharedQueue; 
    this.MAX_CAPACITY = size; 
}


@Override
public void run() {
    // TODO Auto-generated method stub
    int counter = 0; 

    while (counter < 15) {

        try {
            produce(counter++);
        } catch (InterruptedException e) {
            e.getMessage();
        }
    }
}

private void produce (int i) throws InterruptedException {

    synchronized (taskQueue) {

        while (taskQueue.size() == MAX_CAPACITY) {

            System.out.println("Queue is full : "+Thread.currentThread().getName()+" is waiting , size: "+ taskQueue.size());
            taskQueue.wait();
        }

        Thread.sleep(1000);
        if(i%2==0) {
            taskQueue.add(i);
        }
            taskQueue.notifyAll();
        }
    }   

}

public class ProduceOdd implements Runnable {

private final List<Integer> taskQueue; 
private final int           MAX_CAPACITY;

public ProduceOdd (List<Integer> sharedQueue, int size) {
    this.taskQueue = sharedQueue; 
    this.MAX_CAPACITY = size; 
}


@Override
public void run() {

    int counter = 0; 

    while (counter < 15) {

        try {
            produce(counter++);
        } catch (InterruptedException e) {
            e.getMessage();
        }
    }
}

private void produce (int i) throws InterruptedException {

    synchronized (taskQueue) {

        while (taskQueue.size() == MAX_CAPACITY) {

            System.out.println("Queue is full : "+Thread.currentThread().getName()+" is waiting , size: "+ taskQueue.size());
            taskQueue.wait();
        }

        Thread.sleep(1000);
        if(i%2==1) {
            taskQueue.add(i);           
        }           
        taskQueue.notify();
    }
}   

}

public class OddEvenExampleWithWaitAndNotify {

public static void main(String[] args) {

        List<Integer> taskQueue = new ArrayList<Integer>(); 
        int MAX_CAPACITY = 15;
        Thread tProducerEven = new Thread(new ProduceEven(taskQueue, MAX_CAPACITY), "Producer Even");
        Thread tProducerOdd = new Thread(new ProduceOdd(taskQueue, MAX_CAPACITY), "Producer Odd");
        tProducerEven.start();
        tProducerOdd.start();

        try {
            tProducerEven.join();
            tProducerOdd.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        ListIterator listIterator = taskQueue.listIterator();

        System.out.println("Elements Are:: ");
        while(listIterator.hasNext()) {
            System.out.print(listIterator.next()+"  ");
        }
}

}

我得到的输出是:Elements Are :: 02134657911810131214

输出都混乱了。为什么不按顺序排列。 01234567891011121314我错过了什么。我现在正试图使用​​信号量来制作程序。另外我们如何使用显式锁来创建这个程序?

6 个答案:

答案 0 :(得分:0)

是的,您可以使用ExecutorService作为运行线程的起点。您也可以手动创建和启动线程,这没什么区别。

重要的是,如果您不同步它们,您的线程将并行运行(即,它们必须彼此等待)。要同步,您可以,例如使用Semaphores或其他线程通信机制。

你在评论中写道,你写了一个生产者/消费者计划。这有点像是一回事。每次1-Thread产生1时,2-Thread必须知道它现在可以产生2.当它完成时,它必须让3-Thread知道它必须产生3.基本概念是相同的。只是线程有生产者角色和消费者角色。

答案 1 :(得分:0)

嗨,这是一个打印奇数和偶数使用两个线程并在它们之间使用线程同步的示例程序。 我们也使用了Executor框架,这不是强制性的,你也可以使用新的Thread()创建线程。对于快速原型,我使用了system.exit(),可以用线程的优雅关闭代替,例如,中断和所有。

package com.ones.twos.threes;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class OnesTwos {
    public static void main(String[] args) {
        BlockingQueue<Integer> bq1 = new ArrayBlockingQueue<Integer>(100);
        BlockingQueue<Integer> bq2 = new ArrayBlockingQueue<Integer>(100);

        ExecutorService executorService = Executors.newFixedThreadPool(2);
        try {
            bq1.put(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        executorService.submit(new OddEven(bq1, bq2));
        executorService.submit(new OddEven(bq2, bq1));
        executorService.shutdown();
    }

    public static class OddEven implements Runnable {

        BlockingQueue<Integer> bq1;
        BlockingQueue<Integer> bq2;

        public OddEven(BlockingQueue<Integer> bq1, BlockingQueue<Integer> bq2) {
            this.bq1 = bq1;
            this.bq2 = bq2;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    int take = bq1.take();
                    System.out.println(take);
                    bq2.offer(take + 1);
                    if (take > 20)
                        System.exit(0);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

答案 2 :(得分:0)

Mycode也类似于Anirban的,除了我没有使用执行器框架,

Yandex

答案 3 :(得分:0)

通过建立3 (ExecutorService exectorService = Executors.newFixedThreadPool(3);的线程池,您必须将执行程序容量限制为3,其他传入线程将被保留。如果你想在paralel中运行它们,你可以立即提交它们。如果你想等待对方并希望找到结果,我建议你使用Callable。我个人非常喜欢Callable因为在提交之后你可以调用Futureget方法,等待执行线程的返回值,然后继续下一个。从API中您可以看到:

  

/ **        *提交一个返回值的任务以执行并返回一个        * Future表示任务的待定结果。该        *未来的{@code get}方法将返回任务的结果        *圆满完成。        *        *

       *如果您想立即阻止等待        *对于任务,您可以使用表单的构造        * {@code result = exec.submit(aCallable).get();}

这是一个非常好的例子here。如果您选择Callable替代方案,那么您不需要线程池。只是一个正常的执行者是好的。记得最后关闭执行程序。

答案 4 :(得分:0)

class MyNumber {
    int i = 1;
}

class Task implements Runnable {
    MyNumber myNumber;
    int id;

    Task(int id, MyNumber myNumber) {
        this.id = id;
        this.myNumber = myNumber;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (myNumber) {
                while (myNumber.i != id) {
                    try {
                        myNumber.wait(); //Wait until Thread with correct next number
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(id);
                if (myNumber.i == 1) {
                    myNumber.i = 2;
                } else if (myNumber.i == 2) {
                    myNumber.i = 3;
                } else {
                    myNumber.i = 1;
                }
                myNumber.notifyAll();
            }
        }
    }
}

主要方法:

  MyNumber myNumber = new MyNumber();
  new Thread(new Task(1, myNumber)).start();
  new Thread(new Task(2, myNumber)).start();
  new Thread(new Task(3, myNumber)).start();

答案 5 :(得分:0)

您好,我们使用2个线程打印偶数,另一个打印奇数。 两者都是分开的,彼此没有关系。 但我们必须在它们之间建立同步机制。我们还需要一种机制让球滚动,即开始一个线程打印。

每个线程都在等待条件,在完成任务之后,它让其他线程工作并使自己处于等待状态。

好快乐的路径工作得很好,但是当线程没有处于等待状态时我们需要特别小心,而主要火灾中的信号(),在这种情况下甚至线程将永远不能唤醒并且程序挂起。

因此,为了确保主线程成功地将信号()发送到偶数线程,甚至线程也不会错过我们使用Phaser(带有聚会)并在main中检查while循环中的线程状态。

代码如下。

package com.ones.twos.threes;

import java.util.concurrent.Phaser;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class OnesTwosTrial2 {

    public static void main(String[] args) {

        Lock lk = new ReentrantLock();
        Phaser ph = new Phaser(3); // to let main start the even thread
        Condition even = lk.newCondition();
        Condition odd = lk.newCondition();

        OnesTwosTrial2 onestwostrial2 = new OnesTwosTrial2();
        Thread ev = onestwostrial2.new Evens(lk, even, odd, ph);
        Thread od = onestwostrial2.new Odds(lk, even, odd, ph);

        ev.start();
        od.start();
        System.out.println("in main before arrive");
        ph.arriveAndAwaitAdvance();
        System.out.println("in main after arrive");
        // we have to make sure odd and even thread is
        // started and waiting on respective condition.
        // So we used Phaser with 3, because we are having here
        // 3 parties (threads)
        // main, odd,even. We will signal only when all the
        // threads have started.
        // and waiting on conditions.
        while (!Thread.State.WAITING.equals(ev.getState())) {
            System.out.println("waiting");
        }
        lk.lock();
        even.signal();
        lk.unlock();

    }

    class Evens extends Thread {
        Lock lk;
        Condition even;
        Condition odd;
        Phaser ph;

        public Evens(Lock lk, Condition even, Condition odd, Phaser ph) {
            this.lk = lk;
            this.even = even;
            this.odd = odd;
            this.ph = ph;
        }

        @Override
        public void run() {
            System.out.println("even ph");
            int cnt = 0;
            while (cnt < 20) {
                try {
                    lk.lock();
                    ph.arrive();
                    even.await();
                    System.out.println(cnt);
                    cnt += 2;
                    odd.signal();
                    lk.unlock();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    class Odds extends Thread {
        Lock lk;
        Condition even;
        Condition odd;
        Phaser ph;

        public Odds(Lock lk, Condition even, Condition odd, Phaser ph) {
            this.lk = lk;
            this.even = even;
            this.odd = odd;
            this.ph = ph;
        }

        @Override
        public void run() {
            System.out.println("odd ph");
            int cnt = 1;
            while (cnt < 20) {
                try {
                    lk.lock();
                    ph.arrive();
                    odd.await();
                    System.out.println(cnt);
                    cnt += 2;
                    even.signal();
                    lk.unlock();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}