Java 多线程

多线程实现方式

Thread类

MyThread类继承了Thread类

 MyThread thread=  new MyThread1("窗口1");
 thread.start();
       

Runnable接口

自定义一个MyRunnable类来实现Runnable接口,在MyRunnable类中重写run()方法

,创建Thread对象,并把MyRunnable对象作为Tread类构造方法的参数传递进去。

        MyRunnable myrunnable = new MyRunnable();
        Thread thread = new Thread(myrunnable, "线程01");
        thread.start();

lambda转换为Runnable功能接口(interface)的子类的实例,然后将其传递给overloaded to take a Runnable object的Thread构造函数。

new Thread(() -> System.out.println("Lol")).start();

Callable接口

自定义一个MyThread类来实现Callable接口,在MyThread类中重写call()方法,创建FutureTask,Thread对象,并把MyCallable对象作为FutureTask类构造方法的参数传递进去,把FutureTask对象传递给Thread对象。可以获取到线程的执行结果。

public class MyThread implements Callable {
    @Override
    public Object call() throws Exception {
        return 100;
    }
}
        MyThread myThread=new MyThread();
        FutureTask futureTask=new FutureTask(myThread);
        Thread thread=new Thread(futureTask);
        thread.start();
        try {
            Object o = futureTask.get();
            System.out.println(o);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }

方法介绍

线程生命周期

当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中,它要经过 新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5种状态。尤其是当线程启动以后,它不可能一直"霸占"着CPU独自运行,所以CPU需要在多条线程之间切换,于是 线程状态也会多次在运行、阻塞之间切换。

新建状态(NEW):线程已创建,尚未调用start()方法启动之前。
运行状态(RUNNABLE):线程对象被创建后,调用该对象的start()方法,并获取CPU权限进行执行。
阻塞状态(BLOCKED):线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
等待状态(WAITING ):等待状态。正在等待另一个线程执行特定动作来唤醒该线程的状态。
超时等待状态(TIME_WAITING):有明确结束时间的等待状态。
终止状态(TERMINATED ):当线程结束完成之后就会变成此状态。

线程的操作方法

start:使该线程开始执行,Java虚拟机底层调用该线程的start0( )方法;

run:调用线程对象run方法。start底层会创建新的线程,run是一个简单的方法调用,不会启动新线程。

sleep:在指定的毫秒数内让当前正在执行的线程休眠;醒来后进入就绪状态。

interrupt:中断线程,但并没有真正结束线程,所以一般用于中断正在休眠线程。

yield:线程的礼让。yield()方法的执行会让当前线程从“运行状态”回到“就绪状态”。

join:线程的插队

守护线程

线程分为用户线程和守护线程。虚拟机必须确保用户线程执行完毕。虚拟机在非守护线程都结束时,守护线程会陆续结束。

线程同步

线程安全

什么是线程安全性
当多个线程访问某个类时,不管运行时环境采用 何种调度方式 或者这些进程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类就是线程安全的。
线程安全性的三个体现
原子性:提供互斥访问,同一时刻只能有一个线程对数据进行操作(Atomic、CAS算法、synchronized、Lock)
可见性:一个主内存的线程如果进行了修改,可以及时被其他线程观察到(synchronized、volatile)
有序性:如果两个线程不能从 happens-before原则 观察出来,那么就不能观察他们的有序性,虚拟机可以随意的对他们进行重排序,导致其观察观察结果杂乱无序(happens-before原则)

线程同步机制

解决线程并发问题的方法是线程同步,线程同步就是让线程排队,就是操作共享资源要有先后顺序,一个线程操作完之后,另一个线程才能操作或者读取。

使用synchronized关键字, 同步方法或者同步代码块。需要唯一的同步监视器。

买票问题

public class MyThread1 extends Thread{
    public MyThread1(String name) {
        super(name);
    }

    static int ticket=0;
    static Lock lock=new ReentrantLock();

