Java线程Step By Step(Step 2)

(See http://www.suneca.com/article.asp?id=54
四、Join方法的使用    
Thread API 包含了等待另一个线程完成的方法:join() 方法。当调用 Thread.join() 时,调用线程将阻塞,直到被join方法加入的目标线程完成为止。 可以于解起来抽象一睦,现在我们来举一个例子说明问题。
 程序代码
package  zizz ;

/**
* 使用继承方式的线程实现.

* @author <a href="http://www.suneca.com">zizz</a>
*
* @Create-Time:2008 下午10:25:33
*/

public   class  MyThread   extends  Thread   {
    
     public   void  run ( )   {
         //循环输出1 到 100
         for ( int  i =1 ;i < =10 ;i + + ) {
            System .out .println ( "子线程输出:"   +  i ) ;
         }
     }
    
     public   static   void  main (String [ ]  args ) {
         //创建一个线程实例
        MyThread t1   =   new  MyThread ( ) ;
         //线程进入运行态
        t1 .start ( ) ;
         //主线程输出
        System .out .println ( "主线程输出:http://www.suneca.com" ) ;
     }
}


输出结果为:


从结果我们可以看到,对于我们运行MyThread这个程序,在主线程当中创建一个新的子线程,新的子线程启动后,主线程输出博客地址。因为当前主线程处于运行态,而子线程是处于可运行态,所以输出的结果为先输出网址,再输出了线程输出的数据。但也有可能会出现这种情况,子线程进入可运行态之后,马上进入运行态,那此时的是输出子线程的数据,再输出主线程的数据,它们的执行完全是由线程调度器进行调度。我们再将程序做如下的修改:
 程序代码
package  zizz ;

/**
* 使用继承方式的线程实现.

* @author <a href="http://www.suneca.com">zizz</a>
*
* @Create-Time:2008 下午10:25:33
*/

public   class  MyThread   extends  Thread   {
    
     public   void  run ( )   {
         //循环输出1 到 100
         for ( int  i =1 ;i < =100 ;i + + ) {
            System .out .println ( "子线程输出:"   +  i ) ;
         }
     }
    
     /**
     * 应用程序入口.
     * 
     * @param args
     */

     public   static   void  main (String [ ]  args ) {
         //创建一个线程实例
        MyThread t1   =   new  MyThread ( ) ;
         //线程进入运行态
        t1 .start ( ) ;
         //主线程输出
         for ( int  i =0 ;i <100 ;i + + ) {
            System .out .println ( "主线程输出:http://www.suneca.com" ) ;
         }
     }
}


此时的输出结果为:



主线程跟子线程在线程调度器的调度下,相互抢夺CPU资源,交叉运行着!

那么,如果我希望程序启用子线程,必须等待着子线程执行完毕之后,主线程才能继续执行下去,那该怎么办?那此时,我们就必须使用到join这个方法啦!当使用Thread.join()时,调用线程将阻塞。因为子线程是由主线程进行调用的,所以当子线程调用到join这个函数时,主线程将阻塞,必须等待子线程执行完毕之后,才能继续执行。使用join的程序:
 程序代码
package  zizz ;

/**
* 使用继承方式的线程实现.

* @author <a href="http://www.suneca.com">zizz</a>
*
* @Create-Time:2008 下午10:25:33
*/

public   class  MyThread   extends  Thread   {
    
     public   void  run ( )   {
         //循环输出1 到 100
         for ( int  i =1 ;i < =10 ;i + + ) {
            System .out .println ( "子线程输出:"   +  i ) ;
         }
     }
    
     /**
     * 应用程序入口.
     * 
     * @param args
     */

     public   static   void  main (String [ ]  args ) {
         //创建一个线程实例
        MyThread t1   =   new  MyThread ( ) ;
         //线程进入运行态
        t1 .start ( ) ;
         try   {
             //线程的调用者,即主线程阻塞!
            t1 .join ( ) ;
         }   catch   (InterruptedException e )   {
            e .printStackTrace ( ) ;
         }
         //主线程输出
         for ( int  i =0 ;i <10 ;i + + ) {
            System .out .println ( "主线程输出:http://www.suneca.com" ) ;
         }
     }
}


输出结果为:



从结果当中,我们可以看出,当子线程调用到join的时候,主线程将阻塞,子线程执行完毕之后,主线程才能继续执行。对于join()方法,还有两个重载方法,比如joing(long millis),那就是让主程序阻塞多长时间后才能恢复到可运行状态。

如果主程序调用了两个子线程,那这两个子线程是如何工作的呢?
 程序代码
package  zizz ;

/**
* 使用继承方式的线程实现.

* @author <a href="http://www.suneca.com">zizz</a>
*
* @Create-Time:2008 下午10:25:33
*/

public   class  MyThread   extends  Thread   {
    
     public  MyThread (String name ) {
         super (name ) ;
     }
    
     public   void  run ( )   {
         //循环输出1 到 100
         for ( int  i =1 ;i < =100 ;i + + ) {
            System .out .println ( "子线程输出:"   +   this .getName ( )   +   " - "   +  i ) ;
         }
     }
    
     /**
     * 应用程序入口.
     * 
     * @param args
     */

     public   static   void  main (String [ ]  args ) {
         //创建一个线程实例
        MyThread t1   =   new  MyThread ( "t1" ) ;
        MyThread t2   =   new  MyThread ( "t2" ) ;
         //线程进入运行态
        t1 .start ( ) ;
        t2 .start ( ) ;
         try   {
             //线程的调用者,即主线程阻塞!
            t1 .join ( ) ;
            t2 .join ( ) ;
         }   catch   (InterruptedException e )   {
            e .printStackTrace ( ) ;
         }
         //主线程输出
         for ( int  i =0 ;i <10 ;i + + ) {
            System .out .println ( "主线程输出:http://www.suneca.com" ) ;
         }
     }
}


输出结果:



从图的输出结果我们可以看出:对于在线程t1跟线程t2当中都调用了线程的join()方法,那么,调用t1、t2的主线程将阻塞,等到t1、t2都执行完毕之后,再执行主线程。对于子线程t1跟t2,它们的一个先后顺序,如何进行调度,完全是由线程调度器进行管理!

五、多线程
从前面的例子我们已经可以看出,对于有两个以上的线程同时启动,那这些线程的执行的先后顺序我们是没有办法预知的,因为对于它们的调用完全是由线程调度器进行调用!当子线程t1跟t2同时处于运行状态,那谁先执行谁慢执行,由线程调度器决定。那我们程序能否进行控制呢?答案是可以的,我们可以通过设计优先级别来进行控制。每一个线程,默认的优先级别为5,值越高,表示优先级别越高(最高为10),值越低(最低为1),表示优先级别越低!当线先级别相等的时候,那就只有排队等待着线程调度器的调用。
 程序代码
package  zizz ;

/**
* 使用继承方式的线程实现.

* @author <a href="http://www.suneca.com">zizz</a>
*
* @Create-Time:2008 下午10:25:33
*/

public   class  MyThread   extends  Thread   {
    
     public  MyThread (String name ) {
         super (name ) ;
     }
    
     public   void  run ( )   {
         //循环输出1 到 100
         for ( int  i =1 ;i < =10 ;i + + ) {
            System .out .println ( "子线程输出:"   +   this .getName ( )   +   " - "   +  i ) ;
         }
     }
    
     /**
     * 应用程序入口.
     * 
     * @param args
     */

     public   static   void  main (String [ ]  args ) {
         //创建一个线程实例
        MyThread t1   =   new  MyThread ( "t1" ) ;
        MyThread t2   =   new  MyThread ( "t2" ) ;
        
         //线程进入运行态
        t1 .start ( ) ;
        t2 .start ( ) ;
         //主线程输出  
        System .out .println ( "主线程输出:http://www.suneca.com" ) ;
        
     }
}


t1比t2先启动,进入可运行态,排队等候线程的调度。执行结果:



如果设置了线程的优先级,如:
 程序代码
package  zizz ;

/**
* 使用继承方式的线程实现.

* @author <a href="http://www.suneca.com">zizz</a>
*
* @Create-Time:2008 下午10:25:33
*/

public   class  MyThread   extends  Thread   {
    
     public  MyThread (String name ) {
         super (name ) ;
     }
    
     public   void  run ( )   {
         //循环输出1 到 100
         for ( int  i =1 ;i < =10 ;i + + ) {
            System .out .println ( "子线程输出:"   +   this .getName ( )   +   " - "   +  i ) ;
         }
     }
    
     /**
     * 应用程序入口.
     * 
     * @param args
     */

     public   static   void  main (String [ ]  args ) {
         //创建一个线程实例
        MyThread t1   =   new  MyThread ( "t1" ) ;
        MyThread t2   =   new  MyThread ( "t2" ) ;
        
         //设置t1的的优先级别为最低
        t1 .setPriority (Thread .MIN_PRIORITY ) ;
         //设置t2的优先级别为最高
        t2 .setPriority (Thread .MAX_PRIORITY ) ;        
        
         //线程进入运行态
        t1 .start ( ) ;
        t2 .start ( ) ;
         //主线程输出  
        System .out .println ( "主线程输出:http://www.suneca.com" ) ;
        
     }
}


执行结果:

从结果当中我们可以看到,最早的时候是由主线程执行,主线程占用着CPU资源,接着,线程t1启动,线程t1进入排队,等候着线程调度器的调度;再接着,线程t2启动,线程t2也进入排队。
我们从结果当中更可以发现一点,对于高于5的线程优先级别,它更容易从当中正在执行的线程(主线程)当中抢夺CPU资源。

多线程的另外一种情况就是,多个线程共享同一个线程实例。
 程序代码
package  zizz ;

/**
* 使用实现Runnable接口的线程.

* @author <a href='http://www.suneca.com'>ZIZZ</a>
*
* @Create-Time:2008 下午10:48:31
*/

public   class  MyRunnable   implements  Runnable {

     public   void  run ( )   {
         for ( int  i =0 ;i <100 ;i + + ) {
            System .out .println (Thread .currentThread ( ) .getName ( )   +   " : "   +  i ) ;
         }
     }
    
     public   static   void  main (String [ ]  args ) {
         //构造一个Runnable的实例
        MyRunnable runnable   =   new  MyRunnable ( ) ;
         //创建新的线程
        Thread t1   =   new  Thread (runnable , "T1" ) ;
        Thread t2   =   new  Thread (runnable , "T2" ) ;
        
         //线程启动.
        t1 .start ( ) ;
        t2 .start ( ) ;
     }

}


执行结果片断:



当多线程共享同一个线程实例的时候,我们需要考虑一下线程的同步问题。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如果需要在Spring Batch中终止多线程Step任务,可以使用TaskExecutorRepeatTemplate类。该类可以在多个线程之间分配可重复执行的任务,并在需要时终止任务。以下是使用TaskExecutorRepeatTemplate类终止多线程Step任务的步骤: 1. 在Step中使用TaskExecutorRepeatTemplate类代替SimpleStepExecutor类。 2. 在TaskExecutorRepeatTemplate类中设置TaskExecutor和RepeatOperations。TaskExecutor用于执行可重复执行的任务,RepeatOperations用于控制可重复执行的任务。 3. 当需要终止任务时,调用RepeatOperations的cancel方法。 下面是一个示例代码片段,展示如何使用TaskExecutorRepeatTemplate类终止多线程Step任务: ``` <bean id="taskExecutor" class="org.springframework.core.task.SimpleAsyncTaskExecutor"/> <bean id="taskExecutorRepeatTemplate" class="org.springframework.batch.repeat.support.TaskExecutorRepeatTemplate"> <property name="taskExecutor" ref="taskExecutor"/> <property name="throttleLimit" value="10"/> <property name="retryTemplate"> <bean class="org.springframework.retry.support.RetryTemplate"> <property name="retryPolicy"> <bean class="org.springframework.retry.policy.SimpleRetryPolicy"> <property name="maxAttempts" value="3"/> </bean> </property> </bean> </property> </bean> <batch:step id="multiThreadedStep"> <batch:tasklet> <batch:chunk reader="reader" processor="processor" writer="writer" commit-interval="100"/> </batch:tasklet> <batch:listeners> <batch:listener ref="myStepExecutionListener"/> </batch:listeners> <batch:task-executor ref="taskExecutorRepeatTemplate"/> </batch:step> ``` 在上面的代码中,我们定义了一个名为taskExecutor的SimpleAsyncTaskExecutor bean,并将其用作TaskExecutorRepeatTemplate的taskExecutor属性。我们还设置了TaskExecutorRepeatTemplate的throttleLimit属性,以限制并发线程的数量。我们还定义了一个重试模板,并将其设置为TaskExecutorRepeatTemplate的retryTemplate属性。 在Step中,我们使用TaskExecutorRepeatTemplate作为task-executor属性,以便在多个线程之间分配可重复执行的任务。当需要终止任务时,我们可以在StepExecutionListener中调用RepeatOperations的cancel方法,如下所示: ``` public class MyStepExecutionListener implements StepExecutionListener { private volatile RepeatOperations repeatOperations; @Override public void beforeStep(StepExecution stepExecution) { repeatOperations = ((TaskletStep) stepExecution.getStep()).getTasklet().getRepeatOperations(); } @Override public ExitStatus afterStep(StepExecution stepExecution) { repeatOperations.cancel(); return null; } } ``` 在上面的代码中,我们实现了StepExecutionListener接口,并在beforeStep方法中获取RepeatOperations实例,以便在afterStep方法中调用其cancel方法来终止任务。请注意,我们使用了volatile关键字来确保多个线程之间的可见性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值