专业的JAVA编程教程与资源

网站首页 > java教程 正文

快速掌握Java多线程-构造函数和方法

temp10 2024-10-17 16:21:20 java教程 15 ℃ 0 评论

目录

快速掌握Java多线程-Thread和Runnable

快速掌握Java多线程-构造函数和方法

快速掌握Java多线程-构造函数和方法

快速掌握Java多线程-线程同步

快速掌握Java多线程-线程间通信

快速掌握Java多线程-增强功能

Java 通过Thread类为并发编程提供了强大的支持。线程允许我们同时执行多个任务,从而通过有效利用CPU的处理能力来增强应用程序的性能。在本文中,我们将深入研究 Java 中Thread类提供的各种构造函数和方法。

Thread类的构造函数

1.Thread()

构造函数Thread()创建一个新的线程对象。但是如果我们使用此构造函数,则线程不会执行任何代码,直到我们调用其上的start()方法。

2.Thread(String name)

使用这个构造函数,我们可以创建一个线程并直接指定其名称。

3.Thread(Runnable target)

此构造函数允许我们传递一个Runnable对象作为线程的目标。当线程启动时,将调用Runnable对象的run()方法。

4.Thread(Runnable target, String name)

与上一个构造函数类似,这个构造函数也将一个Runnable对象作为目标。此外,我们还可以指定线程的名称,这对于调试和日志记录很有用。

5.Thread(ThreadGroup group, String name)

该构造函数允许我们指定一个线程组。线程组可用于组织和管理线程,但是一般很少用。

6.Thread(ThreadGroup group, Runnable target)

此构造函数允许我们为线程指定线程组以及运行的Runnable对象。

7.Thread(ThreadGroup group, Runnable target, String name)

与前面的构造函数类似,我们可以提供线程组、Runnable对象和线程名称。

8.Thread(ThreadGroup group, Runnable target, String name, long stackSize)

此构造函数允许我们指定线程组、Runnable对象、名称和线程的堆栈大小。

设置和获取线程的名称

Java中,每个线程都有一个名称,这个名称可以是JVM生成的默认名称,也可以是程序员提供的自定义名称。我们可以使用Thread类的以下两个方法获取和设置线程的名称:

  1. public final void setName(String name)
  2. public final String getName()

示例代码:

class MyThread extends Thread {
}

class Test {
  public static void main(String[] args) {
    System.out.println(Thread.currentThread().getName());  // main
    MyThread t = new MyThread();
    System.out.println(t.getName()); // Thread-0
    Thread.currentThread().setName("softAai Apps");
    System.out.println(Thread.currentThread().getName()); // softAai Apps
  }
}

如何获取当前正在执行的线程

我们可以使用Thread.currentThread()方法获取当前正在执行的线程对象。此方法返回对当前正在执行线程的Thread对象的引用。使用方法如下:

示例代码:

class MyThread extends Thread {
  public void run() {
    System.out.println("run method executed by Thread: " + Thread.currentThread().getName());
  }
}

class Test {
  public static void main(String[] args) {
    MyThread t = new MyThread();
    t.start();
    System.out.println("main method executed by thread: " + Thread.currentThread().getName());
  }
}

//main method executed by Thread: main
//run method executed by Thread: Thread-0

线程优先级

每个线程都有一个优先级,这个优先级可以是JVM生成的默认优先级,也可以是w我们自定义的优先级。线程优先级的有效范围为 1 到 10,其中 1 表示最低优先级,10 表示最高优先级。Thread类提供常量来表示一些标准优先级:

  • Thread.MIN_PRIORITY--> 1
  • Thread.NORM_PRIORITY--> 5
  • Thread.MAX_PRIORITY--> 10

以下是有效的线程优先级:

  • 0 → 无效
  • 1 → 有效
  • 10 → 有效
  • Thread.LOW_PRIORITY--> 无效
  • Thread.HIGH_PRIORITY--> 无效
  • Thread.MIN_PRIORITY--> 有效
  • Thread.NORM_PRIORITY--> 有效

如果尝试设置有效范围(1 到 10)之外的线程优先级,则会抛出IllegalArgumentException异常。

下面是设置线程优先级的示例:

Thread t = new Thread();
t.setPriority(7);  // Valid
t.setPriority(15); // Throws IllegalArgumentException

默认优先级

主线程的默认优先级是5。但是对于所有其他线程来说,包括我们创建的线程,默认优先级都是从父线程继承的。这意味着子线程将与其父线程具有相同的优先级。

示例代码:

class MyThread extends Thread {
}

class Test {
  public static void main(String[] args) {
    System.out.println(Thread.currentThread().getPriority());  // 5
    // Thread.currentThread().setPriority(15);  // RE: IllegalArgumentException
    // Thread.currentThread().setPriority(7);  // --> line 1
    MyThread t = new MyThread();
    System.out.println(t.getPriority());  // 5 (if line 1 is commented)
  }
}
  • Thread.currentThread().getPriority()返回主线程的优先级,默认为5。
  • 如果尝试将主线程的优先级设置为有效范围(1 到 10)之外的值,则会抛出IllegalArgumentException异常。
  • 当我们创建一个新MyThread对象时,它的优先级将从其父线程继承,在本例中是主线程。因此,t.getPriority()也将返回5。

