1、线程的创建和启动

  • Java语言的JVM允许程序运行多个线程,它通过java.lang.Thread类来体现。
  • Thread类的特性

    • 每个线程都是通过某个特定Thread对象的run()方法来完成操作的,经常把run()方法的主体称为线程体
    • 通过该Thread对象的start()方法来启动这个线程,而非直接调用run()

      2、Thread类

  • Thread():创建新的Thread对象

  • Thread(String threadname):创建线程并指定线程实例名
  • Thread(Runnabletarget):指定创建线程的目标对象,它实现了Runnable接口中的run方法
  • Thread(Runnable target, String name):创建新的Thread对象

    3、API创建线程的两种方式

  • JDK1.5之前创建新执行线程有两种方法:

    • 继承Thread类的方式
    • 实现Runnable接口的方式

      3.1、创建多线程的方式一:继承Thread类

  1. 创建一个继承于Thread类的子类
  2. 重写Thread的run()方法 —-> 将此线程的方法声明在run()方法中
  3. 创建Thread的子类对象
  4. 通过此对象调用start() ```java package com.haiyang.java;

/**

  • 多线程的创建 方式一: 继承于 Thread类
  • 1、创建一个继承于Thread类的子类
  • 2、重写Thread类的run()方法
  • 3、创建Thread类的子类对象
  • 4、通过此对象调用start() *
  • 例子: 遍历100以内的所有偶数 */ public class ThreadTest { public static void main(String[] args) {
    1. //3、创建Thread子类的对象
    2. MyThread myThread = new MyThread();
    3. //4、调用start()方法
    4. myThread.start();
    }

} //1、创建一个继承Thread类的子类 class MyThread extends Thread{ //2、重写Thread类的run()方法

  1. @Override
  2. public void run() {
  3. for (int i = 0; i < 100; i++) {
  4. if (i % 2 == 0){
  5. System.out.println(i);
  6. }
  7. }
  8. }

}

  1. <a name="BaQNW"></a>
  2. ### 3.1.1、Thread中常用的方法
  3. - **start()**: 启动当前线程,调用当前线程的run()方法
  4. - **run()**:通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
  5. - **currentThread()**:静态方法,返回执行当前代码的线程
  6. - **getName()**:获取当前线程的名字
  7. - **setName()**:设置当前线程的名字
  8. - **yield() **:释放当前cpu的执行权
  9. - **join()**: 在线程a中调用线程b的join(),此时线程a就会进入阻塞状态,直到线程b完全执行完成以后,线程a才结束阻塞状态
  10. - **stop()**: 已过时 当执行此方法时强制线程结束
  11. - **sleep(long millitime) **: 让当前线程“睡眠"指定的millitime毫秒,在指定睡眠的时间内,当前线程处于阻塞状态
  12. - **isAlive() **: 判断当前线程是否存活
  13. <br />
  14. ```java
  15. /**
  16. *
  17. * 测试 Thread中常用的方法
  18. * 1、start(): 启动当前线程,调用当前线程的run()方法
  19. * 2、run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
  20. * 3、currentThread():静态方法,返回执行当前代码的线程
  21. * 4、getName():获取当前线程的名字
  22. * 5、setName():设置当前线程的名字
  23. * 6、yield() :释放当前cpu的执行权
  24. * 7、join(): 在线程a中调用线程b的join(),此时线程a就会进入阻塞状态,直到线程b完全执行完成以后,线程a才结束阻塞状态
  25. * 8、stop(): 已过时 当执行此方法时强制线程结束
  26. * 9、sleep(long millitime) : 让当前线程“睡眠"指定的millitime毫秒,在指定睡眠的时间内,当前线程处于阻塞状态
  27. * 10、isAlive() : 判断当前线程是否存活
  28. * @author 杨磊
  29. * @create 2021-09-24 20:12
  30. */
  31. public class ThreadMethodTest {
  32. public static void main(String[] args) {
  33. //通过构造器给线程取名
  34. HelloThread ht = new HelloThread("Thread:1");
  35. //ht.setName("线程1");
  36. //给主线程命名
  37. Thread.currentThread().setName("主线程");
  38. ht.start();
  39. for (int i = 0; i < 100; i++) {
  40. if (i % 2 == 0){
  41. System.out.println(Thread.currentThread().getName() + ":" + i);
  42. //yield(); 释放线程的执行权
  43. }
  44. if (i == 20){
  45. try {
  46. ht.join();
  47. } catch (InterruptedException e) {
  48. e.printStackTrace();
  49. }
  50. }
  51. }
  52. }
  53. }
  54. class HelloThread extends Thread{
  55. public HelloThread(String name){
  56. super(name);
  57. }
  58. @Override
  59. public void run() {
  60. for (int i = 0; i < 100; i++) {
  61. try {
  62. sleep(1000);
  63. } catch (InterruptedException e) {
  64. e.printStackTrace();
  65. }
  66. System.out.println(Thread.currentThread().getName() + ":" + i);
  67. //yield(); 释放线程的执行权
  68. }
  69. }
  70. }

