展开菜单
首页 精品内容 本月促销 装机必备 Windows PDF教程 macOS软件 IOS软件 Android AI 专题
全部分类

当前位置:

首页 > 编程开发 > Java多线程Thread类如何使用

Java多线程Thread类如何使用

Thread类的基本用法1.创建子类,继承自Thread并且重写run方法:classMyThreadextendsThread{@Overridepublicvoidrun(){System.out.println("hellothread");}}publicclassDemo1{publicstaticvoidmain(String[]args){//最基本的创建线程的办法.Threadt=newMyThread();//调用了start方法才是真正的在系统中创建了线程,执行run方法t.start

本文目录

      Thread类的基本用法

      1.创建子类,继承自Thread并且重写run方法:

      class MyThread extends Thread {
          @Override
          public void run() {
              System.out.println("hello thread");
          }
      }
      public class Demo1 {
          public static void main(String[] args) {
              // 最基本的创建线程的办法.
              Thread t = new MyThread();
              //调用了start方法才是真正的在系统中创建了线程,执行run方法
              t.start();
          }
      }

      2.创建一个类,实现Runnable接口再创建Runnable是实例传给Thread

      class MyRunnable implements Runnable{
          @Override
          public void run() {
              System.out.println("hello");
          }
      }
      public class Demo3 {
          public static void main(String[] args) {
              Thread t = new Thread(new MyRunnable());
              t.start();
          }
      }

      3.匿名内部类

      创建了一个匿名内部类,继承自Thread类,同时重写run方法,再new出匿名内部类的实例

      public class Demo4 {
          public static void main(String[] args) {
              Thread t = new Thread(){
                  @Override
                  public void run() {
                      System.out.println("hello");
                  }
              };
              t.start();
          }
      }

      new的Runnable,针对这个创建的匿名内部类,同时new出的Runnable实例传给Thread的构造方法

      public class Demo5 {
          public static void main(String[] args) {
              Thread t = new Thread(new Runnable() {
                  @Override
                  public void run() {
                      System.out.println("hello");
                  }
              });
              t.start();
          }
      }

      4.lambda表达式 lambda代替Runnable

      public class Demo6 {
          public static void main(String[] args) {
              Thread t = new Thread(() ->{
                  System.out.println("hello");
              });
              t.start();
          }
      }

      线程指标

      1.isDaemon();
      是否后台线程 后台线程不影响进程退出,不是后台线程会影响进程退出

      2.isAlive();
      是否存活 在调用start前系统中是没有对应线程的,run方法执行完后线程就销毁了,t对象可能还存在

      3.isinterrupted();
      是否被中断

      run和start的区别

      run单纯的只是一个普通方法描述了任务的内容 start则是一个特殊的方法,内部会在系统中创建线程

      中断线程

      线程停下来的关键是要让对应run方法执行完,对于main线程来说main方法执行完了才会终止

      1.手动设置标志位
      在线程中控制这个标志位就能影响到这个线程结束,但是此处多个线程共用一片虚拟空间,因此main线程修改的isQuit和t线程判断的isQuit是同一个值

      public class Demo10 {
          // 通过这个变量来控制线程是否结束.
          private static boolean isQuit = false;
          public static void main(String[] args) {
              Thread t = new Thread(() -> {
                  while (!isQuit) {
                      System.out.println("hello thread");
                      try {
                          Thread.sleep(1000);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }
              });
              t.start();
      
              // 就可以在 main 线程中通过修改 isQuit 的值, 来影响到线程是否退出
              try {
                  Thread.sleep(5000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              // main 线程在 5s 之后, 修改 isQuit 的状态.
              isQuit = true;
          }
      }

      2.使用Thread中内置的一个标志位来判定 Thread.interruted()这是一个静态方法 Thread.currentThread().isInterrupted()这是一个实例方法,其中currentThread能够获取到当前线程的实例

      public class Demo7 {
          public static void main(String[] args)  {
              Thread t = new Thread(() -> {
                  while(!Thread.currentThread().isInterrupted()){
                      System.out.println("hello");
                      try {
                          Thread.sleep(1000);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                          // 当触发异常之后, 立即就退出循环~
                          System.out.println("这是收尾工作");
                          break;
                      }
                  }
              });
              t.start();
              try{
                  Thread.sleep(5000);
              }catch (InterruptedException e){
                  e.printStackTrace();
              }
              // 在主线程中, 调用 interrupt 方法, 来中断这个线程.
              // t.interrupt 的意思就是让 t 线程被中断!!
              t.interrupt();
          }
      }

      需要注意的是调用这个方法t.interrupt()可能会产生两种情况:
      1)如果t线程处在就绪就设置线程的标志位为true
      2)如果t线程处在阻塞状态(sleep),就会触发一个InterruptExeception

      线程等待

      多个线程之间调度顺序是不确定的,有时候我们需要控制线程之间的顺序,线程等待就是一种控制线程执行顺序的手段,此处的线程等待只要是控制线程结束的先后顺序。
      哪个线程中的join,哪个线程就会阻塞等待直到对应的线程执行完毕为止。

      t.join();
      调用这个方法的线程是main线程,针对t这个对象调用的此时就是让main等待t。
      代码执行到join这一行就停下了,让t先结束然后main继续。

      t.join(10000);
      join提供了另一个版本为带一个参数的,参数为等待时间10s之后join直接返回不再等待

      Thread.currentThread()

      能够获取当前线程的应用,哪个线程调用的currentThread就获取到哪个线程的实例 对比this如下:
      对于这个代码来说,通过继承Thread的方法来创建线程。此时run方法中直接通过this拿到的就是当前Thread的实例

      public class Demo4 {
          public static void main(String[] args) {
              Thread t = new Thread(){
                  @Override
                  public void run() {
                      System.out.println(Thread.currentThread().getName());
                      System.out.println(this.getName());
                  }
              };
              t.start();
          }
      }

      然而此处this不是指向Thread类型,而是指向Runnable,Runnable只是一个单纯的任务没有name属性,要想拿到线程名字只能通过Thread.currentThread()

      public class Demo5 {
          public static void main(String[] args) {
              Thread t = new Thread(new Runnable() {
                  @Override
                  public void run() {
                      //err
                      //System.out.println(this.getName());
                      //right
                      System.out.println(Thread.currentThread().getName());
                  }
              });
              t.start();
          }
      }

      进程状态

      针对系统层面:

      • 就绪

      • 阻塞

      java中Thread类进一步细化:

      • NEW:把Thread对象创建好了但是还没有调用start

      • TERMINATED:操作系统中的线程已执行完毕销毁,但是Thread对象还在获取到的状态

      • RUNNABLE:就绪状态,处在该状态的线程就是在就绪队列中,随时可以调度到CPU上

      • TIME_WAITING:调用了sleep就会进入到该状态,join(超时时间) BLOCKED:当前线程在等待锁导致了阻塞

      • WAITING:当前线程在等待唤醒

      状态转换图:

      Java多线程Thread类如何使用

      线程安全问题

      定义:操作系统中线程调度是随机的,导致程序的执行可能会出现一些bug。如果因为调度随机性引入了bug线程就是不安全的,反之则是安全的。
      解决方法:加锁,给方法直接加上synchronized关键字,此时进入方法就会自动加锁,离开方法就会自动解锁。当一个线程加锁成功的时候,其他线程尝试加锁就会触发阻塞等待,阻塞会一直持续到占用锁的线程把锁释放为止。

      synchronized public void increase() {
              count++;
      }

      线程不安全产生的原因:

      • 1.线程是抢占式执行,线程间的调度充满随机性。

      • 2.多个线程对同一个变量进行修改操作

      • 3.针对变量的操作不是原子的

      • 4.内存可见性也会影响线程安全(针对同一个变量t1线程循环进行多次读操作,t2线程少次修改操作,t1就不会从内存读数据了而是从寄存器里读)

      • 5.指令重排序,也是编译器优化的一种操作,保证逻辑不变的情况下调整顺序,解决方法synchronized。

      内存可见性解决方法:

      • 1.使用synchronized关键字 使用synchronized不光能保证指令的原子性,同时也能保证内存的可见性。被synchronized包裹起来的代码编译器就不会从寄存器里读。

      • 2.使用volatile关键字 能够保证内存可见性,禁止编译器作出上述优化,编译器每次执行判定相等都会重新从内存读取。

      synchronized用法

      在java中每个类都是继承自Object,每个new出来的实例里面一方面包含自己安排的属性,另一方面包含了“对象头”即对象的一些元数据。加锁操作就是在这个对象头里面设置一个标志位。

      1.直接修饰普通的方法

      使用synchronized的时候本质上是对某个“对象”进行加锁,此时的锁对象就是this。加锁操作就是在设置this的对象头的标志位,当两个线程同时尝试对同一个对象加锁的时候才有竞争,如果是两个线程在针对两个不同对象加锁就没有竞争。

      class Counter{
      	public int count;
      	synchronized public void increase(){
      	count++;
      	}
      }

      2.修饰一个代码块

      需要显示制定针对那个对象加锁(java中的任意对象都可以作为锁对象)

      public void increase(){
          synchronized(this){
          count++;
          }
      }

      3.修饰一个静态方法

      相当于针对当前类的类对象加锁,类对象就是运行程序的时候。class文件被加载到JVM内存中的模样。

      synchronized public static void func(){
      }

      或者

      public static void func(){
          synchronized(Counter.class){
      
          }
      }

      监视器锁monitor lock

      可重入锁就是同一个线程针对同一个锁,连续加锁两次,如果出现死锁就是不可重入锁,如果不会死锁就是可重入的。因此就把synchronized实现为可重入锁,下面的例子里啊连续加锁操作不会导致死锁。可重入锁内部会记录所被哪个线程占用也会记录加锁次数,因此后续再加锁就不是真的加锁而是单纯地把技术给自增。

      synchronized public void increase(){
          synchronized(this){
              count++;
          }
      }

      死锁的其他场景

      • 1.一个线程一把锁

      • 2.两个线程两把锁

      • 3.N个线程M把锁(哲学家就餐问题,解决方法:先拿编号小的筷子)

      死锁的四个必要条件(前三个都是锁本身的特点)

      • 1.互斥使用,一个锁被另一个线程占用后其他线程就用不了(锁的本质,保证原子性)

      • 2.不可抢占,一个锁被一个线程占用后其他线程不可把这个锁给挖走

      • 3.请求和保持,当一个线程占据了多把锁之后,除非显示的释放否则这些锁中都是该线程持有的

      • 4.环路等待,等待关系成环(解决:遵循固定的顺序加锁就不会出现环路等待)

      java线程类:

      • 不安全的:ArrayList,LinkedList,HashMap,TreeMap,HashSet,TreeSet,StringBuilder

      • 安全的:Vector,HashTable,ConcurrentHashMap,StringBuffer,String

      volatile

      禁止编译器优化保证内存可见性,产生原因:计算机想执行一些计算就需要把内存的数据读到CPU寄存器中,然后再从寄存器中计算写回到内存中,因为CPU访问寄存器的速度比访问内存快很多,当CPU连续多次访问内存结果都一样,CPU就会选择访问寄存器。

      JMM(Java Memory Model)Java内存模型

      就是把硬件结构在java中用专业的术语又重新抽象封装了一遍。

      工作内存(work memory)其实指的不是内存,而是CPU寄存器。
      主内存(main memeory)这才是主内存。
      原因:java作为一个跨平台编程语言要把硬件细节封装起来,假设某个计算机没有CPU或者内存同样可以套到上述模型中。

      寄存器,缓存和内存之间的关系

      CPU从内存取数据太慢,因此把数据直接放到寄存器里来读,但寄存器空间太紧张于是又搞了一个存储空间,比寄存器大比内存小速度比寄存器慢比内存快称为缓存。寄存器和缓存统称为工作内存。

      寄存器,缓存和内存之间的关系图

      Java多线程Thread类如何使用

      • 存储空间:CPU

      • 速度:CPU>L1>L2>L3>内存

      • 成本:CPU>L1>L2>L3>内存

      volatile和synchronized的区别

      • volatile只是保证可见性不保证原子性,只是处理一个线程读和一个线程写的过程。

      • synchronized都能处理

      wait和notify

      等待和通知处理线程调度随机性问题的,join也是一种控制顺序的方式更倾向于控制线程结束。wait和notify都是Object对象的方法,调用wait方法的线程就会陷入阻塞,阻塞到有线程通过notify来通知。

      public class Demo9 {
          public static void main(String[] args) throws InterruptedException {
              Object object = new Object();
              System.out.println("wait前");
              object.wait();
              System.out.println("wait后");
          }
      }

      wait内部会做三件事;

      • 1.先释放锁

      • 2.等待其他线程的通知

      • 3.收到通知后重新获得锁并继续往下执行

      因此想用wait/notify就得搭配synchronized

      public class Demo9 {
          public static void main(String[] args) throws InterruptedException {
              Object object = new Object();
              synchronized (object){
                  System.out.println("wait前");
                  object.wait();
                  System.out.println("wait后");
              }
          }
      }

      注意:wait notify都是针对同一对象来操作的,例如现在有一个对象o,有10个线程都调用了o.wait,此时10个线程都是阻塞状态。如果调用了o.notify就会把10个线程中的一个线程唤醒。而notifyAll就会把所有10个线程全都给唤醒,此时就会竞争锁。

      本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
      作者最新文章
      编程开发 Java thread
      相关文章 更多
      精品专题 更多
      装机必备

      正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

      Windows

      正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

      PDF教程

      正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

      macOS软件

      正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

      IOS软件

      正软商城iOS软件专区,精选适用于iPhone和iPad的办公、学习、影音、设计、效率及AI应用,提供功能介绍、适用设备、系统要求和正版获取方式等信息。

      AI

      正软商城AI软件专区,汇集AI写作、AI绘画、AI视频、AI办公、AI编程、AI翻译、智能客服和数据分析等人工智能工具,提供功能介绍、适用平台、收费方式及正版购买信息。

      Mac软件 更多
      Aperty macOS版
      Aperty macOS版

      Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

      火山引擎 网页版
      火山引擎 网页版

      火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

      立刻MV 网页版
      立刻MV 网页版

      立刻MV 面向音乐人、内容创作者和品牌团队,帮助用户低成本完成歌曲 MV、歌词视频、AI 分镜、图生视频与最终合成。

      蛙蛙写作 网页版
      蛙蛙写作 网页版

      蛙蛙写作 AI 创作工具,覆盖小说、剧本、文案,一键生成文稿,打通文字转视频全流程,高效解决创作难题。

      讯飞智作 网页版
      讯飞智作 网页版

      讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

      超级简历WonderCV 网页版
      超级简历WonderCV 网页版

      超级简历WonderCV是面向应届生和AI小白的智能求职平台,覆盖简历、面试与职业规划。

      音述AI 网页版
      音述AI 网页版

      音述AI,是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区。

      AionClaw macOS版
      AionClaw macOS版

      AionClaw 是可在桌面端运行的任务型 AI 助手,通过数字专家与技能市场组合办公、创作、数据和开发能力。

      Loomy macOS版
      Loomy macOS版

      Loomy 是科大讯飞推出的桌面 AI 助手,可通过对话、任务与工作目录协助处理办公、内容创作和资料整理。

      AirBuddy 3 macOS版
      AirBuddy 3 macOS版

      AirBuddy 3 是面向 macOS 的蓝牙外设增强工具,可集中查看 AirPods、Beats 与苹果设备电量,提供快速连接、播放控制、电量提醒、桌面小组件和多台 Mac 外设接力等功能。

      AirServer macOS版
      AirServer macOS版

      AirServer macOS版可把 Mac 转换为无线投屏接收端,通过 AirPlay 或 Google Cast 接收手机、平板和电脑画面,并支持多设备同时连接、高清镜像及会话录制,适合家庭娱乐、课堂教学、会议演示和应用展示。

      AnyDesk macOS版
      AnyDesk macOS版

      AnyDesk macOS版是一款适用于远程办公、设备维护和客户支持的远程桌面工具,可连接并操作异地计算机,支持文件传输、无人值守访问、会话权限控制及跨平台协作。

      WINDOWS 更多
      讯飞绘文 网页版
      讯飞绘文 网页版

      讯飞绘文网页版是科大讯飞推出的AI图文创作与内容运营工具,覆盖热点选题、文章生成、稿件校对、智能配图、排版发布和数据复盘等环节,适合自媒体创作者及内容团队在线使用。

      Maya(Autodesk Maya) Windows版
      Maya(Autodesk Maya) Windows版

      Autodesk Maya 是面向影视、电视、动画和游戏行业的专业三维制作软件,集多边形与 NURBS 建模、角色绑定、关键帧和动作捕捉动画、Bifrost 特效模拟、材质开发及 Arnold 渲染于一体,

      Aperty Windows版
      Aperty Windows版

      Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

      火山引擎 网页版
      火山引擎 网页版

      火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

      立刻MV 网页版
      立刻MV 网页版

      立刻MV 面向音乐人、内容创作者和品牌团队,帮助用户低成本完成歌曲 MV、歌词视频、AI 分镜、图生视频与最终合成。

      蛙蛙写作 网页版
      蛙蛙写作 网页版

      蛙蛙写作 AI 创作工具,覆盖小说、剧本、文案,一键生成文稿,打通文字转视频全流程,高效解决创作难题。

      讯飞智作 网页版
      讯飞智作 网页版

      讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

      超级简历WonderCV 网页版
      超级简历WonderCV 网页版

      超级简历WonderCV是面向应届生和AI小白的智能求职平台,覆盖简历、面试与职业规划。

      音述AI 网页版
      音述AI 网页版

      音述AI,是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区。

      AionClaw Windows版
      AionClaw Windows版

      AionClaw 是可在桌面端运行的任务型 AI 助手,通过数字专家与技能市场组合办公、创作、数据和开发能力。

      Loomy Windows版
      Loomy Windows版

      Loomy 是科大讯飞推出的桌面 AI 助手,可通过对话、任务与工作目录协助处理办公、内容创作和资料整理。

      AirServer Windows版
      AirServer Windows版

      AirServer Windows版可把电脑变成AirPlay、Google Cast和Miracast无线投屏接收端,适合会议演示、课堂教学及家庭大屏分享。发送设备通常无需另装客户端,并可同时接收和智能排列多个镜像会话。