当前位置: 首页 > news >正文

Java基础关键_028_线程(一)

目  录

一、概述

二、并发与并行

1.并发(concurrency)

2.并行(parallelism)

3.并发编程与并行编程

三、线程调度策略

 1.分时调度

2.抢占式调度

四、 实现线程的方式

1.继承 Thread 类

(1)步骤

(2)实例

 2.实现 Runnable 接口

(1)步骤

(2)实例

 五、常用方法

1.getName()

2.setName(String threadName)

3.currentThread()

六、生命周期状态

1.NEW(新建状态)

2.RUNNABLE(可运行状态)

3.BLOCKED(阻塞状态)

4.WAITING(等待状态)

5.TIMED_WAITING(超时等待状态)

6.TERMINATED(终止状态)

7.总结

 七、线程的休眠

1.说明

2.实例

3.问题

4.中断线程休眠

八、终止线程

1.stop方法(已过时)

2.标记法

 九、守护线程(后台线程)


一、概述

  1. 进程:操作系统中的一段程序,是一个正在执行中的程序实例,具有独立内存空间和系统资源,例如:文件、网络端口等。计算机程序执行时,先创建进程,再在进程中进行程序的执行。一般情况下,一个进程可以包含多个线程;
  2. 线程:进程中的一个执行单元,负责在进程中执行程序代码。Java 中每个线程都有自己的栈和程序计数器,并且可以共享进程资源。多个线程可以在同一时刻执行不同操作;
  3. 现在操作系统都是支持多进程的,一个软件就是一个进程,称为多进程并发;
  4. 一个进程可以启动多个线程,称为多线程并发;
  5. 多线程的优点:能够使 CPU 在处理一个任务时同时处理多个线程,如此可以提高 CPU 的利用率;
  6. JVM 规范:
    1. 堆内存、方法区 是线程共享的;
    2. 虚拟机栈、本地方法栈、程序计数器 是每个程序私有的。
  7. Java 程序执行原理
    1. 首先启动 JVM ,表示一个进程启动了;
    2. JVM 进程会先启动一个主线程,主线程负责调用 main 方法;
    3. 除主线程外,还启动一个 垃圾回收线程。因此,启动 JVM ,至少启动两个线程;
    4. 在 main 方法执行过程中,可以手动创建其他线程对象并启动。

二、并发与并行

1.并发(concurrency)

  1.  使用单核 CPU ,同一时刻只能有一条指令执行,但多个指令被快速轮换执行,看似有多个指令同时执行的效果,但实际上并不是同时执行的;
  2. 同一时间点,一个 CPU 只能支持一个线程执行。因为 CPU 运行速度快,使用抢占式调度模式在多个线程间高速切换,

2.并行(parallelism)

        使用多核 CPU ,同一时刻,有多条指令在多个 CPU 上同时执行。


3.并发编程与并行编程

  1. CPU 比较繁忙时,如果开启了多个线程,但只能为一个线程分配仅有的 CPU 资源,这些线程就会为自己抢占多的时间片。即通过多线程实现并发,线程之间会竞争 CPU 资源;

  2. 在 CPU 资源比较充足时,一个进程内的多个线程,可以被分配到不同 CPU 资源。即通过多线程实现并行

  3. 所写的多线程可能被分配到不同的 CPU 执行,也可能被分配到一个 CPU 内核执行,是操作系统进行分配的,所以,所写的多线程既可能是并发的,也可能是并行的

  4. 并发与并行都是为了提高 CPU 的利用率。


三、线程调度策略

        若多个线程被分配到一个 CPU 执行,则同一时刻只允许一个线程获得 CPU 执行权,那么进程中的多个线程就会抢夺 CPU 执行权,涉及到了线程的调度策略。

 1.分时调度

        所有线程轮流使用 CPU,并且平均分配每个线程占用 CPU 的时间


2.抢占式调度

        让优先级高的线程以较大概率优先获得 CPU 的执行权,若优先级相同,就会随机选择一个线程获得 CPU 的执行权,Java 采用的即是此种调度模型。


四、 实现线程的方式

1.继承 Thread 类

(1)步骤

  1. 编写一个类继承 java.lang.Thread 类;

  2. 重写 run 方法;

  3. new 一个线程对象;

  4. 调用线程对象的 start 方法来启动线程。


(2)实例

