线程的命名与获取

多线程的运行状态是不确定的,那么在程序开发之中为了可以获取到一些需要使用的线程就只能依靠线程的名字来进行操作。

线程命名

所以线程的名字是一个至关重要的概念,这样在 Thread 类之中就提供有线程名称的处理:

  1. //构造方法
  2. public Thread(Runnable target,String name);
  3. //设置名字
  4. public final void setName(String name);
  5. //取得名字
  6. public final String getName();

对于线程对象的获得是不可能只靠一个 this 来完成的,因为线程的状态不可控,但是有一点是明确的,所有的线程对象都一定要执行 run() 方法,那么这个时候可以考虑获取当前线程,在 Thread 类里面提供有获取当前线程的一个方法。

  1. // 获取当前线程
  2. public static Thread current Thread():

范例测试:观察线程的命名操作

  1. class MyThread implements Runnable{
  2. @Override
  3. public void run() {
  4. // 获取当前线程对象
  5. Thread thread = Thread.currentThread();
  6. // 获取线程名称
  7. String threadName = thread.getName();
  8. System.out.println("当前线程的名称是:"+ threadName);
  9. }
  10. }
  11. public class ThreadDemo {
  12. public static void main(String[] args) {
  13. // 构造函数命名线程名称
  14. Thread threadA = new Thread(new MyThread(), "线程A");
  15. threadA.start();
  16. Thread threadB = new Thread(new MyThread());
  17. threadB.setName("线程B");
  18. threadB.start();
  19. System.out.println("当前线程的名称是:"+Thread.currentThread().getName());
  20. }
  21. }

执行效果:
image.png
主方法也是一个线程,线程名称是:main

默认线程名称

当开发者为线程设置名字的时候,而如果没有设置名字,则会自动生成一个不重复的名字,这种自动的属性命名主要是依靠了 static 属性完成的,在 Thread 类里面定义了如下操作:

  1. private static int threadInitNumber;
  2. private static synchronized int nextThreadNum(){
  3. Return threadInitNumber++;
  4. }
  5. public Thread(Runnable target) {
  6. init(null, target, "Thread-" + nextThreadNum(), 0);
  7. }

测试:

  1. class MyThread implements Runnable{
  2. @Override
  3. public void run() {
  4. // 获取当前线程对象
  5. Thread thread = Thread.currentThread();
  6. // 获取线程名称
  7. String threadName = thread.getName();
  8. System.out.println("当前线程的名称是:"+ threadName);
  9. }
  10. }
  11. public class ThreadDemo {
  12. public static void main(String[] args) {
  13. // 循环创建10个线程,观察其名称
  14. for (int i = 0; i < 10; i++) {
  15. new Thread(new MyThread()).start();
  16. }
  17. }
  18. }

image.png

线程的休眠

