Java多线程与并发

news2024/11/19 7:42:06

1、JDK版本的选择

选择JDK8、JDK11进行讲解的原因:Oracle长期支持

2、进程和线程的区别

进程和线程的由来

请添加图片描述

3、进程与线程的区别

进程是资源分配的最小单位,线程是cpu调度的最小单位.

  • 所有与进程相关的资源,都被记录在PCB(进程控制块)中。
  • 进程是抢占处理机的调度单位;线程属于某个进程,共享其资源。
  • 线程只由堆栈寄存器、程序计数器和TCB组成。

进程是资源分配的基本单位,所有与进程有关的资源都记录在进程控制块PCB中,以表示进程拥有这些资源或者正在使用它们,进程也是抢占处理机的调度单位,它拥有完整的虚拟内存地址空间,当进程发生调度时,不同的进程拥有不同的地址空间,而同一进程内的不同线程共享同一地址空间。与进程相对应,线程与资源分配无关,它属于某一个进程,并与进程内的其它线程共享进程的资源。

请添加图片描述

请添加图片描述

总结:

  • 线程不能看做独立应用,而进程可看做独立应用。
  • 进程有独立的地址空间,相互不影响,线程只是进程的不同执行路径。
  • 线程没有独立的地址空间,多进程的程序比多线程程序健壮。
  • 进程的切换比线程的切换开销大。

4、Java进程和线程的关系

  • Java对操作系统提供的功能进行封装,包括进程和线程。
  • 运行一个程序会产生一个进程,进程包含至少一个线程。
  • 每个进程对应一个 JVM 实例,多个线程共享 JVM 里的堆。
  • Java采用单线程编程模型,程序会自动创建主线程。
  • 主线程可以创建子线程,原则上要后于子线程完成执行

获取当前线程名称:

System.out.println("Current Thread: "+Thread.currentThread().getName());

5、Thread中的start和run方法的区别

  • 调用 start()方法会创建一个新的子线程并启动
  • run( )方法只是 Thread的一个普通方法的调用

请添加图片描述

创建一个新线程

public class Test {
    private static void attack() {
        System.out.println("Current Thread is :" + Thread.currentThread().getName());
    }


    public static void main(String[] args) {
        Thread t = new Thread() {
            //            新线程run()需要执行的内容
            public void run() {
                attack();
            }
        };
        System.out.println("main Thread :" + Thread.currentThread().getName());
//        调用start方法,开启新线程
        t.start();
    }
}

结果:

main Thread :main
Fight
Current Thread is :Thread-0

5、Thread和Runnable的关系

Thread是一个类,实现了Runnable接口

我们可以打开其源码,发现Thread类实现了Runnable接口,进入Runnable接口发现其中只有一个抽象的run方法,也就说明了这个接口并不具备多线程的特性,是依赖Thread中的start的方法去创建一个子线程,再在子线程中调用一个thread实现好了的run方法去执行相应的业务逻辑.才能让类具备多线程的特性。

Thread

public class Thread implements Runnable {
    public synchronized void start() {
    }
}

Runnable

public interface Runnable{
    public abstract void run( );
}

Thread和 Runnable是什么关系

  • Thread是一个类,实现Runnable接口,里面含有start( )方法。
  • Runnable是一个接口,里面只有run( )方法。
  • Thread是实现了 Runnable接口的类,使得run支持多线程。
  • 因类的单一继承原则,推荐多使用 Runnable接口。

请添加图片描述

也就是线程需要执行,需要run( )方法,也就是新建线程的执行逻辑。还需要start( )方法,调用系统创建多线程的方法。也就是 start( )方法让系统创建一个新的线程执行run( )方法中逻辑。

extends Thread:内部有继承的run( )方法和start( )方法。可以自己执行。

implement Runnable:内部只有run( )方法。需要调用Thread中的start ( )才能执行。

6、创建多线程的方式

继承Thread方式(extends Thread)

自定义线程类:

public class MyThread extends Thread {
    /**
     * 利用继承中的特点 * 将线程名称传递 进行设置
     */
    public MyThread(String name) {
        super(name);
    }


    /**
     * 重写run方法 * 定义线程要执行的代码
     */
    public void run() {


        for (int i = 0; i < 20; i++) {
            //getName()方法 来自父亲
            System.out.println(getName() + i);
        }
    }
}

测试类:

