java学习笔记:多线程

  • Post author:
  • Post category:java


一、概述

1、线程

线程是CPU的调度与分配最小单位,它是比进程更小的能独立运行的基本单位,一个进程中可以包含多个线程,但至少包含一个主线程。每个进程执行前,操作系统都会为其分配所需的资源,所有线程共享进程的资源,各个线程也可以拥有属于自己的私有资源

进程仅负责为各个线程提供所需的资源,真正执行任务的是线程,而不是进程。

2、多线程

所谓多线程,即一个进程中拥有多(≥2)个线程,线程之间相互协作、共同执行一个应用程序。在操作系统中,有很多种调度方式,在Java中采用的是抢占式调度。每个线程都拥有一个优先级,优先级高的使用CPU几率更大,如果优先级相同,则随机选择执行。

(1) CPU 使用抢占式调度模式在多个线程间进行着高速的切换

(2) 对于 CPU 的一个核而言,某个时刻只能执行一个线程,而 CPU 在多个线程间切换速度相对我们的感觉要快,看上去就是在同一时刻执行

(3) 多线程程序并不能提高程序的运行速度,但能提高程序运行效率,让 CPU 的使用率更高

二、线程的实现

在jdk.1.5之前: 创建线程的方式: 两种:

  • 继承Thread类

  • 实现Runnable接口

在jdk1.5之后: 多加了两种:

  • 实现 Callable接口

  • 线程池

1、继承Thread类

  1. 定义一个类继承 Thread 类

  2. 重写 run 方法

  3. 创建子类对象,即创建线程对象

  4. 调用 star 方法,开启线程并让线程执行,同时还会告诉 JVM 去调用 run 方法

自定义线程类:

public class MyThread extends Thread{
    @Override
    public void run() {
        for(int i = 0;i < 20;i++)
        {
            System.out.println("新线程正在执行" + i);
        }
    }
}

主线程:

public static void main(String[] args) {
    MyThread mt = new MyThread();
    mt.start();
    for(int i = 0;i < 20;i++)
    {
        System.out.println("main线程正在执行" + i);
    }
}
  • 创建新的线程后,会产生两个执行路径,都会被CPU执行,CPU有自己的选择执行权力,所以会出现随机的执行结果

  • 可以理解为两个线程在抢夺CPU的资源(时间)

  • 注:线程对象调用 run 方法和调用 star 方法的区别: (1) 线程对象调用 run 方法不开启线程,仅仅是对象调用方法 (2) 线程对象调用 start 方法开启线程,并让 JVM 调用 run 方法在开启的线程中执行

2、实现Runnable接口

  1. 定义类实现 Runnable 接口

  2. 重写接口中的 run 方法

  3. 创建 Thread 类的方法

  4. 将 Runnable 接口的子类对象作为参数传递给 Thread 类的构造函数

  5. 调用 Thread 类的 start 方法开启线程

Runnable 实现类:

public class MyThread implements Runnable{
    @Override
    public void run() {
        for(int i = 0;i < 20;i++)
        {
            System.out.println("新线程正在执行" + i);
        }
    }
}

主线程:

public static void main(String[] args) {
    MyThread mt = new MyThread();
    Thread t1 = new Thread(mt);
    Thread t2 = new Thread(mt);
    t1.start();
    t2.start();
    for(int i = 0;i < 20;i++)
    {
        System.out.println("main线程正在执行" + i);
    }
}


实现 Runnable 接口的好处:

  • 避免了单继承的局限性,所以此方法较为常用

  • 将线程分为两部分,一部分线程对象,一部分线程任务,更加符合面向对象思想

  • 将线程任务单独分离出来封装成对象,类型就是 Runnable 接口类型

3、实现Callable接口

  1. 定义类实现 Callable 接口

  2. 重写接口中的 call 方法

  3. 创建 Callable 接口实现类对象

  4. 创建一个 FutureTask 对象, 传递 Callable 接口实现类对象

  5. 创建一个 Thread 对象,传递FutureTask 的对象,并调用 start 方法开启线程

  6. 调用 FutureTask 提供的 get 方法获取返回值

Callable 实现类:

public class MyThread implements Callable {
    @Override
    public Object call() throws Exception {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add(i);
        }
        return list;
    }
}

主线程:

public static void main(String[] args) throws Exception {
    MyThread mt = new MyThread();
    FutureTask futureTask = new FutureTask<>(mt);
    new Thread(futureTask).start();
    List<Integer> list = (List<Integer>) futureTask.get();
    for (Integer integer : list) {
        System.out.print(integer + "  ");
    }
}

三、线程的常用方法

  • Thread.currentThread():获取当前线程对象

  • Thread.currentThread().getName():获取当前线程对象的名称

  • setName():设置线程名称

  • Thread.sleep(毫秒数),让线程休眠,时间到,自动唤醒并继续执行

    优先级:可以给线程设置优先级,范围是1-10,10优先级最高

  • setPriority(数字):给线程设置优先级,这里优先级高,并不代表一定先执行,只是增加了抢占到资源的机会

  • getPriority():获取线程的优先级

  • yield():当前线程暂停执行,与其他线程同时抢占资源,如果还是自己抢占到,则继续执行后续的代码,如果是其他线程抢占到,则其他线程先执行

  • join():当前线程暂停执行,新加入的线程开始执行,当新线程执行完之后,再执行当前线程



版权声明:本文为m0_72087285原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。