Java多线程与并发

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.        //线程已经终止

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数学建模竞赛国赛入场券之攻略

数学建模竞赛国赛入场券之攻略 1.团队契合度 在3天的准备时间中&#xff0c;如果是临时组建的草台班子光处理分歧可能就已经耗掉一半时间&#xff0c;最好在赛前就完成磨合&#xff0c;像一起做模拟题练练手之类&#xff0c;甲准备图论、乙准备优化方法&#xff0c;然后再一块…

存储笔记8 ipsan

Module Objectives IP SAN的组件 IP SAN的好处 描述SAN中的IP融合及其影响 描述的基本架构 –iSCSI –FCIP –FCoE 讨论IP SAN技术的市场驱动因素 列出IP SAN技术 列出iSCSI的组件和连接选项 描述iSCSI体系结构和拓扑结构 解释iSNS操作 描述FCIP的体系结构 IP SAN互联…

Redis持久化机制与Redis事务

一、Redis 持久化机制 Redis 是个基于内存的数据库。那服务一旦宕机&#xff0c;内存中数据必将全部丢失。所以丢失数据的恢复对于 Redis 是十分重要的&#xff0c;我们首先想到是可以从数据库中恢复&#xff0c;但是在由 Redis 宕机时&#xff08;说明相关工作正在运行&#…

UDS系列-31服务(Routine Control)

诊断协议那些事儿 诊断协议那些事儿专栏系列文章,本文介绍例程控制服务RoutineControl,该服务的目的是Client端使用Routine Control服务来执行定义的步骤序列并获取特定序列的相关结果。这个服务经常在EOL、Bootloader中使用,比如,检查刷写条件是否满足、擦除内存、覆盖正…

Maven如何创建Maven web项目

1、创建一个新的模块: 1.1 使用骨架点一下&#xff0c;这里 1.2 找到maven-archetype-webapp项目&#xff0c;选中点击&#xff0c;一路next就行。 1.3 删除不必要的maven配置&#xff1a;&#xff08;这里我不需要&#xff0c;针对自己情况而定&#xff09; 可以从name这里开…

pr视频叠加,即原视频右上角添加另外一个视频方法,以及pr导出视频步骤

一、pr视频叠加&#xff0c;即原视频右上角添加另外一个视频方法 在使用pr制作视频时&#xff0c;我们希望在原视频的左上角或右上角同步播放另外一个视频&#xff0c;如下图所示&#xff1a; 具体方法为&#xff1a; 1、导入原视频&#xff0c;第一个放在v1位置&#xff0c;第…

案例 | 标杆引领!人大金仓智绘数字金融

随着中央数字经济政策推进金融业数字化建设&#xff0c;数字金融已初见成效&#xff0c;但尚存在信息安全缺乏保障、转型覆盖不全面等问题。 为实现金融行业全面数字化转型升级&#xff0c;作为数据库领域国家队&#xff0c;人大金仓紧跟国家战略&#xff0c;自主研发的系列数据…

采用SqlSugar的DBFirst相关功能创建数据库表对应的实体类

.NET Core官方教程中推荐使用的EF Core数据库ORM框架虽然能用&#xff0c;但是用起来并不是太方便&#xff08;或者是不习惯&#xff0c;之前用的最多的还是linq&#xff09;。之前下载的开源博客项目中使用的SqlSugar&#xff0c;后者是由果糖大数据科技团队维护和更新 &#…

【网络1】协议及相关命令

文章目录 1.局域网&#xff1a;CSMA/CD2.互联网&#xff1a;ARP&#xff0c;DHCP&#xff0c;NAT3.TCP协议&#xff1a;telnet&#xff0c;tcpdump&#xff0c;syn/accept队列4.HTTPS协议&#xff1a;摘要&#xff08;sha、md5、crc&#xff09;。win对文件MD5校验&#xff1a;…

C# NX二次开发:通过UFUN函数获取刀具描述,目录号,库号等信息

今天要将的是&#xff0c;在NX中对CAM模块进行二次开发的时候&#xff0c;往往需要获取一些关于刀具使用的信息&#xff0c;这些信息用NXOPEN的的方法录制也可以录制出来&#xff0c;但是录制出来的代码&#xff0c;往往都是一种刀具类型会出现一个Builder。这样在你不知道有多…