3.1.2、线程的调度

  • 调度策略
    • 时间片

image.png

  • 抢占式:高优先级的线程抢占CPU

  • Java的调度方法

    • 同优先级线程组成先进先出队列(先到先服务),使用时间片策略
    • 对高优先级,使用优先调度的抢占式策略

      3.1.2.1、线程的优先级

  • MAX_PRIORITY:10 —->最高优先级

  • MIN _PRIORITY:1 —->最低优先级
  • NORM_PRIORITY:5 —->默认优先级

    3.1.2.2、涉及到的方法

  • getPriority() :返回线程优先值

  • setPriority(intnewPriority) :改变线程的优先级

    说明:

  • 高优先级的线程要抢占低优先级线程cpu的执行权。

  • 但是只是从概率上讲,高优先级的线程高概率的情况下被执行。
  • 并不意味着只有当高优先级的线程执行完以后,低优先级的线程才会被执行。

    3.2、创建多线程的方式二:

  • 创建一个实现了Runnable接口的类

  • 实现类去实现Runnable中的接口方法
  • 创建实现类对象
  • 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
  • 通过Thread类的对象调用start() ```java package com.haiyang.java;

/**

  • 创建多线程的方式二 : 实现Runnable接口
  • 1、创建一个实现了Runnable接口的类
  • 2、实现类去实现Runnable中的接口方法
  • 3、创建实现类对象
  • 4、将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
  • 5、通过Thread类的对象调用start()
  • @author 杨磊
  • @create 2021-09-24 22:04 */ //1、创建一个实现了Runnable接口的类 class MThread implements Runnable{ //2、实现类去实现Runnable中的接口方法 @Override public void run() {
    1. for (int i = 0; i < 100; i++) {
    2. if (i % 2 == 0 ){
    3. System.out.println(i);
    4. }
    5. }
    } } public class ThreadTest1 { public static void main(String[] args) {
    1. //3、创建实现类对象
    2. MThread mThread = new MThread();
    3. //4、将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
    4. Thread th = new Thread(mThread);
    5. //5、通过Thread类的对象调用start()
    6. th.start();
    7. Thread th2 = new Thread(mThread);
    8. th2.start();
    } }
  1. <a name="zJqqh"></a>
  2. ## 3.3、继承方式和实现方式的联系与区别
  3. 开发中:优先选择:实现Runnable接口的方式<br />原因:
  4. - 实现的方式没有类的单继承性的局限性
  5. - 实现的方式更适合来处理多个线程有共享数据的情况。
  6. 联系:public class Thread implements Runnable<br />相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中
  7. <a name="pJWNH"></a>
  8. # 4、补充:线程的分类
  9. **Java中的线程分为两类:一种是守护线程,一种是用户线程。**
  10. - 它们在几乎每个方面都是相同的,唯一的区别是判断JVM何时离开。
  11. - 守护线程是用来服务用户线程的,通过在start()方法前调用**thread.setDaemon(true)**可以把一个用户线程变成一个守护线程。
  12. - Java垃圾回收就是一个典型的守护线程
  13. - 若JVM中都是守护线程,当前JVM将退出。
  14. - 形象理解:兔死狗烹,鸟尽弓藏
  15. <a name="s3DG4"></a>
  16. # 练习1
  17. ```java
  18. /**
  19. * 例子: 创建三个窗口去买票,总票数为100张
  20. * @author 杨磊
  21. * @create 2021-09-24 21:36
  22. */
  23. class Window extends Thread{
  24. private static int ticket = 100;
  25. @Override
  26. public void run() {
  27. while (true){
  28. if (ticket > 0){
  29. System.out.println(getName() + "卖票,票号为:" + ticket);
  30. ticket--;
  31. }else {
  32. System.out.println("不好意思,已经没有票了");
  33. break;
  34. }
  35. }
  36. }
  37. }
  38. public class WindowTest {
  39. public static void main(String[] args) {
  40. Window w1 = new Window();
  41. Window w2 = new Window();
  42. Window w3 = new Window();
  43. w1.setName("窗口1");
  44. w2.setName("窗口2");
  45. w3.setName("窗口3");
  46. w1.start();
  47. w2.start();
  48. w3.start();
  49. }
  50. }