如何阻止同一个线程运行多个实例

时间:2011-09-08 15:16:16

标签: java multithreading concurrency

我有一个守护程序线程,它在页面打开时启动。然后在关闭页面时停止该线程。所以在我持有该主题的类中,我创建它是这样的:

class A {
 private static volatile boolean isStopped=false;

 //this method is called then the page is loaded
 public void testListener() {
   Thread listener = new Thread(new Runnable() {
      public void run() {
       while(!isStopped) {
        //perform listener event
       try {
         //after every event sleep for a while
         Thread.sleep(1000 *2)
       } catch(InterruptedException e){}
      }
     }
    });
 }
 listener.setName("Test-Server-Daemon");
 listener.setDaemon(true);
 listener.start();

 // reset back to false so thread can be restarted when the page load event,
 // call this method instance
 if (isStopped) {
   isStopped=false;
 }
}

 /**This is called when page is closed**/
 public static void stopListener() {
   isStopped=true;
  }
}

经过调查,我注意到当页面关闭并且在30秒间隔内没有再次打开时,线程正常停止。

但是当页面关闭并在2秒间隔内重新打开时,旧线程不会停止,因此会同时运行新线程。

因此,从下图中可以看到,当我关闭并快速打开页面时,我再次启动相同的线程。

有谁知道如何防止这种情况发生?

我尝试使用线程interrupt,我重置了互斥锁但没有快乐。

编辑:

isStopped是volatile

enter image description here

7 个答案:

答案 0 :(得分:2)

在旧线程有机会看到它应该停止之前,您可能会覆盖isStoppedfalse 的值。问题出在这里:

if(isStopped) 
{
  isStopped=false;  
}

您应该更好地隔离代码:为每个线程创建A的单独实例,并使isStopped实例volatile字段(不是static)。并删除该代码块...

答案 1 :(得分:2)

要继续@Jordão的回答,isStopped变量应该是每个线程。我建议使用像AtomicBoolean之类的东西,并将你的线程代码改为大约:

public AtomicBoolean testListener() {
    final AtomicBoolean isStopped = new AtomicBoolean(false);
    Thread listener = new Thread(new Runnable() {
        public void run() {
            while(!isStopped.get()) {
                ...
            }
        }
    });
    listener.setName("Test-Server-Daemon");
    listener.setDaemon(true);
    listener.start();
    return isStopped;
}

然后返回页面控制器,您可以执行以下操作:

AtomicBoolean isStopped = testListener();
// do the page stuff
...
// when done stop the thread
isStopped.set(true);

答案 2 :(得分:1)

如果您的标志isStopped在至少2秒内不成立,那么当发生这种情况时,您的线程可能正在休眠。一个更简单的解决方案是避免启动/停止线程,因为这可能会导致节省的开销(这肯定会使问题复杂化)

我要做的就是一次启动线程一次。

public void run() {
   try {
     while(true) {
       if(!isStopped) {
         //perform listener event
       }
       //after every event sleep for a while
       Thread.sleep(1000 *2);
     }
   } catch(InterruptedException e){}
}

通过设置标志,它会停止执行,但线程会继续检查。

答案 3 :(得分:1)

尝试使用AtomicBoolean而不是Boolean字段。 使用compareAndSet方法;如果你需要更多的澄清,请告诉我,因为javadoc非常有用。

答案 4 :(得分:0)

尝试制作isStopped volatile,即private static volatile boolean isStopped=false;。两个线程(主线程和您自己的线程)之间的内存同步可能存在延迟。

答案 5 :(得分:0)

将您的实例化移到方法之外并使其静态化。这个保证人你将只有一个这个线程的实例。

private static Thread listener;

完成此操作后,您可以将此添加到方法中:

if(listener.isAlive()) try { Thread.sleep(100); } catch (InterruptedException ie) {}

listener = new Thread(new Runnable() {
     public void run() {
          while(!isStopped) {
              //perform listener event
              try {
                  //after every event sleep for a while
                  Thread.sleep(1000 *2)
              }
              catch(InterruptedException e){}
         }
     }
});

现在,在前一个线程停止之前,您不会启动新线程。

(注意,不确定isAlive()是否准确,您可能需要创建自己的Thread实现以准确反映线程是否停止,如果不是)

答案 6 :(得分:0)

我会使用java.util.concurrent.ScheduledExecutorService。它将管理线程和任务的安排。

例如:

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

public class Scheduler {

static ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();

static ScheduledFuture<?> future;

// called when the page is opened
public static void open() {
    future = service.scheduleAtFixedRate(new Runnable() {
        public void run() {
            //perform listener event
        }
    }, 0, 2, TimeUnit.SECONDS); // every 2 seconds


}

// called when the page is closed
public static void close() {
    // stop listener event
    future.cancel(true);
    future = null;
}

}