    @Override
    public void run() {
       while (true){
           lock.lock();
           if(ticket==100)
               break;
           else {
               try {
                   Thread.sleep(10);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
               ticket++;
               System.out.println(getName()+"在卖第"+ticket+"张票");
               lock.unlock();
           }
       }
    }
}
        MyThread1 thread=  new MyThread1("窗口1");
        MyThread1 thread1=new MyThread1("窗口2");
        MyThread1 thread2=new MyThread1("窗口3");
        thread.start();
        thread1.start();
        thread2.start();

注:

Volatile关键字的作用主要有如下两个:
1.线程的可见性:当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。
2. 顺序一致性:禁止指令重排序。

死锁

多个线程各自占有一个资源,并且相互等待其他线程占有的资源才能运行,从而导致另个或者多个线程都在等待对方释放资源,都停止了执行。某一个同步代码块同时拥有“两个以上对象的锁”时,就可能会发生“死锁”的问题。

Lock 锁也称同步锁,java.util.concurrent.locks.Lock 机制提供了⽐ synchronized 代码块和 synchronized ⽅法更⼴泛的锁定操作,同步代码块 / 同步⽅法具有的功能 Lock 都有,除此之外更强⼤,更体现⾯向对象。创建对象 Lock lock = new ReentrantLock() ,加锁与释放锁⽅法如下:
public void lock() :加同步锁
public void unlock() :释放同步锁

synchronized和Lock的对比:
Lock是显式锁(手动开启和关闭锁,别忘记关闭),synchronized是隐式锁,除了作用域就自动释放。
Lock只是代码块锁(执行体放在开启锁和关闭锁中间),synchronized有代码块锁和方法锁。
使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)。
优先使用顺序:Lock > 同步代码块(已经进入了方法体,分配了相应资源) > 同步方法(在方法体之外)。

线程池

经常创建和销毁线程,消耗特别大的资源,比如并发的情况下的线程,对性能影响很大。线程池就是问题为了解决这个问题,提前创建好多个线程,放在线程池中,使用时直接获取,使用完放回线程池中,可以避免频繁的创建、销毁,实现重复利用。

       ExecutorService executorService = Executors.newCachedThreadPool();
        MyRunnable  myThread2=new MyRunnable();
        executorService.submit(myThread2);

阻塞队列

他也是队列的一种,那么他肯定是一个先进先出(FIFO)的数据结构。与普通队列不同的是,他支持两个附加操作,即阻塞添加阻塞删除方法。BlockingQueue是实现了Queue接口,一般使用两种如下。

  • ArrayBlockingQueue数组构成的有界阻塞队列

  • LinkedBlockingQueue链表构成的有界阻塞队列

阻塞添加:当阻塞队列是满时,往队列里添加元素的操作将被阻塞。

阻塞移除:当阻塞队列是空时,从队列中获取元素/删除元素的操作将被阻塞。

生产
add、offer、put这3个方法都是往队列尾部添加元素,区别如下:
add:不会阻塞,添加成功时返回true,不响应中断,当队列已满导致添加失败时抛出IllegalStateException。
offer:不会阻塞,添加成功时返回true,因队列已满导致添加失败时返回false,不响应中断。
put:会阻塞会响应中断。
消费
take、poll方法能获取队列头部第1个元素,区别如下:
take:会响应中断,会一直阻塞直到取得元素或当前线程中断。
poll:会响应中断,会阻塞,阻塞时间参照方法里参数timeout.timeUnit,当阻塞时间到了还没取得元素会返回null

综合练习(leetcode)

给你一个类:

public class Foo {
public void first() { print("first"); }
public void second() { print("second"); }
public void third() { print("third"); }
}
三个不同的线程 A、B、C 将会共用一个 Foo 实例。

线程 A 将会调用 first() 方法
线程 B 将会调用 second() 方法
线程 C 将会调用 third() 方法
请设计修改程序,以确保 second() 方法在 first() 方法之后被执行,third() 方法在 second() 方法之后被执行。

输入:nums = [1,2,3]
输出:"firstsecondthird"
解释:
有三个线程会被异步启动。输入 [1,2,3] 表示线程 A 将会调用 first() 方法,线程 B 将会调用 second() 方法,线程 C 将会调用 third() 方法。正确的输出是 "firstsecondthird"。

引用leetcode精选代码

public class Foo {
    private volatile int flag = 1;
    //创建一把锁
    private final Object object = new Object();