public class Demo {
public static void main(String[] args) {
    System.out.println("这里是main线程");
        MyThread mt = new MyThread("小强");
            mt.start();//开启了一个新的线程
        for (int i = 0; i < 20; i++) {
            System.out.println("旺财:"+i);
}

实现Runnable方式(implements Runnable)

定义实现类:

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
        System.out.println(Thread.currentThread().getName()+" "+i);
}

定义测试类:

public class Demo {
    public static void main(String[] args) {
    //创建自定义类对象 线程任务对象
        MyRunnable mr = new MyRunnable();
    //创建线程对象
        Thread t = new Thread(mr, "小强");
            t.start();
        for (int i = 0; i < 20; i++) {
            System.out.println("旺财 " + i);
}

7、如何给run( )方法传参

  • 构造函数传参
  • 成员变量传参
  • 回调函数传参

8、如何实现处理线程的返回值

实现的方式

  • 主线程等待法
  • 使用Thread类的join()阻塞当前线程等待子线程处理完毕
  • 通过Callable接口:通过FutureTask或者线程池实现

8,1主线程等待

当主线程没有获取到值时,主线程等待子线程完成赋值。、

优点:实现简单,

缺点:

  • 自己需要实现循环等待逻辑,等待变量越多,代码异常臃肿。
  • 循环多久是不确定的。

子线程:

public class CycleWait implements Runnable {
    private String name;

    @Override
    public void run() {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        name = "we have date now";
    }
}

主线程:

//  主线程
public static void main(String[] args) throws Exception {
    CycleWait cw = new CycleWait();
    Thread t = new Thread(cw);
    t.start();
//  当辅助线程中name没有值时,主线程等待
    while (cw.name == null) {
            Thread.sleep(100);
    }
    System.out.println(cw.name);
}

8.2 使用Thread类的join()阻塞当前线程等待子线程处理完毕

  • 使用Thread里面的join( )方法,替代主线程等待法代码。
  • 优点:更精准控制,代码更简单。
  • 缺点:力不不够细,不能指定等待某个子线程完成再执行

替换为:t.join( )等待该线程终止。

//  主线程
public static void main(String[] args) throws Exception {
    CycleWait cw = new CycleWait();
    Thread t = new Thread(cw);
    t.start();
   当辅助线程中name没有值时,主线程等待
    t.join();
    System.out.println(cw.name);
}

8.3 通过Callable接口:通过FutureTask或者线程池实现

子线程:

import java.util.concurrent.Callable;
public class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        String value = "test";
        System.out.println("Ready to work");
        Thread.sleep(5000);
        System.out.println("task done");
        return value;
    }
}

使用FutureTask方式:

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class FutureTaskDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<String> task = new FutureTask<String>(new MyCallable());
        //启动线程
        new Thread(task).start();
//        线程等待
        if (!task.isDone()){
            System.out.println("wait");
        }
        System.out.println("task return="+task.get());
    }
}

执行结果:

请添加图片描述

使用线程池方式:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ThreadPoolDemo {
    public static void main(String[] args) {
//        1、创建线程池
        ExecutorService pool = Executors.newCachedThreadPool();
//        2、获取返回的结果
        Future<String> result = pool.submit(new MyCallable());
//        3、线程等待
        if (!result.isDone()){
            System.out.println("wait ..");
        }
        try {
//        4、获取返回值
            System.out.println(result.get());
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
//         5、关闭线程池
            pool.shutdown();
        }
    }
}

9、线程状态

线程状态导致状态发生条件
NEW(新建)线程刚被创建,但是并未启动。还没调用start方法。
Runnable(可运行)线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。
Blocked(锁阻塞)当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。
Waiting(无限等待)一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。
TimedWaiting(计时等待)同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep.
Teminated(被终止)因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。

无限等待(Waiting)

没有设置 Timeout参数的 object.wait( )方法。
没有设置 Timeout参数的 Threadjoin( )方法。
LockSupport.park( )方法。

限期等待(Timed Waiting)

Thread.sleep( )方法。
设置了 Timeout参数的 Object.wait( )方法。
设置了 Timeout参数的 Thread.join( )方法。
LockSupport.parkNanps( )方法。
LockSupport parkUntil( )方法。

请添加图片描述

10、Sleep和Wait的区别

基本的差别

  • sleep是 Thread类的方法,wat是Object类中定义的方法
  • sleep( )方法可以在任何地方使用
  • wait( )方法只能在 synchronized方法或 synchronized块中使用

最主要的本质区别

  • Thread.sleep只会让出CPU,不会导致锁行为的改变。
  • Object.wait不仅让出CPU,还会释放已经占有的同步资源锁。

