Java线程基础

2. Java线程基础

 

一、 创建新线程

 

1.1 继承Thread类

 

 Thread t1 = new Thread();
 t1.start();

start()方法可以创建一个新的线程,并让这个线程执行run()方法

 Thread t2 = new Thread();
 t2.run()

这种直接用run() 方法,不会创建一个新的线程,只是在当前线程中串行的执行run方法。

 

1.2 通过Runnable接口实现

 public class CreateThread implements Runnable(){
   public static void mian(String args[]){
     Thread t1 = new Runnable(new CreateThread());
     t1.start();
   }
   @Override
   public void run(){
     System.out.println("Runnable Thread");
   }
 }

 

二、 线程终止

Thread提供了stop() 方法,但是这是已经被废弃的方法。因为stop()方法会强行将执行一半的线程终结,可能会引

数据不一致的情况。

 

所以停止线程的方法,可以定义一个标记变量,用于指示线程是否需要退出。

 

 

三、 线程中断

 

与线程中断有关的三个方法

 public void Thread.interrupt() //中断线程
 public boolean Thread.isInterrupted() //判断是否被中断
 public static boolean Thread.interrupted //判断是否被中断,并清除当前中断状态       

Thread.interrupt()方法是一个实例方法。他通知目标线程中断,也就是设置中断标志。中断标志表示当前线程已经被中断了。

Thread.isInterrupted() 方法也是实例方法,它判断当前线程是否有被中断(通过检查中断标志位)。

Thread.interrupted() 也是用来判断当前线程的中断状态,但同时会清楚线程的中断标志位状态。

 

Thread.sleep()方法会让当前线程休眠若干时间,它会抛出一个InterruptedException 中断异常。 InterruptedException 不是运行时异常,也就是说程序必须捕获并且处理它,当线程在sleep() 休眠时,如果被中断,这个异常就会产生。

 

 public static void main(String args[]) throws InterrupterException{
   Thread t1  = new Thread();
   @Override
   public void run(){
     while(true){
       if(Thread.currentThread().isInterrupted()){
         System.out.println("Interruted");
         break;
       }
       try{
         Thread.sleep(2000); 
       }catch(InterruptedException e){
         System.out.println("Interrupted When Sleep");
         //设置中断状态
         Thread.currentThread().interrupte();
       }
       Thread.yield();
     }
   };
   t1.start();
   Thread.sleep(2000);
   t1.interrupt();
 }

执行sleep()方法,会抛出异常,被catch捕获。可以立即退出线程了,但是为了保证数据的完整性,我们可以在

catch中重新设置中断状态(sleep()由于线程中断,会清除中断标记状态),继续后续的处理。

 

 

四、wait() 和 nodify()

一个线程调用了object.wait() 就进入了等待队列,直到其他线程调用了object.nodify()方法。当

object.nodify()被调用时,会随机从等待队列中选择一个线程,将其唤醒。nodifyAll(),唤醒等待队列中

的所有线程。

 

Object.wait() 方法不是可以随便调用的。它必须包含在对应的 synchronized 语句中,无论是wait() 或者 notify() 都需要首先获得目标对象的一个 监视器。而wait() 方法执行后,会释放这个监视器。这样能使其他线程不至于因为该线程的休眠而全部无法正常执行

 

 

五、挂起(suspend)和继续执行(resume)线程(已被废弃,不推荐使用)

一个线程执行suspend方法是会挂起,但是不释放锁,导致其他线程必须等到resume被执行,才能得到锁。如果

resumesuspend前不幸被执行,可能导致线程被永久挂起。

 

 

 

 

 

六、 等待线程join

两个join方法

 public final void join() throws InterruptedException
 public final synchronized void join(long millis) throws InterruptedException

第一个join() 方法表示无限等待,它会一直阻塞当前线程,知道目标线程执行完毕((别的线程得等调用了join的线程执行完)。第二个方法给出了一个最大等待时间,如果超过给定时间目标线程还在执行,当前线程也会因为“等不及了”,而继续往下执行。

 public class JoinMain{
     public volatile static int i = 0;
     public static class AddThread extends Thread{
         @Override
         public void run(){
             for(i=0;i<10000000;i++);
         }
     }
     public static void main(String[] args) throws InterruptedException{
         AddThread at = new AddThread();
         at.start();
         at.join();
         System.out.println(i);
     }
 }

 

如果不使用join(),可能输出的 i 的值很小,因为AddThread() 没来得及全部执行完,就已经输出了。使用了join(),只有等AddThread() 执行完,才执行其他线程。

join方法本质是让调用线程wait() 在当前线程的实例上。

join方法的核心代码:

 while(isAlive){
   wait(0);
 }

 

 

七、谦让yield() 方法:

定义如下:

 public static native void yield();
 ​

调用该方法后,线程会让出CPU,但不意味着不进行CPU的竞争。让出CPU后,还是会进行竞争的。如果认为一

个线程不是那么重要,优先级不是那么高,那么可以适当调用Thread.yield(),让其他线程有更多机会。

 

 

八、守护线程Daemon

 

守护线程是一种特殊的线程,就和它的名字一样,它是系统的守护者,在后台默默地守护一些系统服务,比如垃圾

回收线程,JIT线程就可以理解守护线程。与之对应的就是用户线程,用户线程就可以认为是系统的工作线程,它

会完成整个系统的业务操作。用户线程完全结束后就意味着整个系统的业务任务全部结束了,因此系统就没有对象

需要守护的了,守护线程自然而然就会退。当一个Java应用,只有守护线程的时候,虚拟机就会自然退出。下面以

一个简单的例子来表述Daemon线程的使用。

 public class DaemonDemo {
     public static void main(String[] args) {
         Thread daemonThread = new Thread(new Runnable() {
             @Override
             public void run() {
                 while (true) {
                     try {
                         System.out.println("i am alive");
                         Thread.sleep(500);
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     } finally {
                         System.out.println("finally block");
                     }
                 }
             }
         });
         daemonThread.setDaemon(true);
         daemonThread.start();
         //确保main线程结束前能给daemonThread能够分到时间片
         try {
             Thread.sleep(800);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
     }
 }

 

输出结果为:

i am alive finally block i am alive

上面的例子中daemodThread run方法中是一个while死循环,会一直打印,但是当main线程结束后

daemonThread就会退出所以不会出现死循环的情况。main线程先睡眠800ms保证daemonThread能够拥有一次

时间片的机会,也就是说可以正常执行一次打印“i am alive”操作和一次finally块中"finally block"操作。紧接着

main 线程结束后,daemonThread退出,这个时候只打印了"i am alive"并没有打印finnal块中的。因此,这里需

要注意的是守护线程在退出的时候并不会执行finnaly块中的代码,所以将释放资源等操作不要放在finnaly块中执

行,这种操作是不安全的

 

线程可以通过setDaemon(true)的方法将线程设置为守护线程。并且需要注意的是设置守护线程要先于start()方

法,否则会报

Exception in thread "main" java.lang.IllegalThreadStateException at java.lang.Thread.setDaemon(Thread.java:1365) at learn.DaemonDemo.main(DaemonDemo.java:19)

这样的异常,但是该线程还是会执行,只不过会当做正常的用户线程执行。

 

 

 

九、线程状态转化图

 

 

参考

《实战Java高并发程序设计》

线程状态及基本操作

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值