订购线程按照创建/启动的顺序运行

时间:2010-09-18 12:55:18

标签: java multithreading synchronization

我如何按照实例化的顺序订购线程。如何使下面的程序按顺序打印数字1 ... 10。

public class ThreadOrdering {
    public static void main(String[] args) {

        class MyRunnable implements Runnable{
            private final int threadnumber;

            MyRunnable(int threadnumber){
                this.threadnumber = threadnumber;
            }

            public void run() {
                System.out.println(threadnumber);
            }
        }

        for(int i=1; i<=10; i++){
            new Thread(new MyRunnable(i)).start();
        }
    }
}

10 个答案:

答案 0 :(得分:12)

听起来你想要ExecutorService.invokeAll,它会以固定顺序返回工作线程的结果,即使它们可能按任意顺序排列:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ThreadOrdering {

    static int NUM_THREADS = 10;

    public static void main(String[] args) {
        ExecutorService exec = Executors.newFixedThreadPool(NUM_THREADS);
        class MyCallable implements Callable<Integer> {
            private final int threadnumber;

            MyCallable(int threadnumber){
                this.threadnumber = threadnumber;
            }

            public Integer call() {
                System.out.println("Running thread #" + threadnumber);
                return threadnumber;
            }
        }

        List<Callable<Integer>> callables =
            new ArrayList<Callable<Integer>>();
        for(int i=1; i<=NUM_THREADS; i++) {
            callables.add(new MyCallable(i));
        }
        try {
            List<Future<Integer>> results =
                exec.invokeAll(callables);
            for(Future<Integer> result: results) {
                System.out.println("Got result of thread #" + result.get());
            }
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        } catch (ExecutionException ex) {
            ex.printStackTrace();
        } finally {
            exec.shutdownNow();
        }
    }

}

答案 1 :(得分:7)

“我实际上有一些我希望并行执行的部分,然后一旦生成结果,我想按特定顺序合并结果。”谢谢,这澄清了你的要求。

您可以一次性运行它们,但重要的是在线程完成计算时按顺序获取结果。要么Thread#join()按照您希望获得结果的顺序排列,要么Thread#join()全部,然后遍历它们以获得结果。

// Joins the threads back to the main thread in the order we want their results.
public class ThreadOrdering {
    private class MyWorker extends Thread {
        final int input;
        int result;
        MyWorker(final int input) {
            this.input = input;
        }
        @Override
        public void run() {
            this.result = input; // Or some other computation.
        }
        int getResult() { return result; }
    }

    public static void main(String[] args) throws InterruptedException {
        MyWorker[] workers = new MyWorker[10];
        for(int i=1; i<=10; i++) {
            workers[i] = new MyWorker(i);
            workers[i].start();
        }

        // Assume it may take a while to do the real computation in the threads.

        for (MyWorker worker : workers) {
            // This can throw InterruptedException, but we're just passing that.
            worker.join();
            System.out.println(worker.getResult());
        }
    }
}

答案 2 :(得分:4)

简单地说,线程的安排是不确定的。

http://www.janeg.ca/scjp/threads/scheduling.html 死域 - 请勿点击

WaybackMachine version of the above page

更长的答案是,如果要执行此操作,则需要手动等待每个线程完成其工作,然后再允许其他线程运行。请注意,以这种方式,所有线程仍将交错,但在您批准之前它们将无法完成任何工作。看一下同步保留字。

答案 3 :(得分:3)

你可以链接它们 - 也就是说,让第一个开始第二个,第二个开始第三个,等等。它们不会真正同时运行,除了每个启动时有点重叠。

public class ThreadOrdering
{
    public static void main(String[] args)
    {
        MyRunnable[] threads = new MyRunnable[10];//index 0 represents thread 1;
        for(int i=1; i<=10; i++)
            threads[i] = new MyRunnable(i, threads); 
        new Thread(threads[0].start);  
    }
}

public class MyRunnable extends Runnable
{
    int threadNumber;
    MyRunnable[] threads;

    public MyRunnable(int threadNumber, MyRunnable[] threads)
    {
        this.threadnumber = threadnumber;
        this.threads = threads;
    }

    public void run()
    {
        System.out.println(threadnumber);
        if(threadnumber!=10)
            new Thread(threadnumber).start();
    }
}

答案 4 :(得分:2)

这是一种方法,无需拥有等待每个结果的主线程。相反,让工作线程共享一个对结果进行排序的对象。

import java.util.*;

public class OrderThreads {
    public static void main(String... args) {
        Results results = new Results();
        new Thread(new Task(0, "red", results)).start();
        new Thread(new Task(1, "orange", results)).start();
        new Thread(new Task(2, "yellow", results)).start();
        new Thread(new Task(3, "green", results)).start();
        new Thread(new Task(4, "blue", results)).start();
        new Thread(new Task(5, "indigo", results)).start();
        new Thread(new Task(6, "violet", results)).start();
    }
}

class Results {
    private List<String> results = new ArrayList<String>();
    private int i = 0;

    public synchronized void submit(int order, String result) {
        while (results.size() <= order) results.add(null);
        results.set(order, result);
        while ((i < results.size()) && (results.get(i) != null)) {
            System.out.println("result delivered: " + i + " " + results.get(i));
            ++i;
        }
    }
}


class Task implements Runnable {
    private final int order;
    private final String result;
    private final Results results;

    public Task(int order, String result, Results results) {
        this.order = order;
        this.result = result;
        this.results = results;
    }

