【4-5】 《Java中多线程重点》——继承Thread、实现Runnable、死锁、线程池、Lambda表达式

本文详细介绍了Java中实现多线程的四种方式,包括继承Thread、实现Runnable以及避免死锁的方法。还讨论了线程安全问题,通过同步代码块、同步方法和显式锁Lock确保线程安全。此外,文章还涉及线程池的使用和Lambda表达式在多线程中的应用。
摘要由CSDN通过智能技术生成

多线程

多线程:栈空间独立,堆内存共享。

一、线程与进程

  • 进程

正在运行的应用程序:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间

每个进程都有着自己独立的堆、栈且是互不共享的

  • 线程

    • 进程中的一个执行路径(一个应用程序从执行到结束的整个过程),共享一个内存空间,一个进程中可以包含多条线程;
    • 线程之间可以自由切换,并发执行;
    • 一个进程最少有一个线程,线程控制着进程。

二、线程调度

  1. 分时调度

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

  2. 抢占式调度

    优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性), Java使用的为 抢占式调度。

  • CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核新而言,某个时刻, 只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是在同一时刻运行。 其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高。

三、同步与异步&并发与并行

同步:排队执行 , 效率低但是安全

异步:同时执行 , 效率高但是数据不安全

并发:指两个任务在同一时间段内都请求运行

处理器只能接收一个任务,把这两个任务安排轮流进行,由于时间间隔较短,使人感觉两个任务都在运行

  • 如果用一台电脑先给友人A发个消息,然后立刻再给友人B发消息,然后再跟友人A聊,再跟友人B聊。这就叫并发。

并行:两个任务同一时刻运行,需要多核CPU

  • 比如跟两个朋友聊天,左手操作一个电脑跟甲聊,同时右手用另一台电脑跟乙聊天,这就叫并行。

四、多线程实现方式

1、继承Thread

步骤:

  1. 定义MyThread类并继承Thread类;
  2. 重写run方法,新的执行路径(通过thread对象的start()启动任务)
/**
 * @author Elvira
 * @date 2020/10/12 16:01
 * @description 继承Thread
 */
public class MyThread extends Thread{
   

    @Override
    public void run() {
   
        for (int i = 0; i < 10; i++) {
   
            System.out.println("嘿……"+i);
        }
    }
}
  1. 创建子类对象

  2. 调用start(),让线程执行

/**
 * @author Elvira
 * @date 2020/10/12 16:08
 * @description 继承Thread的程序入口
 */
public class ThreadTest {
   
    public static void main(String[] args) {
   
        MyThread m = new MyThread();
        m.start();
        for (int i = 0; i < 10; i++) {
   
            System.out.println("嘻嘻……"
            +i);
        }
    }
}
  • 运行结果:可以看到顺序并不统一,这是因为线程在抢占时间片,谁先抢到就是谁的
    在这里插入图片描述

线程时序图:

在这里插入图片描述

注意:运行过程中子线程任务中调用的方法都在子线程中运行

2、实现Runnable

步骤:

  1. 实现Runnable接口;
  2. 实现抽象方法run(),编写线程的任务
/**
 * @author Elvira
 * @date 2020/10/12 16:22
 * @description 实现Runnable
 */
public class MyRunnable implements Runnable{
   

    @Override
    public void run() {
   
        for (int i = 0; i < 10; i++) {
   
            System.out.println("哼哼!" + i);
        }
     }
}
  1. 创建一个任务对象

  2. 创建一个线程,并给它一个任务

  3. 调用start()方法执行线程

/**
 * @author Elvira
 * @date 2020/10/12 16:30
 * @description 实现Runnable的程序入口
 */
public class RunnableTest {
   
    public static void main(String[] args) {
   
        MyRunnable r = new MyRunnable();
        Thread t = new Thread(r);
        t.start();
        for (int i = 0; i < 10; i++) {
   
            System.out.println("呵呵?" + i);
        }
    }
}
  • 运行结果:
    在这里插入图片描述

3、实现Runnable和继承Thread比较

继承Thread

  • 优点:直接使用Thread类中的方法,代码简单
  • 弊端:如果已有父类,不可用

实现Runnable接口(更常用)

  • 其优点:

    ​ 1. 通过创建任务,给线程分配任务实现多线程,更适合多个线程同时执行相同任务的情况

    1. 可以避免单继承带来的局限性

    2. 任务和线程分离,提高程序健壮性

    3. 最重要)后续提供的线程池任务,接收Runnable类型任务,不接收Thread类型线程

  • 弊端:不能直接使用Thread中的方法,需先获取线程对象,再得到Thread的方法,代码复杂

4、匿名内部类实现方式

  1. 继承Thread
/**
 * @author Elvira
 * @date 2020/10/12 16:52
 * @description 继承thread的匿名内部类实现方式
 */
public class MyThread0 {
   
    public static void main(String[] args) {
   
        new Thread() {
   
            public void run() {
   
                for (int i = 0; i < 10; i++) {
   
                    System.out.println("一二三四五" + i);
                }
            }
        }.start();
        for (int i = 0; i < 10; i++) {
   
            System.out.println("啦啦啦啦啦" + i);
        }
    }
}
  • 运行结果:

在这里插入图片描述

  1. 实现Runnable
new Thread(new Runnable() {
   				
    public void run() {
   			   
        for(int i = 0; i < 5; i++) {
   	
            System.out.println("aaa" + i);
        }
    }
}).start(); 
for (int i = 0; i < 5; i++) {
   
    System.out.println("bbb" + i);
}
  • 运行结果:
    在这里插入图片描述

五、Thread类

接下来列举API中Thread类最常用的方法:

构造器 描述
Thread() 分配新的 Thread对象。
Thread(Runnable target) 分配新的 Thread对象。
Thread(Runnable target, String name) 分配新的 Thread对象。
Thread(String name) 分配新的 Thread对象。

常见方法:

变量和类型 方法 描述
long getId() 返回此Thread的标识符。
String getName() 返回此线程的名称。
int getPriority() 返回此线程的优先级。
void setPriority(int newPriority) 更改此线程的优先级。
void start() 导致此线程开始执行; Java虚拟机调用此线程的run方法。
static void sleep(long millis) 导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数,具体取决于系统计时器和调度程序的精度和准确性。
static void sleep(long millis, int nanos) 导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数加上指定的纳秒数,具体取决于系统定时器和调度程序的精度和准确性。
void setDaemon(boolean on) 将此线程标记为 daemon线程或用户线程。

特殊字段:控制线程抢到时间片的几率

变量和类型 字段 描述
static int MAX_PRIORITY 线程可以拥有的最大优先级。
static int MIN_PRIORITY 线程可以拥有的最低优先级。
static int NORM_PRIORITY 分配给线程的默认优先级。

六、设置和获取线程名称

首先需要了解:currentThread() 可以获取当前正在执行的线程对象

  1. 获取线程名称
/**
 * @author Elvira
 * @date 2020/10/12 17:41
 * @description 获取线程名称
 */
public class NameTest {
   
    public static void main(String[] args) {
   
        System.out.println(Thread.currentThread().getName());
        //给线程指定一个名称
        new Thread(new MyRunnable(), "Elvira").start();
    }
}

实现类:

public class MyRunnable implements Runnable {
      
    @Override
    public void run() {
   
 		System.out.println(Thread.currentThread().getName());
    }
}
  • 运行结果:
    在这里插入图片描述

如果不指定线程名称,直接给线程一个任务:再加两句

new Thread(new MyRunnable()).start();
new Thread(new MyRunnable()).start();
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值