【问题标题】:Java multi threading - run threads run method only once in sequenceJava多线程-运行线程仅按顺序运行一次
【发布时间】:2018-10-01 11:09:45
【问题描述】:

在我的应用程序中,在程序的整个生命周期中,必须按顺序一个接一个地执行 n 个操作。我没有创建实现这些动作的方法并在 while(true) 循环中按顺序调用它们,而是决定为每个动作创建一个线程,并让它们执行一次 run 方法,然后等到所有其他线程完成同理,等待轮到,再重新执行,以此类推……

为了实现这个机制,我创建了一个名为 StatusHolder 的类,它有一个名为 threadTurn 的字段(表示应该执行哪个线程)、一个读取该值的方法以及一个用于更新它的方法。 (注意,这个类使用单例设计模式)

package Test;

public class StatusHolder
{
    private static volatile StatusHolder statusHolderInstance = null;
    public static volatile int threadTurn = 0;

    public synchronized static int getTurn()
    {
        return threadTurn;
    }

    public synchronized static void nextTurn()
    {
        System.out.print("Thread turn: " + threadTurn + " --> ");

        if (threadTurn == 1)
        {
            threadTurn = 0;
        }
        else
        {
            threadTurn++;
        }

        System.out.println(threadTurn);

        //Wake up all Threads waiting on this obj for the right turn to come
        synchronized (getStatusHolder())
        {
            getStatusHolder().notifyAll();
        }
    }

    public static synchronized StatusHolder getStatusHolder()
    {//Returns reference to this object

        if (statusHolderInstance == null)
        {
            statusHolderInstance = new StatusHolder();
        }

        return statusHolderInstance;
    }
}

然后,假设有两个线程,必须以上述方式执行,t1 和 t2。

T1 类如下所示:

package Test;

public class ThreadOne implements Runnable
{
    @Override
    public void run()
    {
        while (true)
        {
            ThreadUtils.waitForTurn(0);

            //Execute job, code's not here for simplicity
            System.out.println("T1 executed");

            StatusHolder.nextTurn();
        }
    }
}

和T2一样,只是在waitForTurn(0)中将0改为1,在print语句中将T1改为T2。

我的主要内容如下:

package Test;

public class Main
{
    public static void main(String[] args) throws InterruptedException
    {
        Thread t1 = new Thread(new ThreadOne());
        Thread t2 = new Thread(new ThreadTwo());

        t1.start();
        t2.start();

    }
}

所以 run 方法是这样的: 在循环开始时,线程会通过调用 waitForTurn() 来检查转角值是否可以采取行动:

package Test;

public class ThreadUtils
{
    public static void waitForTurn(int codeNumber)
    { //Wait until turn value is equal to the given number

        synchronized (StatusHolder.getStatusHolder())
        {
            while (StatusHolder.getTurn() != codeNumber)
            {
                try
                {
                    StatusHolder.getStatusHolder().wait();
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
        }
    }
}

如果两个值相等,则线程执行,否则等待 StatusHolder 对象从 nextTurn() 调用中唤醒,因为当轮值更改时,所有线程都被唤醒,以便它们可以检查是否有新的转值是他们正在等待以便他们可以运行的值。

请注意,nextTurn() 在 0 和 1 之间循环:这是因为在这种情况下,我只有两个线程,第一个在转弯标志为 0 时执行,第二个在其 1 时执行,然后再次为 0,依此类推。我可以通过更改此值轻松更改转数。

问题:如果我运行它,一切顺利并且似乎可以工作,但是输出控制台突然停止流动,即使程序根本没有崩溃。我尝试在 main 中放置一个 t1.join() 然后 print ,但该 print 永远不会执行,这意味着线程永远不会停止/死亡,但有时它们会保持锁定状态。 如果我放置三个线程,这看起来会更加明显:它比两个线程停止得更快。

我对线程比较陌生,所以我可能在这里遗漏了一些非常愚蠢的东西......

编辑:我不希望每次都删除一个线程并创建一个新线程:每秒创建和删除数千个 obj 似乎对垃圾收集器来说是一个很大的工作量。

我使用线程而不是函数的原因是因为在我的实际应用程序中(此代码只是简化了)在某个回合实际上必须运行多个线程(并行),例如:turn 1 one螺纹,转 2 一个螺纹,转 3 30 个螺纹,重复。所以我想为什么不也为单个函数创建线程并让整体思考顺序。

【问题讨论】:

  • 您可以使用 Semaphore 来同步函数,而不需要线程。但是,对于您的情况,考虑在您调用下一回合并重新创建线程以进行新交互时终止线程
  • 此外,如果必须一个接一个地执行操作,则使用单个线程(例如在while 循环和调用方法中)是首选解决方案。对这样的事情使用多线程没有任何好处,只会消耗额外的资源并使代码结构复杂化。
  • 您可以使用联接。 T1。开始()

标签: java multithreading synchronization


【解决方案1】:

这是一个不好的方法。多个线程允许您同时执行任务。 “一个接一个地”执行动作是单线程的工作。

只需这样做:

List<Runnable> tasks = new ArrayList<>();
tasks.add(new ThreadOne()); /* Pick better names for tasks */
tasks.add(new ThreadTwo());
...
ExecutorService worker = Executors.newSingleThreadExecutor();
worker.submit(() -> {
    while (!Thread.interrupted()) 
        tasks.forEach(Runnable::run);
});
worker.shutdown();

当您的应用程序完全退出时调用 worker.shutdownNow() 以在它们的周期结束时停止这些任务。

【讨论】:

    【解决方案2】:

    你可以使用 Semaphore 类,它更简单

    类 t1:

    public class t1 implements Runnable{
       private Semaphore s2;
         private Semaphore s1;
        public t1(Semaphore s1,Semaphore s2){
        this.s1=s1;
        this.s2=s2;
        }
          public void run()
        {
            while (true)
            {
                try {
                    s1.acquire();
                } catch (InterruptedException ex) {
                    Logger.getLogger(t1.class.getName()).log(Level.SEVERE, null, ex);
                }
    
                //Execute job, code's not here for simplicity
                System.out.println("T1 executed");
           s2.release();
    
            }
        }
    }
    

    类 t2:

    public class t2 implements Runnable{
        private Semaphore s2;
         private Semaphore s1;
        public t2(Semaphore s1,Semaphore s2){
        this.s1=s1;
        this.s2=s2;
        }
    
          public void run()
        {
            while (true)
            {
                try {
                    s2.acquire();
                } catch (InterruptedException ex) {
                    Logger.getLogger(t2.class.getName()).log(Level.SEVERE, null, ex);
                }
    
                //Execute job, code's not here for simplicity
                System.out.println("T2 executed");
    
                s1.release();
            }
        }
    }
    

    类主:

    public class Testing {
    
        /**
         * @param args the command line arguments
         */
        public static void main(String[] args) {
             Semaphore s2=new Semaphore(0);
             Semaphore s1=new Semaphore(1);
           Thread th1 = new Thread(new t1(s1,s2));
            Thread th2 = new Thread(new t2(s1,s2));
    
            th1.start();
            th2.start();
    }}
    

    【讨论】:

    • 好答案!塔哈爵士。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-08
    • 2016-03-20
    • 1970-01-01
    • 2012-10-06
    • 2012-12-28
    • 1970-01-01
    相关资源
    最近更新 更多