    public Foo() {

    }

    public void first(Runnable printFirst) throws InterruptedException {
        synchronized (object) {
            while (flag != 1){
                //如果当前标志不是1,那就阻塞当前线程,释放锁,等待被唤醒再重新执行
                //被唤醒的线程需要重新竞争锁对象,获得锁的线程可以从wait处继续往下执行
                object.wait();
            } 
            printFirst.run();
            flag = 2;
            object.notifyAll();
        }
    }

    public void second(Runnable printSecond) throws InterruptedException {
        synchronized (object) {
            while (flag != 2){
                //如果当前标志不是2,那就阻塞当前线程,释放锁,等待被唤醒再重新执行
                //被唤醒的线程需要重新竞争锁对象,获得锁的线程可以从wait处继续往下执行
                object.wait();
            } 
            printSecond.run();
            flag = 3;
            object.notifyAll();
        }
    }

    public void third(Runnable printThird) throws InterruptedException {
        synchronized (object) {
            while (flag != 3){
                //如果当前标志不是3,那就阻塞当前线程,释放锁,等待被唤醒再重新执行
                //被唤醒的线程需要重新竞争锁对象,获得锁的线程可以从wait处继续往下执行
                object.wait();
            } 
        }
        printThird.run();
    }
}

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

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

相关文章

I.MX6ULL_Linux_驱动篇(32) 设备树GPIO驱动

在前面章节中,我们直接在驱动文件 newchrled.c 中定义有关寄存器物理地址,然后使用 io_remap 函数进行内存映射,得到对应的虚拟地址,最后操作寄存 器对应的虚拟地址完成对 GPIO 的初始化。本章我们使用设备树来向 Linux 内核传递相…

劝退还是坚守?计算机视觉行业综述

劝退还是坚守?计算机视觉行业综述 1 从炙手可热到充满争议 计算机视觉(Computer Vision,简写为CV)是一门研究如何让计算机从图像或图像序列中获取信息并 理解其信息的学科,其主要目的在于从图像或图像序列中提取对世…

基于51单片机AT89C51的小型音乐喷泉控制系统设计

wx供重浩:创享日记 对话框发送:单片机小喷泉 获取完整无水印论文报告(内含电路原理图和程序) 根据目前音乐喷泉的发展现状,介绍了一个以AT89C51单片机为核心的小型音乐喷泉控制系统。给出了一个简洁的单片机控制电路&a…

Java_Spring:9. 基于 XML 的 AOP 配置

目录 1 环境搭建 1.1 第一步:准备必要的代码 1.2 第二步:拷贝必备的 jar 包到工程的 lib 目录 1.3 第三步:创建 spring 的配置文件并导入约束 1.4 第四步:配置 spring 的 ioc 1.5 第五步:抽取公共代码制作成通知 …

数据结构与算法笔记--数据结构与算法基本知识

目录 1--数据结构 2--算法 3--算法分析 4--实例1:普通算法与秦九韶算法的运算效率比较 5--实例2:最大子列和问题 5-1--暴力求解法 5-2--分而治之 5-3--动态规划 5-4--完整代码 1--数据结构 定义:所有数据元素以及数据元素之间的关系…

JS手写Promise(详细过程)