请添加图片描述

public class WaitSleepDemo {
    public static void main(String[] args) {
            //创建lock锁对象
        final Object lock = new Object();

            //启动线程A
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("thread A is waiting to get lock");
                synchronized (lock){
                    try {
                        System.out.println("thread A get lock");
                        Thread.sleep(20);
                        System.out.println("thread A do wait  method");
                        lock.wait(1000);
                        System.out.println("thread A is done");
                    } catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        }).start();

        //线程等待10s让线程A,执行到wait( );
            Thread.sleep(10);

        //启动线程B
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("thread B is waiting to get lock");
                synchronized (lock){
                    try {
                        System.out.println("thread B get lock");
                        System.out.println("thread B is sleeping 10 ms");
                        Thread.sleep(10);
                        System.out.println("thread B is done");
                    } catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
}

执行结果:

thread A is waiting to get lock
thread A get lock
thread B is waiting to get lock
thread A do wait  method
thread B get lock
thread B is sleeping 10 ms
thread B is done
thread A is done

11、notify和norifyAll的区别?

两个概念:

  • 锁池(EntryList)
  • 等待池(WaitSet)

锁池:

假设线程A已经拥有了某个对象(不是类)的锁,而其它线程B、C想要调用这个对象的某个 synchronized方法(或者块),由于B、C线程在进入对象的 synchronized方法(或者块)之前必须先获得该对象锁的拥有权,而恰巧该对象的锁目前正被线程A所占用,此时B、C线程就会被阻塞,进入一个地方去等待锁的释放,这个地方便是该对象的锁池

等待池(Object.wait( )):

假设线程A调用了某个对象的wat()方法,线程A就会释放该对象的锁,同时线程A就进入到了该对象的等待池中,进入到等待池中的线程不会去竞争该对象的锁。

notify和norifyAll的区别?

  • notifyAll会让所有处于等待池的线程全部进入锁池去竞争获取锁的机会。
  • notify只会随机选取一个处于等待池中的线程进入锁池去竞争获取锁的机会。

12、yield

public static native void yield()    暂停当前正在执行的线程对象,并执行其他线程。

概念

当调用 Thread.yield( )函数时,会给线程调度器一个当前线程愿意让出CPU使用的暗示,但是线程调度器可能会忽略这个暗示。

public class YieldDemo {
    public static void main(String[] args) {
        Runnable yieldTask = new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 10; i++) {
                    System.out.println(Thread.currentThread().getName() + i);
                    if (i == 5) {
                        Thread.yield();
                    }
                }
            }
        };
        Thread t1 = new Thread(yieldTask, "A");
        Thread t2 = new Thread(yieldTask, "B");
        t1.start();
        t2.start();
    }
}

执行结果:

请求让出CPU执行的,不一定会被采纳,可能会让你继续执行(B1后还是B线程 的B2),也可能让出(B5后换成A线程的A1)。

B1
B2
B3
B4
B5
A1
B6
A2
A3
A4
A5
A6
A7
A8
A9
A10

13、如何中断线程

已经被抛弃的方法

  • 线程A通过调用stop( )方法停止线程B。
  • 通过调用 suspend( )和 resume( )方法

目前使用的方法

调用 interrupt0,通知线程应该中断了

  • ①如果线程处于被阻塞状态,那么线程将立即退出被阻塞状态,并抛出一个 InterruptedException异常。
  • ②如果线程处于正常活动状态,那么会将该线程的中断标志设置为true。被设置中断标志的线程将继续正常运行,不受影响。

需要被调用的线程配合中断。

  • ①在正常运行任务时,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程。
  • ②如果线程处于正常活动状态,那么会将该线程的中断标志设置为true。被设置中断标志的线程将继续正常运行,不受影响。

代码:

public class InterruptDemo {
    public static void main(String[] args) throws InterruptedException {
        Runnable interruptTask = new Runnable() {
            @Override
            public void run() {
                int i = 0;
                try {
                    //在正常运行任务时,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程
                    while (!Thread.currentThread().isInterrupted()) {
                        Thread.sleep(100); // 休眠100ms
                        i++;
                        System.out.println(Thread.currentThread().getName() + " (" + Thread.currentThread().getState() + ") loop " + i);
                    }
                } catch (InterruptedException e) {
                    //在调用阻塞方法时正确处理InterruptedException异常。(例如,catch异常后就结束线程。)
                    System.out.println(Thread.currentThread().getName() + " (" + Thread.currentThread().getState() + ") catch InterruptedException.");
                }
            }
        };

    // 启动“线程t1”
        Thread t1 = new Thread(interruptTask, "t1");
        System.out.println(t1.getName() + " (" + t1.getState() + ") is new.");
        t1.start();                      
        System.out.println(t1.getName() + " (" + t1.getState() + ") is started.");


        // 主线程休眠300ms,然后主线程给t1发“中断”指令。
        Thread.sleep(300);
        t1.interrupt();
        System.out.println(t1.getName() + " (" + t1.getState() + ") is interrupted.");


        // 主线程休眠300ms,然后查看t1的状态。
        Thread.sleep(300);
        System.out.println(t1.getName() + " (" + t1.getState() + ") is interrupted now.");
    }
}