请记住,如果注释掉设置主线程优先级的第 1 行,子线程仍将从其父线程继承默认优先级 5。

我们再来看一个例子:

class MyThread extends Thread {
  public void run() {
    for (int i = 0; i < 10; i++) {
      System.out.println("child thread");
    }
  }
}

class ThreadPriorityDemo {
  public static void main(String[] args) {
    MyThread t = new MyThread();
    // t.setPriority(10); // Uncomment to set child thread priority to 10
    t.start();

    for (int i = 0; i < 10; i++) {
      System.out.println("main thread");
    }
  }
}
  • 如果取消注释t.setPriority(10);,则将子线程 ( MyThread) 的优先级设置为 10,这是最高优先级。
  • 如果不设置优先级,主线程和子线程都将具有默认优先级5。在这种情况下,我们无法确定确切的执行顺序。
  • 如果子线程的优先级 (10) 高于主线程 (5),则子线程很可能会先获得 CPU 时间并在主线程之前执行其任务。
  • 但是,请务必注意,线程优先级行为在不同平台和 JVM 实现之间可能会有所不同。某些平台可能无法为线程优先级提供适当的支持,即使我们设置了优先级,也会导致不可预测的行为。

暂时阻止线程执行有哪些方式

yield()

yield()方法会导致当前正在执行的线程暂时暂停,以便为具有相同优先级的其他线程提供运行机会。如果没有等待线程或所有等待线程的优先级都较低,则同一线程可以继续执行。但是,如果多个线程以相同的优先级等待,则等待的线程接下来获得机会取决于线程调度程序。yield()方法对线程生命周期的影响在于,它暂时将线程置于可运行状态,允许其他线程运行,但yield线程恢复执行的确切时间取决于线程调度程序。

class MyThread extends Thread {
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("Child Thread");
            Thread.yield(); // Comment out to observe different behavior
        }
    }
}

class ThreadYieldDemo {
    public static void main(String[] args) {
        MyThread t = new MyThread();
        t.start();
        for (int i = 0; i < 10; i++) {
            System.out.println("main Thread");
        }
    }
}
  • 如果注释掉Thread.yield(),两个线程将同时执行,并且我们无法预测哪个线程将首先完成。
  • 如果包含Thread.yield(),子线程将频繁地让出处理器,从而为主线程提供更多执行机会。因此,主线程可能会更频繁地完成其任务。

join()

join()方法等待线程完成其执行。当一个线程调用join()另一个线程时,它会等待指定线程完成执行后再继续执行自己。join()方法有多个重载版本,允许我们指定等待时间的超时持续时间。

public final void join() throws InterruptedException
public final void join(long ms) throws InterruptedException
public final void join(long ms, int ns) throws InterruptedException

每个join()方法都会抛出InterruptedException异常。因此,有必要通过使用try-catch块或通过在方法签名中声明可能抛出的异常类型来处理。

示例代码:

class MyThread extends Thread {
  public void run() {
    for(int i=0; i<10; i++) {
      System.out.println("Seetha Thread");
      try {
        Thread.sleep(2000);
      } catch(InterruptedException e) {
      }
    }
  }
}
 
class ThreadJoinDemo {
  public static void main(String[] args) throws InterruptedException {
    MyThread t = new MyThread();
    t.start();
  
    t.join();     // Commented line 1
   
    for(int i=0; i<10; i++) {
      System.out.println("Rama Thread");
    }
  }
}
  • 如果注释掉t.join();,主线程和子线程都会同时执行,我们无法预测确切的输出。
  • 如果包含t.join();,主线程将调用子线程t对象join()方法,导致主线程等待,直到子线程完成其执行。因此,输出将由主线程和子线程的交替行组成,主线程等待子线程完成后再完成自己的执行。

让我们讨论另一种场景,子线程使用join()方法等待主线程完成

class MyThread extends Thread {
  static Thread mt;   // this line holds main thread object in main method
   
  public void run() {
    try {
      mt.join();
    } catch(InterruptedException e) {
    }
   
    for(int i=0; i<10; i++) {
      System.out.println("child thread");
    }
  }
}

class ThreadJoinDemo {
  public static void main(String[] args) throws InterruptedException {
    MyThread.mt = Thread.currentThread();
    MyThread t = new MyThread();
    t.start();

    for(int i=0; i<10; i++) {
      System.out.println("main thread");
      Thread.sleep(2000);
    }
  }
}
  • 在此示例中,子线程 ( MyThread) 调用主线程对象 (mt ) 上的join()方法。这会导致子线程等待,直到主线程完成执行。
  • 主线程由Thread.currentThread()获取,其引用存储在MyThread类的静态变量mt中。
  • 子线程启动后,主线程继续执行。它打印“主线程”十次,每个打印语句之间有 2 秒的延迟。
  • 同时,子线程由于join()方法而处于等待状态。一旦主线程完成执行,子线程就会恢复并打印“child thread”十次。
  • 这可确保子线程等待主线程完成后再继续执行。

