public class ThreadStarvation implements Runnable{
long startTime=System.currentTimeMillis();
Timer t;
class RunnerTask extends TimerTask {
public void run(){
if((System.currentTimeMillis()- startTime)< 100000){
System.out.println(Thread.currentThread().getName()+": in timer task run() method"+" : "+Calendar.getInstance().getTime());
}
else
t.cancel();
}
}
public synchronized void synchronizedTimerMethod(){
try{
Thread.sleep(1000);
t=new Timer();
t.schedule(new RunnerTask(), 0, 2000);
}
catch(InterruptedException ie){}
}
public void run(){
synchronizedTimerMethod();
}
public static void main(String[] args) {
ThreadStarvation ts1=new ThreadStarvation();
Thread t1=new Thread(ts1);
Thread t2=new Thread(ts1);
t1.start();
t2.start();
}
}
上述计划有三个疑点:
1)我想从这个程序得到的是当线程t1启动时,转到同步方法synchronizedTimerMethod()将锁定对象ts1并应锁定它直到计时器对象超出条件。因此,线程t2将无法获得对共享资源ts1的定期访问,并且无法取得进展并将进入饥饿状态。但这不会发生。 Timer类是不可能的?我是新手。
2)当线程t1启动时,它进入同步方法synchronizedTimerMethod,对象ts1将被锁定。在调度计时器对象直到条件被破坏之前,不会释放对象ts1。但是当时线程t1定时器对象首先调度启动的那一天只有Thread t2进入同步方法synchronizedTimerMethod。对象ts1是否因Timer run方法而被释放?
3)此外,当两个线程都违反了条件时,任务没有被取消,特别是程序卡住或我认为它陷入死锁。为什么?
我重写了我的代码如下:
public class ThreadStarvation implements Runnable{
long startTime=System.currentTimeMillis();
final Timer t=new Timer;
class RunnerTask extends TimerTask {
public void run(){
if((System.currentTimeMillis()- startTime)< 100000){
System.out.println(Thread.currentThread().getName()+": in timer task run() method"+" : "+Calendar.getInstance().getTime());
}
else
t.cancel();
}
}
public synchronized void synchronizedTimerMethod(){
try{
Thread.sleep(1000);
t.schedule(new RunnerTask(), 0, 2000);
}
catch(InterruptedException ie){}
}
public void run(){
synchronizedTimerMethod();
}
public static void main(String[] args) {
ThreadStarvation ts1=new ThreadStarvation();
Thread t1=new Thread(ts1);
Thread t2=new Thread(ts1);
t1.start();
t2.start();
}
}
现在我只想让任务停止。为此,我将Timer对象作为final。然后这个任务似乎也没有取消。是否需要进行一些修改?请帮忙。
答案 0 :(得分:0)
1)如果你想强制t1
在t2
之前输入的事实,那么你不能依赖Timer(或者更确切地说是时间)来确保这一点(任意交错)。您应该重写为具有屏障(条件)的监视器,该屏障仅允许t1
先进入。然后让t1
永远不会释放锁定以使t2
饿死,即阻止同步方法终止(请参阅here)。
什么是监视器?如何创建监视器?
在并发中,它是一个用于同步程序并使其更加可预测的构造,即t1
之前的t2
,如图所示。在此上下文中,同步基于满足的某些条件/状态。这些条件充当“障碍”,阻止或允许线程执行。这是非常有用的,因为我们可以使用这些障碍不仅使我们的程序模式可预测,而且还允许我们保证并发中的某些理想属性,即公平性,避免死锁等。因此监视器的重要性。
在Java中,我们可以通过定义一个包含障碍条件的类作为private
变量来创建监视器。然后,我们只允许通过synchronized
方法更改这些变量,这些方法首先测试条件是否已满足(屏障)。
基于代码简化的简单示例:
public class ExampleMonitor implements Runnable{
// Condition for our barrier, note it is private
private boolean t1Entered = false;
public synchronized void synchronizedTimerMethod(){
// Test the barrier (check if conditions hold)
while (!t1Entered && !Thread.currentThread().getName().equals("t1")) {
try {
// Did not pass barrier so wait and release lock
wait();
} catch (Exception e) {
// Handle
}
}
// Thread passed barrier and has acquired the lock and can do what it wants
// Update condition so now anyone can enter/pass the barrier
t1Entered = true;
// If this method never terminates then no other thread can enter because lock is never released
long enterTime = System.currentTimeMillis();
while (true) {
System.out.println(Thread.currentThread().getName());
// Let's allow the method to return and thus release the lock after fixed amount of time
// We can then see that threads other than t1 can now acquire the lock
if (System.currentTimeMillis() - enterTime > 5000) {
break;
}
}
// Notify/wake up any waiting threads
this.notifyAll();
}
public void run(){
synchronizedTimerMethod();
// Thread will now terminate
}
public static void main(String[] args) throws InterruptedException {
ExampleMonitor ts1 = new ExampleMonitor();
Thread t1=new Thread(ts1);
t1.setName("t1");
Thread t2=new Thread(ts1);
t2.setName("t2");
t2.start();
// To illustrate how Monitors can be used to ensure
// ordering despite the order threads start in
Thread.sleep(2000);
t1.start();
}
}
注意:这只是一个快速示例,用于说明并且不理想,即您应该不定义实现Runnable
的监视器。您可以阅读有关监视器here的更多信息。另外,我建议通过我也使用的book。
2)见immibis的彻底回答。
3)来自Java doc:
在对Timer对象的最后一次实时引用消失并且所有未完成的任务都已完成执行之后,计时器的任务执行线程正常终止(并且变为垃圾回收)。但是,这可能会发生任意长的时间。默认情况下,任务执行线程不作为守护程序线程运行,因此它能够阻止应用程序终止。如果调用者想要快速终止计时器的任务执行线程,则调用者应该调用计时器的cancel方法。
答案 1 :(得分:0)
public synchronized void synchronizedTimerMethod(){
try{
Thread.sleep(1000);
t=new Timer();
t.schedule(new RunnerTask(), 0, 2000);
}
catch(InterruptedException ie){}
}
这不符合你的想法。 synchronized方法等待一秒钟,然后每两秒调度一次RunnerTask,然后返回。请注意,它不等待RunnerTask运行。 ts1
仅在synchronizedTimerMethod返回之前被锁定,即一秒钟。
100秒后“条件被破坏”时,由于存在错误,您只能取消一个Timer。请注意,synchronizedTimerMethod将t
设置为新的Timer,并且只有一个变量t
。计划完第一个任务后,t
是一个计时器 - 称之为计时器1.计划完第二个任务后,t
是一个不同的计时器 - 称之为计时器2.然后当100秒结束时,两个任务都取消t
,这会两次取消定时器2。