如果现在希望某一个线程可以暂缓执行,那么可以使用休眠的处理。在 Thread 类之中定义的休眠的方法如下:

  1. // 休眠毫秒
  2. public static void sleep(long millis)throws InterruptedException;
  3. // 休眠毫秒+纳秒
  4. public static void sleep(long mills,int nanosthrowsInterruptedException;

在进行休眠的时候有可能会产生中断异常 “InterruptedException”, 中断异常属于 Exception 的子类,所以证明该异常必须进行休眠处理。

范例:观察休眠处理

  1. public class ThreadDemo{
  2. public static void main(String[]args)throws Exception{
  3. new thread(()->{
  4. for(int x=0;x<10;x++){
  5. // 休眠1s
  6. Thread.sleep(1000);
  7. System.out.println(Thread.currentThread.currentThread());
  8. }
  9. },"线程对象A").start();
  10. }

暂缓执行之后执行慢了,休眠时间一到程序马上回复继续执行。

休眠的主要特点是可以自动实现线程的唤醒,以继续进行后续的处理。但是需要注意的是,如果现在你有多个线程对象,那么休眠也是有先后顺序的。

线程的中断

在之前发现线程的休眠里面提供有一个中断异常,实际上就证明线程的休眠是可以被打断的,而这种打断肯定是由其他线程完成的,在 Thread 类里面提供有这种中断执行的处理方法:

  1. // 判断线程是否被中断
  2. public boolean isInterrupted();
  3. //中断线程执行
  4. public void interrupt();

范例:观察线程中断操作

  1. package com.hanliukui.example;
  2. class MyThread implements Runnable {
  3. @Override
  4. public void run() {
  5. try {
  6. for (int i = 0; i < 100; i++) {
  7. Thread.sleep(1000);
  8. System.out.println(Thread.currentThread().getName()+"线程执行中...");
  9. }
  10. } catch (InterruptedException e) {
  11. e.printStackTrace();
  12. }
  13. }
  14. }
  15. public class ThreadDemo {
  16. public static void main(String[] args) {
  17. Thread thread = new Thread(new MyThread(),"a");
  18. // 开启线程a
  19. thread.start();
  20. // 执行3s后中断线程a
  21. try {
  22. Thread.sleep(3000);
  23. } catch (InterruptedException e) {
  24. e.printStackTrace();
  25. }
  26. if (!thread.isInterrupted()){
  27. System.out.println("线程a不是中断状态...");
  28. thread.interrupt();
  29. System.out.println(thread.isInterrupted());
  30. }
  31. }
  32. }

image.png
所有正在执行的线程都是可以被中断的中断线程必须进行异常的处理。

线程强制执行

所谓的线程的强制执行指的是当满足于某些条件之后,某一个线程对象可以一直独占资源一直到该线程的程序执行结束。

范例:观察一个没有强制执行的程序

  1. package com.hanliukui.example;
  2. class MyThread implements Runnable {
  3. @Override
  4. public void run() {
  5. try {
  6. for (int i = 0; i < 5; i++) {
  7. Thread.sleep(1000);
  8. System.out.println(Thread.currentThread().getName()+"执行...");
  9. }
  10. } catch (InterruptedException e) {
  11. e.printStackTrace();
  12. }
  13. }
  14. }
  15. public class ThreadDemo {
  16. public static void main(String[] args) {
  17. new Thread(new MyThread(),"子线程A").start();
  18. try {
  19. for (int i = 0; i < 5; i++) {
  20. Thread.sleep(1000);
  21. System.out.println(Thread.currentThread().getName()+"执行...");
  22. }
  23. } catch (InterruptedException e) {
  24. e.printStackTrace();
  25. }
  26. }
  27. }

image.png
这个时候的主线程和子线程都在基本交替执行着,但是如果说现在你希望主线程独占执行。

那么就可以利用 Thread 类中的方法

  1. // 强制执行,直至线程执行结束
  2. public final void join() throws InterruptedException;
  3. // 强制线程执行时长
  4. public final synchronized void join(long millis)
  5. throws InterruptedException;
  6. // 强制线程执行时长
  7. public final synchronized void join(long millis, int nanos)
  8. throws InterruptedException;
  1. package com.hanliukui.example;
  2. class MyThread implements Runnable {
  3. @Override
  4. public void run() {
  5. try {
  6. for (int i = 0; i < 5; i++) {
  7. Thread.sleep(1000);
  8. System.out.println(Thread.currentThread().getName()+"执行...");
  9. }
  10. } catch (InterruptedException e) {
  11. e.printStackTrace();
  12. }
  13. }
  14. }
  15. public class ThreadDemo {
  16. public static void main(String[] args) {
  17. Thread threadA = new Thread(new MyThread(), "子线程A");
  18. threadA.start();
  19. try {
  20. for (int i = 0; i < 5; i++) {
  21. if (i==1){
  22. // 让子线程A强制执行
  23. threadA.join();
  24. }
  25. Thread.sleep(1000);
  26. System.out.println(Thread.currentThread().getName()+"执行...");
  27. }
  28. } catch (InterruptedException e) {
  29. e.printStackTrace();
  30. }
  31. }
  32. }

image.png
在进行线程强制执行的时候一定要获取强制执行对象之后才可以执行 jion()调用。

线程的礼让

在多线程里面有各种各样的方法,其中有一个礼让的方法很有意思,现实生活中所谓的礼让,就是“委屈自己方便他人”。线程的礼让指的是将资源让出去让别的线程先执行。

线程的礼让可以使用 Thread 中提供的方法:

  1. // 礼让
  2. public static void yield()

特点:

  • 礼让线程,让当前正在执行的线程暂停,但不阻塞。
  • 将线程从运行状态转为就绪状态
  • 通过Thread.yield()函数实现;
  • 每一次调用 yield() 方法都只会礼让一次当前的资源;
  • 礼让不一定成功,要看CPU的调度,不如A礼让B,A可能会再次抢到CPU的资源

范例:使用礼让操作

  1. package com.hanliukui.example;
  2. class MyThread implements Runnable {
  3. @Override
  4. public void run() {
  5. for (int i = 0; i < 50; i++) {
  6. try {
  7. if (i % 3==0) {
  8. Thread.yield();
  9. System.out.println("线程A礼让..."+i);
  10. }
  11. Thread.sleep(1000);
  12. System.out.println("执行的是" + Thread.currentThread().getName()+i);
  13. } catch (InterruptedException e) {
  14. e.printStackTrace();
  15. }
  16. }
  17. }
  18. }
  19. public class ThreadDemo {
  20. public static void main(String[] args) {
  21. MyThread demo = new MyThread();
  22. Thread threadA = new Thread(demo, "线程A");
  23. threadA.start();
  24. try {
  25. for (int i = 0; i < 100; i++) {
  26. System.out.println("执行的是" + Thread.currentThread().getName());
  27. Thread.sleep(1000);
  28. }
  29. } catch (InterruptedException e) {
  30. e.printStackTrace();
  31. }
  32. }
  33. }

image.png

线程的优先级

什么是线程的优先级

在操作系统中,线程可以划分优先级,线程优先级越高,获得 CPU 时间片的概率就越大,但线程优先级的高低与线程的执行顺序并没有必然联系,优先级低的线程也有可能比优先级高的线程先执行。

设置线程的优先级

在Thread类里面针对于优先级的操作提供有如下的两个处理方法:

  1. //设置优先级
  2. public finial void setPriority(int newPriority);
  3. //获取优先级
  4. public final int getPriority()

线程的优先级分为 1-10 共10个等级,所有线程默认优先级为 5,如果优先级小于 1 或大于 10,则会抛出 java.lang.IllegalArgumentException 异常。

Java 提供了 3 个常量值可以用来定义优先级,源码如下:

  1. public final static int MIN_PRIORITY = 1;
  2. public final static int NORM_PRIORITY = 5;
  3. public final static int MAX_PRIORITY = 10;

线程优先级的继承特性

在 Java 中,线程的优先级具有继承性,如果线程 A 启动了线程 B,则线程 B 的优先级与线程 A 的优先级是一样的。
我们可以来举例说明,代码如下:

  1. public class ThreadDemo {
  2. public static void main(String[] args) {
  3. System.out.println("线程 main 的优先级(改变前):" + Thread.currentThread().getPriority());
  4. // Thread.currentThread().setPriority(9);
  5. // System.out.println("线程 main 的优先级(改变后):" + Thread.currentThread().getPriority());
  6. Thread thread = new Thread("A") {
  7. @Override
  8. public void run() {
  9. System.out.println("线程 A 的优先级:" + this.getPriority());
  10. }
  11. };
  12. thread.start();
  13. }
  14. }

控制台输出:

  1. 线程 main 的优先级(改变前):5
  2. 线程 A 的优先级:5

我们把注释去掉,再次运行代码,控制台输出如下:

  1. 线程 main 的优先级(改变前):5
  2. 线程 main 的优先级(改变后):9
  3. 线程 A 的优先级:9