栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

多线程基础

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

多线程基础

进程和线程 进程:程序在系统中的一次执行过程

进程是os资源分配的基本单位

线程:进程中的一个独立的子任务

打卡Chrome浏览器–>打开一个Chrome进程

在浏览器上可以同时下载视频,同时听音乐,下载任务就是一个线程,听音乐就是另一个线程

同一个进程的所有线程共享进程的资源,线程是操作系统调度的基本单位

进程与线程的区别
  • 进程是os资源分配的基本单位,线程是os系统调度的基本单位
  • 创建和销毁进程的开销远比创建和销毁线程大得多,线程更轻量化
  • 调度一个线程也远比调度一个进程快得多
  • 进程包含线程,每一个进程至少包含一个线程(主线程)
  • 进程之间彼此相对独立,不同的进程不会共享内存空间,同一个进程的线程共享内存空间
JAVA中描述线程对象的类-java.lang.Thread

我们程序的入口是main方法,所有的调用都是从主方法开始的,所以相同的,所有的线程任务也是从主方法(主线程)开始进行。

Java中描述线程的类是Thread类——java.lang.Thread类线程的核心类,都是通过Thread类来启动一个线程。

当我们覆写完线程子类的run方法,在主方法中调用 线程对象.start方法来启动线程。

使用jconsole可以观察线程状态,用于对比各线程

run方法和start方法的区别 run方法

run方法就是每个线程的核心执行流程方法,每个线程都是从run方法开始执行代码。

run方法是线程类的工作任务,也就是说,run方法决定了线程启动后要干什么,当run方法执行完毕(JVM启动run方法),线程会进入销毁状态。

start方法

start方法是Thread类中启动线程的方法,只有线程对象调用start方法时才会被系统调度,进入运行状态。

启动线程调用start方法,线程启动之后由JVM自动执行每个线程的run方法

调用start方法,才真正在操作系统底层创建出一个线程

JAVA创建线程的四种方式
  • 继承Thread类,覆写run方法
  • 覆写Runnable接口,覆写run方法
  • 覆写Callable接口,覆写call方法
  • 使用线程池创建线程
继承Thread类,覆写run方法
public class ThreadMethod extends Thread {
    @Override
    public void run(){
        System.out.println("继承Thread类,覆写run方法");
        
    }
}
Thread t1=new Thread(){
    @Override
    public void run(){
        System.out.println("匿名内部类实现Thread子类对象");
    }
};
覆写Runnable接口,覆写run方法
public class RunnableMethod implements Runnable{

    @Override
    public void run() {
        System.out.println("这是Runnable方式实现的子线程任务");
    }
}
Thread t2=new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("匿名内部类是实现Runnable子类对象");
    }
});
Thread t3=new Thread(() -> System.out.println("Lambda表达式实现Runnable子类对象"));
覆写Callable接口,覆写call方法

##使用线程池创建线程

Thread类的常见方法 1.构造方法

传入Runnable接口子类的构造方法

Thread t2=new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("匿名内部类是实现Runnable子类对象");
    }
});
2.Thread类的核心属性

3.启动线程调用Thread类的start方法
Thread t2=new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("匿名内部类是实现Runnable子类对象");
    }
});
t2.start();
为什么我们调用start()方法会执行run()方法,为什么我们不能直接调用run()方法?
  • new一个Thread对象,线程进入了新建状态;调用start()会执行线程的相应准备工作,然后JVM自动执行run()方法内容。(调用start方法,会启动一个线程并⑩线程进入就绪状态,当分配到时间片后就开始运行了。)这是真正的多线程工作。

  • 直接执行run()方法,会把run方法当场一个main线程下的普通方法区执行,并不会在某个线程中执行它,这并不是多线程工作。

    调用start方法可启动线程并使线程进入就绪态,而run方法只是thread的一个普通方法调用,还在主线程里执行

4.中断线程 => 线程间通信

中断一个正在执行的线程(run方法还没有执行结束);

普通线程会在run方法执行结束之后自动停止

中断线程的两种方式

通过共享变量进行中断

使用Thread.interrupted()静态方法 或 Thread对象的成员方法isInterrupted(),Thread类的内部包含了一个属性,当前线程是否被中断属性

注意:Thread类的所有静态方法都是在哪个线程中调用的,就生效在哪个线程

