当前位置: 首页 > 知识库问答 >
问题:

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

严成礼
2023-03-14

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

为了实现这个机制,我创建了一个名为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()调用检查turn值来查看是否可以执行操作:

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()调用中唤醒,因为当turn值更改时,所有线程都将被唤醒,以便它们可以检查新的turn值是否是它们正在等待的值,以便它们可以运行。

注意nextturn()在0和1之间循环:这是因为在这个场景中我只有两个线程,第一个线程在turn标志为0时执行,第二个线程在其为1时执行,然后再次执行0,依此类推。我可以通过更改此值轻松更改圈数。

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

我对线程相对较新,所以我可能错过了一些非常愚蠢的东西...

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

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

共有2个答案

钱经业
2023-03-14

您可以使用信号量类,它更简单

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();
}}
芮学
2023-03-14

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

就做这样的事情:

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

 类似资料:
  • 问题内容: 我正在尝试在我正在处理的Python项目中使用线程,但是线程似乎没有按照我的代码的预期运行。似乎所有线程都按顺序运行(即,线程2在线程1结束后开始,它们不是同时启动)。我编写了一个简单的脚本来对此进行测试,并且该脚本也按顺序运行线程。 这是我从运行它得到的输出: 循环的迭代次数更多时,观察到相同的行为。 我尝试搜索网络和较早的SO答案,但找不到任何有帮助的方法。有人可以指出这段代码有什

  • 问题内容: 您将如何依次执行三个线程?例如。线程1,线程2,线程3。不可能将一个线程的引用传递给另一个线程并从run()方法调用。 因此代码应如下所示: 并应该把 这可以通过使用ThreadPoolExecutor并使用阻塞队列来实现,但即使那样也不是可以接受的答案。 问题答案: 在java.util.concurrent包中使用ExecutorService。更精确地使用

  • 问题内容: 我有3个线程第一打印A第二打印B第三打印C 我想按顺序打印ABCABCABC,依此类推..... 因此,我在下面编写了程序,但无法实现相同的目的。我知道一个问题,当时状态为1时,例如B1和C1线程正在等待,而当我做notifyAll()时,两个等待线程都被唤醒,并且取决于CPU分配,它可能会打印B或C。 在这种情况下,我只希望在A之后打印B。 我需要做什么修改。 问题答案: 将那些IF

  • 给定打印字母a-C的三个线程1-3,如何保证输出顺序? 我希望线程的输出是“abcabc”

  • 这就是我试图解决但不确定如何解决的问题:我有一个对象数组(假设大小为100),每个对象都有一些id。 有10个线程将从该数组中读取数据并将其插入数据库。 如何确保数据以递增序列的方式根据员工ID的序列插入数据库。例如: 如果数组中的对象具有员工ID 6、8和4,那么这些对象应该按照员工ID 4、6和8在数据库中的顺序插入数据库。如何为此编写多线程代码? 更新:请忽略数据库部分,如果它令人困惑,我的

  • 问题内容: 我尝试从一本书(Paul Hyde,Java Thread Programming)中运行示例。它说线程的顺序将互换。但是我总是得到:之后打印10个“主线程”,然后打印10个“新线程”。更有趣的是:如果我将使用tt.run而不是tt.start,那么结果将相反。也许这本书太老了,示例基于JDK 1.2的原因???代码如下: 问题答案: JVM决定何时将控制权从主线程转移到第二个线程。由