微服务开发系列 第十一篇:XXL-JOB

总概 A、技术栈 开发语言&#xff1a;Java 1.8数据库&#xff1a;MySQL、Redis、MongoDB、Elasticsearch微服务框架&#xff1a;Spring Cloud Alibaba微服务网关&#xff1a;Spring Cloud Gateway服务注册和配置中心&#xff1a;Nacos分布式事务&#xff1a;Seata链路追踪框架…

【AI】金融FinGPT模型

金融FinGPT模型开源&#xff0c;对标BloombergGPT&#xff0c;训练参数可从61.7亿减少为367万&#xff0c;可预测股价 继Bloomberg提出了500亿参数的BloombergGPT&#xff0c;GPT在金融领域的应用受到了广泛关注&#xff0c;但BloombergGPT是一个非开源的模型&#xff0c;而且…

RWA 成下一轮加密大叙事,PoseiSwap 的价值正在凸显

PoseiSwap是Nautilus Chain上的首个DEX&#xff0c;在Nautilus Chain模块化开发的支持下&#xff0c;PoseiSwap能够以更具延展性的方式来构建应用&#xff0c;并能够为交易者提供极佳的交易体验。基于Nautilus Chain支持下的Zk-Rollup方案&#xff0c;PoseiSwap构建了基于零知识…

cuda pyinstall cvs 使用记录

1.pip 换源 pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/2.安装匹配cuda的pytorch 官网&#xff1a;PyTorch pip3 install torch torchvision torchaudio查看能否使用cuda: import torch torch.cuda.is_available()获得以下反馈&#xff1a; 意思…

设计模式之原型模式笔记

设计模式之原型模式笔记 说明Prototype(原型)目录UML原型模式示例类图RealizeType类(浅克隆)测试类 原型模式案例奖状类测试类 扩展(深克隆)学生类奖状类测试类 说明 记录下学习设计模式-原型模式的写法。 Prototype(原型) 意图:用原型实例指定创建对象的种类&#xff0c;并…

如何监测和优化阿里云服务器的性能?有哪些性能分析工具和指标?

如何监测和优化阿里云服务器的性能&#xff1f;有哪些性能分析工具和指标&#xff1f;   阿里云服务器性能监测与优化是云计算服务中一个非常重要的环节。为了确保服务器稳定、高效地运行&#xff0c;我们需要对其性能进行监测&#xff0c;并在监测的基础上进行优化。本文将为…

突破 Python 爬虫的瓶颈:WebKit 在线模拟技术与环境搭建

部分数据来源:ChatGPT 引言 在使用 Python 进行爬虫开发的时候,很多情况下我们需要利用一些浏览器内核来模拟浏览器行为。而目前最为常用的两种浏览器内核是基于 WebKit 和基于 Chromium 的内核。那么在 Windows 10 操作系统中,我们可以使用 Anaconda 作为 Python 的发行版…

CSS3-定位

网页常见布局方式 1 标准流 1 块级元素独占一行 → 垂直布局 2 行内元素/行内块元素一行显示多个 → 水平布局 2 浮动 可以让原本垂直布局的 块级元素变成水平布局 3 定位 1 可以让元素自由的摆放在网…

使用SOCKET搭建linux和window实现实时摄像头传输(linux传输win端使用C++mfc显示)--Win端开发

1.使用MFC搭建框架 配置: Window10VS2013opencv249 如果VS和opencv配置不一样&#xff0c;让版本对应 Opencv与VS版本 1.1 MFC项目搭建 通过这些步骤就创建了一个MFC基础项目。 1.2项目属性配置 本项目因为要使用opencv,所以就要配置以下opencv的环境 首先在opencv官网下载…

(十一)CSharp-LINQ(1)

一、LINQ 数据库可以通过 SQL 进行访问&#xff0c;但在程序中&#xff0c;数据要被保存在差异很大的类对象或结构中。由于没有通用的查询语言来从数据结构中获取数据。所以可以使用 LINQ 可以很轻松地查询对象集合。 LINQ 高级特性&#xff1a; LINQ 代表语言集成查询。LIN…