join() 发生死锁的情况

情况一:主线程与子线程死锁:

  • 如果主线程在子线程对象调用join(),并且子线程同时在主线程对象调用join(),则两个线程将无限期地等待对方完成。这会导致死锁情况,两个线程都无法继续进行。

情况2:同一线程内死锁:

  • 如果一个线程自身调用join(),程序就会陷入死锁的情况。线程无限期地等待自身完成,而这种情况永远不会发生,从而导致程序被卡住。

sleep()

如果线程不想在特定时间内执行任何操作,则可以使用sleep()方法。

public static native void sleep(long ms) throws InterruptedException
public static void sleep(long ms, int ns) throws InterruptedException

示例代码:

class SlideRotator {
    public static void main(String[] args) throws InterruptedException {
        for (int i = 1; i <= 10; i++) {
            System.out.println("Slide-" + i);
            Thread.sleep(5000);
        }
    }
}

在上面的示例中,模拟了线程在每张幻灯片显示后暂停 5000 毫秒(5 秒)。这模拟了幻灯片旋转过程,其中每张幻灯片显示 5 秒,然后再移至下一张。

interrupt()

线程可以通过调用线程对象上的interrupt()方法来中断睡眠或等待线程。

class MyThread extends Thread {
    public void run() {
        try {
            for (int i = 0; i < 10; i++) {
                System.out.println("I am lazy thread");
                Thread.sleep(2000);
            }
        } catch (InterruptedException e) {
            System.out.println("I got interrupted");
        }
    }
}

class ThreadInterruptedDemo {
    public static void main(String[] args) {
        MyThread t = new MyThread();
        t.start();
        t.interrupt(); // Interrupt the child thread
        System.out.println("End of main thread");
    }
}
  • 在此示例中,main线程通过MyThread调用interrupt()方法来中断该线程。
  • 如果第 t.interrupt()行被注释掉,主线程将不会中断子线程。在这种情况下,子线程不间断地执行for循环10次。
  • 如果第t.interrupt()行没有被注释掉,主线程会中断子线程。其结果是子线程抛出一个InterruptedException异常,并打印消息“I got interrupted”。
  • 无论子线程是否被中断,main线程执行结束时都会打印“End of main thread”消息。

现在,让我们讨论当调用interrupt()方法时线程不处于休眠或等待状态时会发送什么:

  • 如果调用interrupt()时线程未处于睡眠或等待状态,则不会立即产生影响。
  • 中断调用将排队,直到目标线程进入睡眠或等待状态。
  • 一旦目标线程进入睡眠或等待状态,中断调用将立即中断该线程。
  • 如果目标线程在其整个生命周期中从未进入睡眠或等待状态,则中断调用将不起作用并且将被浪费。
class MyThread extends Thread {
    public void run() {
        for (int i = 0; i <= 10000; i++) {
            System.out.println("I am lazy thread-" + i);
        }
        System.out.println("I am entering into sleeping state");
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            System.out.println("I got interrupted");
        }
    }
}

class ThreadSleepDemo1 {
    public static void main(String[] args) {
        MyThread t = new MyThread();
        t.start();
        t.interrupt(); 
        System.out.println("End of main thread");
    }
}
  • 在这个例子中,main线程启动一个MyThread线程并立即调用interrupt()方法。
  • 由于MyThread线程正在执行循环而不处于睡眠或等待状态,因此中断调用不会立即生效。
  • 中断调用将排队,直到MyThread线程进入睡眠或等待状态。
  • 在这个例子中,MyThread线程最终执行到Thread.sleep(10000)时进入睡眠状态,中断调用立即执行并中断它,并且导致它抛出一个InterruptedException异常.
  • 如果MyThread线程从未进入睡眠或等待状态,则中断调用在其整个执行过程中不会产生任何影响。

yield()、join()和sleep()方法之间的区别

yield()方法

  • 如果一个线程想要暂停执行,为剩余的相同优先级线程提供机会,可以使用yield()方法。
  • yield()方法没有重载。
  • yield()方法没有final关键字。
  • yield()方法不会抛出InterruptedException异常。
  • yield()方法是原生的。
  • yield()方法是静态方法。

join()方法

  • 如果一个线程想要等待其他线程完成才继续执行,则可以使用join()方法。
  • join()有方法重载。
  • join()方法有final关键字。
  • join()方法会抛出InterruptedException异常。
  • join()方法不是原生的。
  • join()方法不是静态方法。

sleep()方法

  • 如果线程不想在特定时间内执行任何操作,则可以使用sleep()方法。
  • sleep()有方法重载。
  • sleep()方法不是final关键字。
  • sleep()方法会抛出InterruptedException异常。
  • sleep(long ms)方法是原生的,sleep(long ms, int ns)方法是非原生的。
  • sleep()方法是静态方法。

目录

快速掌握Java多线程-Thread和Runnable

快速掌握Java多线程-构造函数和方法

快速掌握Java多线程-线程同步

快速掌握Java多线程-线程间通信

快速掌握Java多线程-增强功能

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表