    public void run() {
        try {
            Thread.sleep(Math.abs(result.hashCode() % 1000)); // simulate a long-running computation
        }
        catch (InterruptedException e) {} // you'd want to think about what to do if interrupted
        System.out.println("task finished: " + order + " " + result);
        results.submit(order, result);
    }
}

答案 5 :(得分:1)

如果你需要那种细粒度的控件,你不应该使用线程,而是考虑使用一个合适的Executor with Callables或Runnables。请参阅Executors类以获得广泛的选择。

答案 6 :(得分:1)

一个简单的解决方案是使用数组A锁(每个线程一个锁)。当线程i开始执行时,它会获取其关联的锁A[i]。当它准备好合并其结果时,它会释放其锁A[i]并等待锁A[0], A[1], ..., A[i - 1]被释放;然后它合并结果。

(在此上下文中,线程i表示i - 已启动的线程)

我不知道在Java中使用哪些类,但它必须易于实现。您可以开始阅读this

如果您有更多问题,请随时提出。

答案 7 :(得分:1)

public static void main(String[] args) throws InterruptedException{
    MyRunnable r = new MyRunnable();
    Thread t1 = new Thread(r,"A");
    Thread t2 = new Thread(r,"B");
    Thread t3 = new Thread(r,"C");

    t1.start();
    Thread.sleep(1000);

    t2.start();
    Thread.sleep(1000);
    t3.start();
}

答案 8 :(得分:0)

使用信号量可以非常容易地实现对线程执行顺序的控制。附带的代码基于Schildt的Java书籍(完整的参考文献......)中提出的想法。     //基于以下提出的想法:     // Schildt H。:Java.The.Complete.Reference.9th.Edition。

void Logger::init(Logger::severity_level level)
{
    boost::log::add_common_attributes();
    boost::shared_ptr<boost::log::core> core = boost::log::core::get();
    core->set_exception_handler(boost::log::make_exception_suppressor()); //disable exception. in case file write permission problem

    core->get()->add_global_attribute("Scope", boost::log::attributes::named_scope()); 
    core->get()->add_global_attribute("File", boost::log::attributes::mutable_constant<std::string>(""));
    core->get()->add_global_attribute("Line", boost::log::attributes::mutable_constant<int>(0));
    //core->set_filter(boost::log::expressions::attr<Logger::severity_level>("Severity") >= level);
#ifdef _DEBUG
    {
        typedef boost::log::sinks::synchronous_sink<boost::log::sinks::text_ostream_backend> cout_sink;
        boost::shared_ptr<cout_sink> sink = boost::make_shared<cout_sink>();
        boost::shared_ptr<std::ostream> stream(&std::clog, boost::null_deleter());
        sink->locked_backend()->add_stream(stream);
        sink->set_filter(channel == main_channel && boost::log::expressions::attr<Logger::severity_level>("Severity") >= level);
        core->add_sink(sink);
    }
    boost::log::add_file_log(
        boost::log::keywords::filter = channel == main_channel && boost::log::expressions::attr<Logger::severity_level>("Severity") >= level,
        //boost::log::keywords::filter = channel == main_channel,
        boost::log::keywords::file_name = "./log/%Y%m%d.log",
        boost::log::keywords::auto_flush = true,
        boost::log::keywords::open_mode = (std::ios::out | std::ios::app),
        boost::log::keywords::time_based_rotation = boost::log::sinks::file::rotation_at_time_point(0, 0, 0), 
        //boost::log::keywords::format = "[%TimeStamp%] [%__LINE__%] (%LineID%) {%ThreadID%}: %Message%"
        boost::log::keywords::format = (
            boost::log::expressions::stream
            << "[" << boost::log::expressions::format_date_time<boost::posix_time::ptime>("TimeStamp", "%Y-%m-%d_%H:%M:%S.%f") << "] "
            << "(" << boost::log::expressions::attr<unsigned int>("LineID") << ") "
            << "(" << boost::log::expressions::attr<boost::log::aux::thread::id>("ThreadID") << ") "
            << '[' << boost::log::expressions::attr<std::string>("File") << ":"
            << boost::log::expressions::attr<int>("Line") << "] "
            << boost::log::expressions::smessage
            )
        );
}

答案 9 :(得分:0)

可以不使用synchronized关键字并在 volatile 关键字的帮助下完成此操作。以下是代码。

package threadOrderingVolatile;

public class Solution {
    static volatile int counter = 0;
    static int print = 1;
    static char c = 'A';

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Thread[] ths = new Thread[4];
        for (int i = 0; i < ths.length; i++) {
            ths[i] = new Thread(new MyRunnable(i, ths.length));
            ths[i].start();
        }
    }

    static class MyRunnable implements Runnable {
        final int thID;
        final int total;

        public MyRunnable(int id, int total) {
            thID = id;
            this.total = total;
        }

        @Override
        public void run() {
            while(true) {
                if (thID == (counter%total)) {
                    System.out.println("thread " + thID + " prints " + c);
                    if(c=='Z'){
                        c='A';
                    }else{
                        c=(char)((int)c+1);
                    }
                    System.out.println("thread " + thID + " prints " + print++);
                    counter++;                  
                } else {
                    try {
                        Thread.sleep(30);
                    } catch (InterruptedException e) {
                        // log it
                    }
                }
            }
        }

    }

}

以下是github链接,它有一个自述文件,详细解释了它是如何发生的。 https://github.com/sankar4git/volatile_thread_ordering