二、线程的使用

分类:N07_Java

标签:

二、线程的使用

2.1 线程的状态

网上对线程状态的描述很多,有5种,6种,7种,都可以接受

5中状态一般是针对传统的线程状态来说(操作系统层面)

Java中给线程准备的6种状态

NEW:Thread对象被创建出来了,但是还没有执行start方法。

RUNNABLE:Thread对象调用了start方法,就为RUNNABLE状态(CPU调度/没有调度)

BLOCKED、WAITING、TIME_WAITING:都可以理解为是阻塞、等待状态,因为处在这三种状态下,CPU不会调度当前线程

BLOCKED:synchronized没有拿到同步锁,被阻塞的情况

WAITING:调用wait方法就会处于WAITING状态,需要被手动唤醒

TIME_WAITING:调用sleep方法或者join方法,会被自动唤醒,无需手动唤醒

TERMINATED:run方法执行完毕,线程生命周期到头了

在Java代码中验证一下效果

NEW:


public static void main(String[] args) throws InterruptedException {
   Thread t1 = new Thread(() -> {
 
   });
   System.out.println(t1.getState());
}

RUNNABLE:


public static void main(String[] args) throws InterruptedException {
   Thread t1 = new Thread(() -> {
       while(true){

       }
   });
   t1.start();
   Thread.sleep(500);
   System.out.println(t1.getState());
}

BLOCKED:


public static void main(String[] args) throws InterruptedException {
   Object obj = new Object();
   Thread t1 = new Thread(() -> {
       // t1线程拿不到锁资源,导致变为BLOCKED状态
       synchronized (obj){

       }
   });
   // main线程拿到obj的锁资源
   synchronized (obj) {
       t1.start();
       Thread.sleep(500);
       System.out.println(t1.getState());
   }
}

WAITING:


public static void main(String[] args) throws InterruptedException {
   Object obj = new Object();
   Thread t1 = new Thread(() -> {
       synchronized (obj){
           try {
               obj.wait();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }
   });
   t1.start();
   Thread.sleep(500);
   System.out.println(t1.getState());
}

TIMED_WAITING:


public static void main(String[] args) throws InterruptedException {
   Thread t1 = new Thread(() -> {
       try {
           Thread.sleep(1000);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
   });
   t1.start();
   Thread.sleep(500);
   System.out.println(t1.getState());
}

TERMINATED:


public static void main(String[] args) throws InterruptedException {
   Thread t1 = new Thread(() -> {
       try {
           Thread.sleep(500);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
   });
   t1.start();
   Thread.sleep(1000);
   System.out.println(t1.getState());
}

2.2 线程的常用方法

2.2.1 获取当前线程

Thread的静态方法获取当前线程对象

public static void main(String[] args) throws ExecutionException, InterruptedException {
// 获取当前线程的方法
   Thread main = Thread.currentThread();
   System.out.println(main);
   // "Thread[" + getName() + "," + getPriority() + "," +  group.getName() + "]";
   // Thread[main,5,main]
}
2.2.2 线程的名字

在构建Thread对象完毕后,一定要设置一个有意义的名称,方面后期排查错误

public static void main(String[] args) throws ExecutionException, InterruptedException {
   Thread t1 = new Thread(() -> {
       System.out.println(Thread.currentThread().getName());
   });
   t1.setName("模块-功能-计数器");
   t1.start();
}
2.2.3 线程的优先级

其实就是CPU调度线程的优先级、

java中给线程设置的优先级别有10个级别,从1~10任取一个整数。

如果超出这个范围,会排除参数异常的错误

public static void main(String[] args) throws ExecutionException, InterruptedException {
   Thread t1 = new Thread(() -> {
       for (int i = 0; i < 1000; i++) {
           System.out.println("t1:" + i);
       }
   });
   Thread t2 = new Thread(() -> {
       for (int i = 0; i < 1000; i++) {
           System.out.println("t2:" + i);
       }
   });
   t1.setPriority(1);
   t2.setPriority(10);
   t2.start();
   t1.start();
}
2.2.4 线程的让步

可以通过Thread的静态方法yield,让当前线程从运行状态转变为就绪状态。

public static void main(String[] args) throws ExecutionException, InterruptedException {
   Thread t1 = new Thread(() -> {
       for (int i = 0; i < 100; i++) {
           if(i == 50){
               Thread.yield();
           }
           System.out.println("t1:" + i);
       }
   });
   Thread t2 = new Thread(() -> {
       for (int i = 0; i < 100; i++) {
           System.out.println("t2:" + i);
       }
   });
   t2.start();
   t1.start();
}
2.2.5 线程的休眠

Thread的静态方法,让线程从运行状态转变为等待状态

sleep有两个方法重载:

sleep会抛出一个InterruptedException

public static void main(String[] args) throws InterruptedException {
   System.out.println(System.currentTimeMillis());
   Thread.sleep(1000);
   System.out.println(System.currentTimeMillis());
}
2.2.6 线程的强占

Thread的非静态方法join方法

需要在某一个线程下去调用这个方法

如果在main线程中调用了t1.join(),那么main线程会进入到等待状态,需要等待t1线程全部执行完毕,在恢复到就绪状态等待CPU调度。

如果在main线程中调用了t1.join(2000),那么main线程会进入到等待状态,需要等待t1执行2s后,在恢复到就绪状态等待CPU调度。如果在等待期间,t1已经结束了,那么main线程自动变为就绪状态等待CPU调度。


public static void main(String[] args) throws InterruptedException {
   Thread t1 = new Thread(() -> {
       for (int i = 0; i < 10; i++) {
           System.out.println("t1:" + i);
           try {
               Thread.sleep(1000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }
   });
   t1.start();
   for (int i = 0; i < 10; i++) {
       System.out.println("main:" + i);
       try {
           Thread.sleep(1000);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       if (i == 1){
           try {
               t1.join(2000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }
   }
}
2.2.7 守护线程

默认情况下,线程都是非守护线程

JVM会在程序中没有非守护线程时,结束掉当前JVM

主线程默认是非守护线程,如果主线程执行结束,需要查看当前JVM内是否还有非守护线程,如果没有JVM直接停止

public static void main(String[] args) throws InterruptedException {
   Thread t1 = new Thread(() -> {
       for (int i = 0; i < 10; i++) {
           System.out.println("t1:" + i);
           try {
               Thread.sleep(1000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }
   });
   t1.setDaemon(true);
   t1.start();
}
2.2.8 线程的等待和唤醒

可以让获取synchronized锁资源的线程通过wait方法进去到锁的等待池,并且会释放锁资源

可以让获取synchronized锁资源的线程,通过notify或者notifyAll方法,将等待池中的线程唤醒,添加到锁池

notify随机的唤醒等待池中的一个线程到锁池

notifyAll将等待池中的全部线程都唤醒,并且添加到锁池

在调用wait方法和notify以及norifyAll方法时,必须在synchronized修饰的代码块或者方法内部才可以,因为要操作基于某个对象的锁的信息维护。

public static void main(String[] args) throws InterruptedException {
   Thread t1 = new Thread(() -> {
       sync();
   },"t1");

   Thread t2 = new Thread(() -> {
       sync();
   },"t2");
   t1.start();
   t2.start();
   Thread.sleep(12000);
   synchronized (MiTest.class) {
       MiTest.class.notifyAll();
   }
}

public static synchronized void sync()  {
   try {
       for (int i = 0; i < 10; i++) {
           if(i == 5) {
               MiTest.class.wait();
           }
           Thread.sleep(1000);
           System.out.println(Thread.currentThread().getName());
       }
   } catch (InterruptedException e) {
       e.printStackTrace();
   }
}

2.3 线程的结束方式

线程结束方式很多,最常用就是让线程的run方法结束,无论是return结束,还是抛出异常结束,都可以

2.3.1 stop方法(不用)

强制让线程结束,无论你在干嘛,不推荐使用当然当然方式,但是,他确实可以把线程干掉


public static void main(String[] args) throws InterruptedException {
   Thread t1 = new Thread(() -> {
       try {
           Thread.sleep(5000);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
   });
   t1.start();
   Thread.sleep(500);
   t1.stop();
   System.out.println(t1.getState());
}
2.3.2 使用共享变量(很少会用)

这种方式用的也不多,有的线程可能会通过死循环来保证一直运行。

咱们可以通过修改共享变量在破坏死循环,让线程退出循环,结束run方法

static volatile boolean flag = true;

public static void main(String[] args) throws InterruptedException {
   Thread t1 = new Thread(() -> {
       while(flag){
           // 处理任务
       }
       System.out.println("任务结束");
   });
   t1.start();
   Thread.sleep(500);
   flag = false;
}
2.3.3 interrupt方式

共享变量方式


public static void main(String[] args) throws InterruptedException {
   // 线程默认情况下,    interrupt标记位:false
   System.out.println(Thread.currentThread().isInterrupted());
   // 执行interrupt之后,再次查看打断信息
   Thread.currentThread().interrupt();
   // interrupt标记位:ture
   System.out.println(Thread.currentThread().isInterrupted());
   // 返回当前线程,并归位为false interrupt标记位:ture
   System.out.println(Thread.interrupted());
   // 已经归位了
   System.out.println(Thread.interrupted());

   // =====================================================
   Thread t1 = new Thread(() -> {
       while(!Thread.currentThread().isInterrupted()){
           // 处理业务
       }
       System.out.println("t1结束");
   });
   t1.start();
   Thread.sleep(500);
   t1.interrupt();
}

通过打断WAITING或者TIMED_WAITING状态的线程,从而抛出异常自行处理

这种停止线程方式是最常用的一种,在框架和JUC中也是最常见的


public static void main(String[] args) throws InterruptedException {
   Thread t1 = new Thread(() -> {
       while(true){
           // 获取任务
           // 拿到任务,执行任务
           // 没有任务了,让线程休眠
           try {
               Thread.sleep(1000);
           } catch (InterruptedException e) {
               e.printStackTrace();
               System.out.println("基于打断形式结束当前线程");
               return;
           }
       }
   });
   t1.start();
   Thread.sleep(500);
   t1.interrupt();
}

wait和sleep的区别?

wait方法会将持有锁的线程从owner扔到WaitSet集合中,这个操作是在修改ObjectMonitor对象,如果没有持有synchronized锁的话,是无法操作ObjectMonitor对象的。


修改内容