执行结果:

t1 (NEW) is new.        //线程被新创建。
t1 (RUNNABLE) is started.        线程执行,(调用start( )方法)
t1 (RUNNABLE) loop09 1        //线程正在执行
t1 (RUNNABLE) loop09 2        //线程正在执行
t1 (RUNNABLE) catch InterruptedException.    //线程正在阻塞,调用抛出异常
t1 (TIMED_WAITING) is interrupted.        //继续执行后面的逻辑
t1 (TERMINATED) is interrupted now.        //线程已经终止

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/669940.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

day08 教你用英语过海关

前言 &#x1f3e0;个人主页&#xff1a;我是沐风晓月 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是沐风晓月&#xff0c;阿里云社区博客专家 &#x1f609;&#x1f609; &#x1f495; 座右铭&#xff1a; 先努力成长自己&#xff0c;再帮助更多的人,一起加油进…

基于深度学习FasterRCNN模型Restnet50 的生活垃圾智能分类(准确率达84%)-含python工程全源码

目录 前言总体设计系统整体结构图系统流程图 运行环境1. 硬件环境2. Python 环境 模块实现1. 数据预处理2. 数据加载3. 模型构建4. 模型训练及保存5. 模型加载与调用 系统测试1. 模型准确率2. 分类别准确率 工程源代码下载其它资料下载 前言 本项目基于Faster R-CNN模型&#…

mmrotate框架基本使用

1、如何将类交给mmrotate框架容器管理 容器&#xff1a;框架中现有基本容器包括DATASETS, BACKBONES, LOSSES, DETECTORS。初始化容器&#xff1a;Registry(‘backbone’)中’backbone’为容器初始化配置文件。#/mmdet/models/builder.py 部分代码 from mmcv.utils import Re…

【Lisp】【Python】在CAD中用插件获取选中字块的文字,在rhino中批量生成图层

文章目录 1 get_selected_text.lsp1.1 使用方法LISP代码解析1.2 动图 2 Rhino中使用PythonScript批量建立图层.py2.1 直接生成2.2 带颜色生成 2.3 动图展示 1 get_selected_text.lsp 1.1 使用方法 用记事本复制以下代码&#xff0c;改文件名为get_selected_text.lsp (defun c:…

springboot高校宿舍报修管理系统-计算机毕设 附源码83946

springboot高校宿舍报修管理系统 摘 要 科技进步的飞速发展引起人们日常生活的巨大变化&#xff0c;电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用。信息时代的到来已成为不可阻挡的时尚潮流&#xff0c;人类发展的历史正进入一个新时代。在现实…

【AI绘图 丨 Stable_diffusion 系列教程四】— Window 环境 | Stable Diffusion入门教程 及安装(全篇)

&#x1f449;腾小云导读 最近&#xff0c;AI图像生成引人注目&#xff0c;它能够根据文字描述生成精美图像&#xff0c;这极大地改变了人们的图像创作方式。Stable Diffusion作为一款高性能模型&#xff0c;它生成的图像质量更高、运行速度更快、消耗的资源以及内存占用更小&a…

MATLAB+JAVA的混合开发

近期项目中需要使用matlab跟java做混合开发。主要记录一下&#xff0c;此次开发遇到的问题点。 环境&#xff1a;使用的matlab版本是 R2018b。 当前状况&#xff1a;MATLAB代码已经编写好&#xff0c;且运行成功。需要打成jar包才可以被java调用。 步骤一&#xff1a; 按照…

OpenJdk 和 oracleJdk

OpenJDK的网站&#xff08;https://jdk.java.net/&#xff09;通常仅显示最新的几个版本&#xff0c;对于更早的版本&#xff0c;可能不再在主页面上列出。这是因为随着时间的推移&#xff0c;Java社区通常会专注于支持和维护最新的版本&#xff0c;并鼓励开发者尽可能地使用最…

