Java多线程 - 运行线程只按顺序运行一次方法

时间:2018-04-20 17:07:17

标签: java multithreading synchronization

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

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

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,依此类推。我可以通过更改此值轻松更改圈数。

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

我对线程比较新,所以我可能会遗漏一些真正愚蠢的东西...

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

我之所以使用线程而不是函数是因为在我的实际应用程序中(这段代码只是简化)在某个转弯处实际上有多个线程必须运行(并行),例如:turn 1个一个螺纹,转动2个螺纹,转动3个30个螺纹,重复。所以我想为什么不为单个函数创建线程,并使整个思维顺序。

2 个答案:

答案 0 :(得分:0)

你可以使用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();
}}

答案 1 :(得分:0)

这是一种糟糕的做法。多个线程允许您同时执行任务。 “按顺序依次执行”操作是单个线程的作业。

做这样的事情:

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()以在周期结束时停止这些任务。