public class MyClassThread extends Thread {
    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            System.out.println("自定义线程执行第" + i + "次!");
        }
    }
}
public class ThreadTest {
    public static void main(String[] args) {
        MyClassThread thread = new MyClassThread();
        thread.start();
        for (int i = 1; i <= 10; i++) {
            System.out.println("主线程执行第" + i + "次");
        }
    }
}

        每次执行的结果都不一样,是由于抢占式调度模式。

        但是在 Java 程序中,方法体中的代码执行,永远是自上而下依次执行的,先执行 start 方法,再执行 main 方法里的循环。

        只不过是 start 方法执行瞬间就结束了, 启动了多线程。 


 2.实现 Runnable 接口

(1)步骤

  1. 编写一个类实现 java.lang.Runnable 接口;
  2. 实现 run 方法;
  3. new 一个线程对象;
  4. 调用线程对象的 start 方法启动线程。

(2)实例

public class MyClassThread implements Runnable {
    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            System.out.println("自定义线程执行第" + i + "次!");
        }
    }
}
public class ThreadTest {
    public static void main(String[] args) {
        Thread thread = new Thread(new MyClassThread());
        thread.start();
        for (int i = 1; i <= 10; i++) {
            System.out.println("主线程执行第" + i + "次");
        }
    }
}


 五、常用方法

1.getName()

        获取线程对象的名字。


2.setName(String threadName)

        修改线程名字。


3.currentThread()

        获取当前线程对象的引用。

public class MyClassThread extends Thread {
    @Override
    public void run() {
        Thread thread = Thread.currentThread();
        System.out.println("MyClassThread name: " + thread.getName());  // MyClassThread name: Thread-0
        thread.setName("MyClass");
        System.out.println("MyClassThread name: " + thread.getName());  // MyClassThread name: MyClass
    }
}
public class ThreadTest {
    public static void main(String[] args) {
        Thread thread = Thread.currentThread();
        System.out.println("mainThread name: " + thread.getName()); // mainThread name: main

        MyClassThread myClassThread = new MyClassThread();
        myClassThread.start();
    }
}

六、生命周期状态

1.NEW(新建状态)

        尚未启动的线程处于此状态。


2.RUNNABLE(可运行状态)

        在 Java 虚拟机中执行的线程处于此状态。又分为就绪状态和运行状态,线程大部分时间在这两个状态之间切换。

        就绪状态的线程有权抢夺 CPU 时间片,当抢夺到后开始执行 run 方法,run 方法开始执行标志着线程进入运行状态。

        在运行过程中,若 CPU 时间片用完,则再次回到就绪状态,继续抢夺 CPU 时间片,以此往复,直到 run 方法执行结束,进入终止状态。


3.BLOCKED(阻塞状态)

        被阻塞等待监视器锁定的线程处于此状态。

        遇到 synchronized 关键字之后,会去锁池中找共享对象的对象锁,此时会进入阻塞状态,线程放弃之前占有的时间片。

        当获取到共享对象的对象锁之后,线程重新回到就绪状态,继续抢占 CPU 时间片。


4.WAITING(等待状态)

        无限期等待另一个线程执行特定动作处于此状态。

        无时长限定,此时线程会放弃之前占有的 CPU 时间片,当解除等待时间后,线程再次进入就绪状态,继续抢夺 CPU 时间片。


5.TIMED_WAITING(超时等待状态)

        正在等待另一个线程执行动作的线程处于此状态。

        有时长限定,此时线程会放弃之前占有的 CPU 时间片,当等待时间到达后,线程再次进入就绪状态,继续抢夺 CPU 时间片。


6.TERMINATED(终止状态)

        已退出的线程处于此状态。


7.总结

        综上所述,线程的生命周期有 7 个状态:新建状态、就绪状态、运行状态、阻塞状态、等待状态、超时等待状态、终止状态。       

        线程在给定的时间点只能处于一种状态。 这些状态是虚拟机状态,不会反映任何作系统线程状态。 


 七、线程的休眠

1.说明

        sleep 方法,无返回值,参数单位是毫秒。

        该方法的作用是:让当前线程进入休眠,放弃占有的 CPU 时间片,让其进入阻塞状态。


2.实例