线程收到设置的中断通知有两种方式
  1. 当线程调用sleep/wait/join等方法处在阻塞状态时,收到中断通知thread.interrupt()就会抛出一个中断异常 InterruptedException,

    当抛出异常后,当前线程的中断状态就会被清除

  2. 线程没有调用以上三种方法时,处在正常运行状态,收到中断通知thread.interrupt()

  • Thread.interrupted():判断当前线程是否被中断,若中断状态为true,清楚中断状态

  • Thread.currentThread.isInterrupted():判断指定线程对象是否为中断状态,若状态为true,不会清除中断状态

5.等待另一个线程-join方法

有时候我们需要等待一个线程完成他的工作后,才能进行自己的下一步工作。

在哪个线程调用别的线程对象的join方法,意识就是这个线程要等待另一个线程执行完毕再继续执行本线程的后续代码。

eg:主线程调用thread.join():主线程就会进入阻塞状态,直到thread1执行结束,主线程才会继续向后执行

6.获取当前正在执行的对象

Thread.cunrrentThread()=>获取正在执行的对象

7.休眠当前线程 -sleep

Thread.sleep(long millis):在哪调用,就休眠哪个线程

8.wait和notify方法
  • wait()/wait(long timeout):让当前线程进入等待状态
  • notify()/notifyAll():唤醒在当前对象上等待的线程

注意:wait,notify,notifyAll都是Object类的方法,用于线程的等待与唤醒,必须打牌synchroniezd锁来使用(等待和唤醒,也是需要对象)

wait方法

调用wait方法的前提时首先要获取该对象的锁,调用wait方法会释放锁,本线程进入等待队列等待被唤醒,被唤醒之后不是立即恢复执行,进入阻塞队列,竞争锁

wait 要搭配 synchronized 来使用. 脱离 synchronized 使用 wait 会直接抛出异常

wait 结束等待的条件:

  • 其他线程调用该对象的 notify 方法.
  • wait 等待时间超时 (wait 方法提供一个带有 timeout 参数的版本, 来指定等待时间).
  • 其他线程调用该等待线程的 interrupted 方法, 导致 wait 抛出 InterruptedException 异常.
notify()方法

notify 方法是唤醒等待的线程.

  • 方法notify()也要在同步方法或同步块中调用,该方法是用来通知那些可能等待该对象的对象锁的
  • 其它线程,对其发出通知notify,并使它们重新获取该对象的对象锁。 如果有多个线程等待,则有线程调度器随机挑选出一个呈 wait 状态的线程。(并没有 “先来后到”)
  • 在notify()方法后,当前线程不会马上释放该对象锁,要等到执行notify()方法的线程将程序执行 完,也就是退出同步代码块之后才会释放对象锁。
notifyAll()方法

notify方法只是唤醒某一个等待线程. 使用notifyAll方法可以一次唤醒所有的等待线程.

注意: 虽然是同时唤醒 3 个线程, 但是这 3 个线程需要竞争锁. 所以并不是同时执行, 而仍然是有先有后的 执行.

9.wait方法和sleep方法的区别
  1. wait方法时Object类提供的方法,需要搭配synchronized锁来使用,调用wait方法会释放锁,线程进入WAITING状态,等待被其他线程唤醒,或者超时自动唤醒,唤醒之后的线程需要再次竞争synchronized锁才能继续执行,通常用于线程间通信
  2. sleep方法是Thread类提供的方法,调用sleep方法的线程进入TIMED_WAITING状态,不会释放锁,时间到自动唤醒,通常被用于暂停执行
10.将线程状态从运行态转换为就绪态 =>yield()方法 大公无私,让出 CPU

调用yield方法的线程会主动让出CPU资源,从运行态转为就绪态,等待被CPU继续调度。

到底啥时候让出CPU,又是啥时候被CPU再次调度,都是os调度的,我们无权选择。

此方法有两种可能性:

  • 线程让出运行态之后,马上又调度
  • 线程让出运行态之后,很久都不调度
线程的状态
  • NEW: 安排了工作, 还未开始行动
  • RUNNABLE: 可工作的. 又可以分成正在工作中和即将开始工作.
  • BLOCKED: 这几个都表示排队等着其他事情
  • WAITING: 这几个都表示排队等着其他事情
  • TIMED_WAITING: 这几个都表示排队等着其他事情
  • TERMINATED: 工作完成了.
观察线程的状态和转移

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1013034.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号