java多线程基础

news2025/1/12 20:36:52

java多线程基础

    • 1. 线程是什么
    • 2. 线程的创建和运行
      • 方式1:继承Thread类
        • 示例:
      • 方式2:实现Runnable接口(推荐)
        • 示例:
    • 3. Thread类的常用方法
    • 4. 线程插队
      • (1)yield 当前线程把时间片让给其它线程,不一定成功
        • 示例:
      • (2)join 足够强势的插队,一定成功
        • 示例:
    • 5. 线程同步的两种方式
      • (1) synchronized修饰方法
        • 示例:
      • (2) synchronized修饰代码块
        • 示例1:使用this作为Object对象
        • 示例2:使用唯一且共享的Object对象
        • 示例3:使用当前类对象

1. 线程是什么

线程是进程中的一个执行流,如果将进程比作一个工厂的加工车间,那么线程就可以看成是其中的一条流水线。线程是程序运行的最小单位。

2. 线程的创建和运行

方式1:继承Thread类

一个类继承Thread类并重写run方法,这个类创建的对象就可以当作线程类使用,通过调用start方法启动一个线程,并执行重写的run方法。
注意不能直接调用run方法启动线程,run方法只是一个普通的成员方法,不会创建线程。

示例:

// 线程的第一种使用方式,继承Thread类并重写run方法
class Dragon extends Thread {
    @Override
    public void run() {
        int times = 0;
        while (true) {
            System.out.println("龙开始第" + (++times) + "次喷火! " + "当前线程名: " + Thread.currentThread().getName());

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(times == 9){
                System.out.println("龙烧死了对手");
                break;
            }
        }
    }
}

public class ThreadTest1 {
    public static void main(String[] args) {
        System.out.println("当前线程名: " + Thread.currentThread().getName());
        Dragon dragon = new Dragon();
        dragon.start();
    }
}

运行结果:
在这里插入图片描述

方式2:实现Runnable接口(推荐)

一个类可以实现Runnable接口,并实现run方法,这个类就作为Runnable的子类,可以使用该类对象创建一个Thread类对象,进而通过Thread对象调用新实现的run方法,因为Thread有一个构造方法只有一个Runnable类型的参数,在调用Thead的start方法时,在创建线程后,它会调用Runnable的run方法。
注意不能直接调用run方法启动线程,run方法只是一个普通的成员方法,不会创建线程。

示例:

接口:

public interface FlyAction {
    void fly();
}
public interface RunAction {
    void runOnEarth();
}

线程类和测试代码:

class Animal{}