开源软件介绍——国内和国际主要开源社区

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天我们来看一看国内和国际上有哪些主要开源社区。 开源社区的定义 开源社区又称为开放源代码社区&#xff0c;一般由拥有共同兴趣爱好的人组成。根据相应的开源软件许可证协议公布软件源代码的网络平台&a…

【深度学习】5-2 与学习相关的技巧 - 权重的初始值

在神经网络的学习中&#xff0c;权重的初始值特别重要。实际上&#xff0c;设定什么样的权重初始值&#xff0c;经常关系到神经网络的学习能否成功。本节将介绍权重初始值的推荐值&#xff0c;并通过实验确认神经网络的学习是否会快速进行。 可以将权重初始值设为0吗 后面我们…

搜索引擎的个性化搜索:为何搜索结果因人而异

&#x1f482; 个人网站:【海拥】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 目录 前言搜索引擎的工作原理…

uniapp uView2 字体加载错误提示处理(字体离线方案)

最近老是收到有人反馈 uView2的字体图标报错&#xff0c;具体错误提示如下图 这个报错的原因有2种情况 at.licdn.com 网站维护&#xff0c;无法加载&#xff1b;国内这些小程序平台的开发工具日常抽风&#xff0c;代码能跑&#xff0c;但就是报错&#xff0c;简直离谱&#x…

漏洞复现 D-Link DCS 密码泄露漏洞

0x01 漏洞描述 D-link DCS是一款网络摄像机&#xff0c;工作温度为0-50℃。D-link DCS系统存在密码泄露漏洞&#xff0c;攻击者通过漏洞可以获取后台权限。 0x02 漏洞复现 fofa&#xff1a;app“D_Link-DCS-4622” 1.使用poc进行账号密码查看&#xff0c;得到密码登录即可 …

网络安全是什么?怎么学

网络安全基础 安全的定义&#xff1a; 1&#xff09;一种能够识别和消除不安全因素的能力&#xff1b; 2&#xff09;安全是一个持续的过程网络安全是一门涉及计算机科学、网络技术、通信技术、密码技术、信息安全技术、应用数学、数论、信息论等多种学科的综合性科学。 网络…

Redis原理 - 数据结构的底层实现

原文首更地址&#xff0c;阅读效果更佳&#xff01; Redis原理 - 数据结构的底层实现 | CoderMast编程桅杆https://www.codermast.com/database/redis/redis-datastruct-underlying-implementation.html 动态字符串SDS #基本概念 Redis 中保存的 Key 是字符串&#xff0c;V…

C国演义 [第六章]

第六章 最长递增子序列题目理解步骤dp含义递推公式初始化遍历顺序 代码 最长连续递增序列题目理解步骤dp含义递推公式初始化遍历顺序 代码 最长递增子序列 力扣链接 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列&am…

什么是http代理504网关超时错误,怎么处理

HTTP代理504网关超时错误是指代理服务器在尝试连接目标服务器时&#xff0c;等待目标服务器响应的时间超过了预设的时间&#xff0c;导致代理服务器无法返回请求结果给客户端&#xff0c;从而出现网关超时。 为什么会遇到http代理504网关超时错误 我们遇到HTTP代理504网关超时…

CSPM项目管理专业人员能力评价证书报考条件与考试形势

2021年10月&#xff0c;中共中央、国务院发布的《国家标准化发展纲要》明确提出构建多层次从业人员培养培训体系&#xff0c;开展专业人才培养培训和国家质量基础设施综合教育。建立健全人才的职业能力评价和激励机制。由中国标准化协会&#xff08;CAS&#xff09;组织开展的项…

【人工智能概论】 Python标准库——dalib(领域自适应)

【人工智能概论】 Python标准库——dalib&#xff08;领域自适应&#xff09; 文章目录 【人工智能概论】 Python标准库——dalib&#xff08;领域自适应&#xff09;一. 领域鉴别器&#xff08;DomainDiscriminator&#xff09;二. 领域对抗损失&#xff08;DomainAdversarial…

【MongoDB】五、MongoDB分片集群的部署

【MongoDB】五、MongoDB分片集群的部署 实验目的实验内容实验步骤环境准备部署 Config server配置Config Server副本集部署Shard部署mongos启动分片功能查看分片信息 实验小结 实验目的 能够通过部署MongoDB分片集群熟悉MongoDB分片集群架构和基本操作&#xff0c;从而解决大数…