public class ThreadTest {
    public static void main(String[] args) {
        int gap = 1000;
        for (int i = 0; i < 10; i++) {
            System.out.println("间隔第" + (i + 1) + "个" + gap + "ms后执行");
            try {
                Thread.sleep(gap);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}


3.问题

        思考:下边的 sleep 方法是让哪一个线程进入休眠状态?

public class ThreadTest {
    public static void main(String[] args) {
        MyClassThread thread = new MyClassThread();
        thread.start();
        /**
         * 问题:当前的 sleep 方法是让哪一个线程进入休眠状态?
         */
        try {
            thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "运行" + i + "次!");
        }
    }
}
public class MyClassThread extends Thread {
    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            System.out.println(Thread.currentThread().getName() + "运行" + i + "次!");
        }
    }
}

        sleep 是让当前线程休眠,而 sleep 出现在哪一个线程,哪一个线程就是当前线程。

        所以,上边的 sleep 方法是让主线程休眠。执行效果便是快速执行 Thread-0 线程的循环,休眠 3 秒后,再执行 main 线程的循环。


4.中断线程休眠

public class MyClassThread extends Thread {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "开始执行");
        try {
            Thread.sleep(1000 * 60);    // 休眠1分钟
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "执行完毕");
    }
}
public class ThreadTest {
    public static void main(String[] args) {
        MyClassThread thread = new MyClassThread();
        thread.start();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        thread.interrupt(); // 中断thread线程的休眠
    }
}


八、终止线程

1.stop方法(已过时)

        从 Java 2 开始不建议使用,因为此方式是强行终止线程,容易导致数据丢失。在高版本 jdk 中,可能会出现抛出异常而不终止线程的情况。


2.标记法

public class MyClassThread extends Thread {
    boolean isRunning = true;   // 是否运行标记

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            if (isRunning) {
                System.out.println(Thread.currentThread().getName() + ":" + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            } else {
                return;
            }
        }
    }
}
public class ThreadTest {
    public static void main(String[] args) {
        MyClassThread thread = new MyClassThread();
        thread.start();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        thread.isRunning = false;
    }
}


 九、守护线程(后台线程)

        在 Java 中,线程分为用户线程和守护线程。JVM 当中有一个隐藏的守护线程,即 GC 线程。守护线程的特点:所有用户线程结束之后,守护线程自动结束

        通过 setDaemon(true) 方法,将该线程变为守护线程。

public class MyClassThread extends Thread {

    @Override
    public void run() {
        while (true) {
            System.out.println("我是守护线程,我在持续守护大家!");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
public class ThreadTest {
    public static void main(String[] args) {
        MyClassThread thread = new MyClassThread();
        thread.setDaemon(true); // 设置为守护线程
        thread.start();
        for (int i = 0; i < 10; i++) {
            System.out.println("我是用户线程演员" + i + "号");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

相关文章:

  • 公司做网站服务费怎样做账网站推广优化方法
  • 济南教育论坛网站建设厦门seo
  • 上海搬家公司排名前十名电话seo词库排行
  • 个人手机版网站建设南宁网络推广热线
  • 个人网站建设的国外文献综述青岛网站建设维护
  • 长沙网站seo推广公司seo提升排名
  • 3.24前端模拟面试
  • C语言基础系列【28】指针进阶1:深入理解指针
  • go test相关命令
  • 医院挂号预约小程序|基于微信小程序的医院挂号预约系统设计与实现(源码+数据库+文档)
  • Tomcat相关的面试题
  • T113-S3-启动报错tee_readfdt:433finenode/firmware/opteefailedwith FDT_ERR_NOTFOUND
  • SpringBoot分布式项目中MyBatis实战技巧:从配置到性能优化
  • 3、孪生网络/连体网络(Siamese Network)
  • 将 PDF 转换为 Word — 固定布局 vs 重排布局?
  • 团体协作项目总结Git
  • 23种设计模式-备忘录(Memento)设计模式
  • ubuntu解决蓝牙耳机已连接,但没有声音
  • docker安装flink
  • OpenCV 基础全方位剖析:夯实计算机视觉开发根基
  • Idea中诡异的文件编码问题: 设置fileCodeing为UTF8但不生效
  • R 基础语法
  • 六十天Linux从0到项目搭建(第五天)(file、bash 和 shell 的区别、目录权限、默认权限umask、粘滞位、使用系统自带的包管理工具)
  • 系统思考—看见未来
  • 黄土高原风蚀区解析多源数据融合与机器学习增强路径-RWEQ+集成技术在风蚀模数估算中的全流程增强策略—从数据融合到模型耦合的精细化操作指南
  • 轮回的起点与终点:一场跨越时空的哲学对话