PS:JS手写Promise方法的整理在下一篇文章 手写Promise的API(resolve,reject,then,catch,finally,all)_Eric加油学!的博客-CSDN博客 1、基础版Promise 首先,通过一个简单的Promise例子回顾其使用 const promise new Promise((resolve, rej…

为什么诚信是项目管理的关键部分?

由于有许多需要指导的活动部件和风险,管理一个新项目可能是一项具有挑战性的工作。在一些对质量有着严格要求的行业,项目结构、设定目标、跟踪状态、风险管理和资源管理等项目管理原则尤为重要,而领导这项工作的是诚信。那么,究竟…

IP 归属用 Ip2region 就够了

文章目录Ip2region 简介是什么特性支持的编程语言案例实操依赖获取IP输入流转化解析IP测试抖音、微博、小红书等各平台相继上线" 网络用户IP地址显示功能", 境外显示 国家, 境内显示到 省市,且该功能无法关闭,IP地址为强…

【新2023Q2模拟题JAVA】华为OD机试 - 分苹果

最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为od机试,独家整理 已参加机试人员的实战技巧本篇题解:分苹果 题目 AB两个人把苹果…

第16章_变量、流程控制与游标

第16章_变量、流程控制与游标 🏠个人主页:shark-Gao 🧑个人简介:大家好,我是shark-Gao,一个想要与大家共同进步的男人😉😉 🎉目前状况:23届毕业生&#xf…

ClickHouse学习笔记(三):MergeTree 原理解析

文章目录1、简介2、MergeTree 创建方式与存储结构2.1、MergeTree 的创建方式2.2、MergeTree 的存储结构3、MergeTree 数据分区3.1、分区目录的命名规则3.2、分区目录合并过程4、一级索引4.1、索引粒度4.2、索引生成4.3、索引查询5、二级索引6、数据存储7、数据标记8、协同总结8…

BootStrap4:栅格系统

1、container容器 container:固定宽度的容器container-fluid:响应式容器 2、栅格选项 Bootstrap4 总共有五个栅格等级,每个响应式分界点隔出一个等级 Ps:.row上带有margin-left: -15px;margin-right: -15px;属性,你…

【22年蓝桥杯】十三届蓝桥杯真题JavaB组解析+代码(带你复习知识点)(一)

试题 A: 星期计算 【填空题】 答案:7 解析:直接对所给数进行取余,然后直接再加6(注意:不能直接让20^226再对7进行取余操作,这是不对的,这个6可以看成已经取余过了。) 直接取余的话可…

Linux系统安装部署及配置Grafana

TOC 用于 UI 展示 wget https://dl.grafana.com/oss/release/grafana-8.0.3-1.x86_64.rpm1 安装 grafana 1.1 下载安装 wget https://dl.grafana.com/oss/release/grafana-8.0.3-1.x86_64.rpmsudo yum install grafana-8.0.3-1.x86_64.rpm1.2 启动&状态查看 sudo syst…

PHP初级教程------------------(3)

目录 文件包含 文件包含的作用 文件包含四种形式 文件加载原理 Include和require区别 文件加载路径 文件嵌套包含 函数 函数的基本概念 函数定义语法 函数命名规范 参数详解 形参 实参 默认值 引用传递 函数体 函数返回值 ​作用域 静态变量 可变函数 匿名函数 闭包 伪类型 文件…

作为一个数学专业的学生,我是怎么看待编程的?

1.概况 博主的专业是数学与应用数学,简称应数。虽然后面跟了个应用数学,但是这个专业应该是本科阶段最接近数学的专业了。我认为这个专业使我具有如下的几个优势: 数学的学习使我具有较强的思维能力。编程本质上就是通过写代码的方式来解决…

大数据Flink进阶(八):Apache Flink架构介绍

Apache Flink架构介绍 一、Flink组件栈 在Flink的整个软件架构体系中,同样遵循这分层的架构设计理念,在降低系统耦合度的同时,也为上层用户构建Flink应用提供了丰富且友好的接口。

山东大学机器学习大作业

数据处理与可视化这里是DLRM模型数据集预处理模块:args.ln_emb ln_emb.tolist() m_spa args.arch_sparse_feature_sizeln_emb np.asarray(ln_emb)num_fea ln_emb.size 1 # num sparse num dense featuresm_den_out ln_bot[ln_bot.size - 1]Sparse fea 26, D…

Java设计模式-3、单例模式

单例模式 单例模式属于创建型模式,⼀个单例类在任何情况下都只存在⼀个实例, 构造⽅法必须是私有的、由⾃⼰创建⼀个静态变量存储实例,对外提供⼀ 个静态公有⽅法获取实例。 优点是内存中只有⼀个实例,减少了开销,尤…

代码随想录|day26|回溯算法part03● 39. 组合总和● 40.组合总和II● 131.分割回文串

今天的练习基本就是回溯法组合问题,这一节只要看labuladong即可。 组合问题: 39. 组合总和---------------------形式三,元素无重可复选 链接:代码随想录 一次对,同样在进入下次循环时,注意startindex是从j…