// 线程的第二种调用方式,实现Runnable接口,实现run接口,适合继承其它类的情况
class Bird extends Animal implements FlyAction, RunAction, Runnable{
    @Override
    public void run() {
        int times = 0;
        while(true){
            System.out.println("鸟的年龄是第" + (++times) + "天~当前线程名:" + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(times == 3){
                System.out.println("鸟可以跑了");
                runOnEarth();
            }else if(times == 5){
                System.out.println("鸟可以飞了");
                fly();
            }else if(times == 10){
                System.out.println("鸟长大了");
                break;
            }
        }
    }

    @Override
    public void fly() {
        System.out.println("鸟在飞");
    }

    @Override
    public void runOnEarth() {
        System.out.println("鸟在地上跑");
    }
}


public class ThreadTest2 {
    public static void main(String[] args) {
        Bird bird = new Bird();
        //需要通过一个Thread对象来调用
        Thread thread = new Thread(bird);
        thread.start();
    }
}

运行结果:
在这里插入图片描述

3. Thread类的常用方法

方法名方法说明参数返回值
sleep让当前线程休眠,静态方法毫秒数void
setName设置线程名,成员方法线程名(String类型)void
getName获取线程名,成员方法线程名(String类型)
currentThread获取当前执行的线程的引用,静态方法当前执行的线程
start创建线程并调用线程入口方法run,成员方法void
setPriority修改线程优先级(10最高1最低),成员方法新的优先级void
getPriority获取当前线程的优先级,成员方法当前线程优先级

4. 线程插队

当需要将特定的线程先运行,给该线程分配时间片时,就可以调用yield或join方法

(1)yield 当前线程把时间片让给其它线程,不一定成功

调用yield方法的线程不一定后运行,会受到CPU调度策略的影响

示例:

主线程打印5次hi之后把时间片交给子线程,子线程独占时间片并打印hi

class ThreadJoinDemo implements Runnable{
    @Override
    public void run() {
        for(int cnt = 0; cnt < 10; ++cnt){
            System.out.println("hello");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class ThreadJoinTest {
    public static void main(String[] args) {
        ThreadJoinDemo threadJoinDemo = new ThreadJoinDemo();
        Thread thread = new Thread(threadJoinDemo);
        thread.start();

        int cnt = 0;
        while(cnt < 10) {
            System.out.println("hi");
            ++cnt;
            if(cnt == 5){
                System.out.println("main线程开始让出时间片");
                //当前线程让出时间片
                Thread.currentThread().yield();
                System.out.println("main线程让出时间片结束");
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

运行结果:
在这里插入图片描述
结果分析:
子线程并没有独占时间片,因为此时CPU资源足够两个线程同事运行,CPU就不会让main线程停止运行,等待子线程运行结束

(2)join 足够强势的插队,一定成功

调用join方法的线程先分得时间片,运行完之后运行其它线程

示例:

main线程执行5次打印语句后,子线程执行10次打印语句,子线程执行完毕之后父线程再执行

class SonThread implements Runnable{
    @Override
    public void run() {
        for(int cnt = 0; cnt < 10; ++cnt){
            System.out.println("我是子线程, hello " + (cnt + 1));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class ThreadJoinTest2 {
    public static void main(String[] args) {
        SonThread sonThread = new SonThread();
        Thread thread = new Thread(sonThread);

        int cnt = 0;
        while (cnt < 10){
            System.out.println("我是main线程, hi " + (cnt + 1));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            ++cnt;
            if(cnt == 5){
                thread.start();
                try {
                    System.out.println("子线程开始运行");
                    thread.join();
                    System.out.println("子线程结束");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

运行结果:
在这里插入图片描述

5. 线程同步的两种方式

当多个线程对同一份资源(临界资源)进行访问时,可能会出现两个线程同时对临界资源进行修改,这样可能会造成临界资源状态的异常,因此需要让一些临界资源在同一时间只能由一个线程访问,这就是线程同步。
线程同步的两种实现方式如下:

(1) synchronized修饰方法

当在一个方法的返回值类型之前使用synchronized修饰,那么这个方法在同一时刻只能让一个线程调用,其它线程需要等待这个线程调用结束后才能调用。

示例:


class SellThread implements Runnable{
    private static int tickets = 100;
    private static boolean isNotSellOver = true;
    @Override
    public void run() {
        while(isNotSellOver){
           sellTask();
        }
    }

    public synchronized void sellTask(){
        if(tickets <= 0){
            isNotSellOver = false;
            return;
        }
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "卖出了一张票,还有" + (--tickets) + "张票");
    }
}

public class ThreadSyn1 {
    public static void main(String[] args) {
        SellThread sellThread = new SellThread();
        Thread thread1 = new Thread(sellThread);
        Thread thread2 = new Thread(sellThread);
        Thread thread3 = new Thread(sellThread);
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

(2) synchronized修饰代码块

可以将synchronized(Object对象)放在代码块之前来修饰代码块,表示这个代码块同一时刻只能有一个线程访问。
重要的是,要想达到对临界资源进行保护的目的需要使用同一个Object对象
静态方法中的代码块可以使用当前类作为Object对象
非静态方法可以使用只有一份的Object对象例如this

示例1:使用this作为Object对象

class ThreadDemo2 implements Runnable{
    private static int tickets = 100;

    @Override
    public void run() {
        while(true){
            synchronized (this) {
                if (tickets <= 0) {
                    break;
                }
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "卖出了一张票,还有 " + (--tickets) + "张");
            }
        }
    }
}

public class SynchronizedThreadTest2{
    public static void main(String[] args) {
        ThreadDemo2 threadDemo = new ThreadDemo2();
        Thread thread = new Thread(threadDemo);
        Thread thread2 = new Thread(threadDemo);
        Thread thread3 = new Thread(threadDemo);
        thread.start();
        thread2.start();
        thread3.start();
    }
}

示例2:使用唯一且共享的Object对象

class ThreadDemo implements Runnable{
    private static int tickets = 100;
    private final static Object object = new Object();

    @Override
    public void run() {
        while(true){
            synchronized (object) {
                if (tickets <= 0) {
                    break;
                }
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "卖出了一张票,还有 " + (--tickets) + "张");
            }
        }
    }
}

public class SynchronizedThreadTest{
    public static void main(String[] args) {
        ThreadDemo threadDemo = new ThreadDemo();
        Thread thread = new Thread(threadDemo);
        Thread thread2 = new Thread(threadDemo);
        Thread thread3 = new Thread(threadDemo);
        thread.start();
        thread2.start();
        thread3.start();
    }
}

示例3:使用当前类对象

class ThreadDemo3 implements Runnable{
    private static int tickets = 100;
    private static boolean isNotSelledOver = true;
    @Override
    public void run() {
        while(isNotSelledOver){
            doTask();
        }
    }

    public static void doTask(){
        synchronized (ThreadDemo3.class) {
            if (tickets <= 0) {
                isNotSelledOver = false;
                return;
            }
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "卖出了一张票,还有 " + (--tickets) + "张");
        }
    }
}

public class StaticMethodSynchronizedThreadTest {
    public static void main(String[] args) {
        ThreadDemo3 threadDemo = new ThreadDemo3();
        Thread thread = new Thread(threadDemo);
        Thread thread2 = new Thread(threadDemo);
        Thread thread3 = new Thread(threadDemo);
        thread.start();
        thread2.start();
        thread3.start();
    }
}

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

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

相关文章

数据库基础-Mongodb数据库复制操作

Mongodb数据库复制操作 关闭mongodb的服务,如下图 创建以下文件夹 现在我们开启三个服务,端口号为9927做为主节点,9928做为从节点,9929做为仲裁节点 仲裁节点的作用是协调leader选举&#xff0c;监测系统运行状态&#xff0c;提供节点互相通讯的数据信息。 开启主服务: m…

【视觉高级篇】24 # 如何模拟光照让3D场景更逼真?(下)

说明 【跟月影学可视化】学习笔记。 什么是镜面反射&#xff1f; 如果若干平行光照射在表面光滑的物体上&#xff0c;反射出来的光依然平行&#xff0c;这种反射就是镜面反射。越光滑的材质&#xff0c;它的镜面反射效果也就越强&#xff0c;并且物体表面会有闪耀的光斑&…

Windows系统下HTTP(S)透明代理

本文为joshua317原创文章,转载请注明&#xff1a;转载自joshua317博客 Windows系统下HTTP(S)透明代理 - joshua317的博客 软件文档地址:goproxy/README_ZH.md at master snail007/goproxy GitHub 一、windows系统下进行下载及安装 分别有两个版本&#xff1a;proxy-admin …

Servlet学习笔记

1.在pom.xml中添加依赖 <dependencies><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><!-- 不会最终打包到服务器中去 --><scope>provided&…

SpringBoot内置tomcat启动过程及原理

作者&#xff1a;李岩科 1 背景 SpringBoot 是一个框架&#xff0c;一种全新的编程规范&#xff0c;他的产生简化了框架的使用&#xff0c;同时也提供了很多便捷的功能&#xff0c;比如内置 tomcat 就是其中一项&#xff0c;他让我们省去了搭建 tomcat 容器&#xff0c;生成 …

和年薪30W的阿里测试员聊过后,才知道自己一直在打杂...

前几天和一个朋友聊面试&#xff0c;他说上个月同时拿到了腾讯和阿里的offer&#xff0c;最后选择了阿里。 阿里内部将员工一共分为了14个等级&#xff0c;P6是资深工程师&#xff0c;P7是技术专家。 其中P6和P7就是一个分水岭了&#xff0c;P6是最接近P7的不持股员工&#x…

JavaScript-DOM操作表格

DOM操作表格的用途 DOM操作表格会在项目做数据展示的时候用到&#xff0c;其余地方使用并不多。 表格内容 <table><thead><tr><th>编号</th><th>姓名</th><th>性别</th><th>年龄</th></tr></thead…

二叉树遍历非递归算法

二叉树遍历非递归算法 文章目录二叉树遍历非递归算法二叉树的遍历一、先序遍历非递归算法算法构思&#xff1a;从先序遍历的递归算法得出循环算法的思路:下面进行框架构建:代码实操:二、中序遍历(左-根-右)非递归算法中序遍历二叉树的过程构建思路:根据以上思路&#xff0c;构建…

vscode 安装clangd插件 替代 c++自带插件

目录 1. 背景 2. 安装clangd 安装前&#xff1a;禁用c插件 2.1 clangd插件名称 2.2 安装 2.3 配置 settings.json 2.4 语言服务器下载 2.5 安装 cmake tools 2.6 设置编译选项 3. 生成 compile_command.json 4. 查看使用效果 1. 背景 vscode c开大家一般用 vscode 自家…

磨金石教育摄影技能干货分享|乡愁摄影作品欣赏

乡愁是是什么&#xff1f; 我们走在异乡的街道上&#xff0c;人声嘈杂的一瞬间&#xff0c; 或许是某个角落&#xff0c;或许是某个人的声音&#xff0c; 让你感到无比的熟悉&#xff0c;在你的记忆深处掀起了一阵阵浪花。 这个熟悉的感觉就是乡愁 它可以是家乡的一棵树 …

JUC(5) : ForkJoinPool | 线程的极致管理

一、前言 前文介绍了线程的异步编排工具类 CompletableFuture 的使用&#xff0c;使用它能够很好的完成线程任务的编排工作&#xff0c;但同时&#xff0c;我们也注意到&#xff0c;其使用的默认线程池是 ForkJoinPool.commonPool() 的方法。则这个线程池是共用的&#xff0c;…

一个普通前端的2022年终总结:多病的一年

多病 用一个词总结我的2022 &#xff0c;毫无疑问是【多病】。 翻看挂号记录&#xff0c;今年累计跑了19次医院&#xff0c;除去定期的脱发复查、尿常规复查外&#xff0c;其他还得了皮肤病、急性咽炎、筋膜炎、结膜炎、肾结石、慢性胃炎、胸闷&#xff0c;体验过了无法忍受的…

基于java+springmvc+mybatis+jsp+mysql的网络作者与美工交流平台

项目介绍 本次设计任务是要设计一个网络作者与美工交流平台&#xff0c;通过这个系统能够满足网络作者与美工交流信息的管理及版主的网络作者与美工交流信息管理功能。系统的主要功能包括&#xff1a;主页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;版主管理&#…

text文本属性

text文本属性 源代码 color color属性用于定义文本的颜色&#xff0c;有预定义的颜色值(red, blue, yellow)、十六进制(#FF0000, #FF6600,#29D794)、RBG代码(rgb(255,0,0)或rgb(100%,0%,0%)) text-align text-align属性用于设置元素内文本的水平对…

R语言对BRFSS数据探索回归数据分析

执行摘要 最近我们被客户要求撰写关于BRFSS的研究报告&#xff0c;包括一些图形和统计输出。该项目包括探索一个现实世界的数据集-CDC的2013年 行为风险因素监视系统 -并针对三个 选择的研究问题创建报告。 选择的研究问题及其各自的结果是&#xff1a; 被访者对其健康状况…

Redis框架(一):Redis入门和Jedis连接池

Redis入门和Jedis连接池&#xff1a;基本介绍实例Demo源码分析SpringCloud章节复习已经过去&#xff0c;新的章节Redis开始了&#xff0c;这个章节中将会回顾Redis 主要依照以下几个原则 基础实战的Demo和Coding上传到我的代码仓库在原有基础上加入一些设计模式&#xff0c;st…

c#扩展方法

1、前言: 通常,我们想要向一个类型中添加方法,可以通过以下两种方式: 修改源代码。 在派生类中定义新的方法。 但是以上方法并不是万能的,我们并不能保证拥有一个类型的源码,也并不能保证这个类型可以让我们继承(如结构,枚举,String等等)。但是C#提供了一个办法,…

教你如何写一个符合自己需求的小程序日历组件

1|0 前言 很多时候&#xff0c;我们生活中会有各种打卡的情况&#xff0c;比如 keep 的运动打卡、单词的学习打卡和各种签到打卡或者酒店的入住时间选择&#xff0c;这时候就需要我们书写一个日历组件来处理我们这种需求。 但是更多时候&#xff0c;我们都是网上找一个插件直…

【HBase】【一】windows搭建源码开发环境

目录环境配置1. Windows安装Cygwin2. 安装ProtocolBuffers3. 启动zookeeper4. 搭建Hadoop环境5. 编译Hbase源码6. 启动HRegionServer7. 启动HMaster8. 启动HShell客户端环境配置 系统&#xff1a;windows10 IDE: Eclipse hadoop: 3.3.4 hbase: 2.4.15 java: 17 1. Window…

pytest学习——pytest插件的7种用法

1.pytest-repeat 重复跑 安装包 pip install pytest-repeat第一种用法&#xff1a; 装饰器 pytest.mark.repeat(次数) 示例代码 import pytest pytest.mark.repeat(5) def test_001(): assert 12 if __name__ __main__: pytest.main([-sv,__file__])第二种用法&#xff1a…