14. Java多线程基础

news2025/1/23 2:14:48

Java —— 多线程

        • 1. 线程与进程
          • 1.1 线程生命周期
          • 1.2 线程优先级
        • 2. 多线程
          • 2.1 守护线程
          • 2.2 多线程高并发
          • 2.3 synchronized同步锁
          • 2.4 synchronized互斥锁

1. 线程与进程
进程(Process) 操作系统进行资源分配和调度的基本单位:系统中正在运行的程序实例,一个程序可以同时启动多个进程
线程(Thread) CPU调度的基本单位:操作系统能够进行运算调度的最小单位

进程:进程是指正在运行的程序的实例

  • 特点:1. 每个进程都有独立的内存空间(程序代码、执行上下文、数据和资源)
       2. 一个程序可以同时启动多个进程(每个进程之间相互独立,彼此不会直接共享内存)
       3. 进程拥有自己的地址空间和系统资源(操作系统进行资源分配和调度的基本单位)
       4. 进程之间通过进程间通信(IPC)机制来交换数据和信息

线程:线程是进程中的执行单元

  • 特点:1. 一个进程可以包含多个线程,它们可以共享进程的上下文和资源
       2. 线程拥有自己的栈空间,但共享进程的代码段、数据段和堆空间
       3. 线程之间的通信和同步比进程之间更加高效
       4. 线程在执行过程中可以独立地完成一系列任务
       5. 多个线程可以并发执行,提高程序的吞吐量和响应速度

1. 进程之间通信开销较大,线程之间通信开销较小
2. 使用多线程可以充分利用多核处理器的性能

线程的3种创建方式

  1. 继承Thread类
  2. 实现Runnable接口
  3. 使用Callable和future创建
  • 线程的创建(2种演示)
package thread;

public class ThreadDemo {
    public static void main(String[] args) {
        // 实例化线程
        Thread t1 = new Thread1();
        Thread t2 = new Thread2();

        // 启动线程
        t1.start();
        t2.start();

        // 创建线程执行任务
        MyRunnable r1 = new MyRunnable();
        MyRunnables r2 = new MyRunnables();
        // 创建线程
        Thread t3 = new Thread(r1);
        Thread t4 = new Thread(r2);
        // 启动线程
        t3.start();
        t4.start();

        // ==============匿名内部类====================
        // 实例化(匿名内部类)
        Thread tl = new Thread(){
            @Override
            public void run() {
                super.run();
            }
        };
        // 实例化(lambda表达式)
        Runnable rl = () -> System.out.println("lambda表达式");
        // 创建线程
        Thread t = new Thread(rl);

        // 启动线程
        tl.start();
        t.start();


    }
}


// ====================第一种创建线程的方法==================
// 线程1
class Thread1 extends Thread {
    public void run() {
        for (int i = 0; i < 10000; i++) {
            System.out.println("你好");
        }
    }
}

// 线程2
class Thread2 extends Thread {
    public void run() {
        for (int i = 0; i < 10000; i++) {
            System.out.println("hello world");
        }
    }
}


// ====================第二种创建线程的方法==================
// 实现runnable接口
class MyRunnable implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            System.out.println("你好");
        }
    }
}

class MyRunnables implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            System.out.println("hello world");
        }
    }
}
1.1 线程生命周期

在这里插入图片描述

名称状态说明
新建(New)当线程对象被创建但还未开始执行时,处于新建状态此时线程没有被分配CPU资源
就绪(Runnable)在调用线程的start()方法后,线程进入就绪状态此时线程已经被分配CPU资源,但还未开始执行,等待调度器进行调度
运行(Running)就绪状态的线程被调度器选中后,线程进入运行状态开始执行run()方法中的任务
阻塞(Blocked)线程在运行过程中,可能因为某些原因而暂停执行,称为阻塞状态常见的情况包括等待IO操作、等待获取锁、等待其他线程的通知等(释放CPU资源)
等待(Waiting)线程执行wait()方法或类似方法后,线程进入等待状态此时线程会释放CPU资源并等待其他线程的通知,直到被唤醒
超时等待(Timed Waiting)线程执行sleep()、join()、LockSupport.parkNanos()等方法时,线程进入超时等待状态与等待状态类似,线程也会释放CPU资源,但在一定时间后会自动唤醒
结束(Terminated)线程执行完run()方法中的任务或者出现异常导致线程终止时,线程进入结束状态已经结束的线程不能再重新启动
1.2 线程优先级

优先级的设置和获取Thread类中的setPriority()getPriority()方法
优先级范围:1到10(1为最低优先级,10为最高优先级),默认情况下,线程的优先级与创建它的父线程的优先级相同

使用setPriority()方法设置线程的优先级:

Thread thread = new Thread();

thread.setPriority(Thread.MAX_PRIORITY); // 设置线程的优先级为最高优先级
thread.setPriority(Thread.MIN_PRIORITY);
thread.setPriority(Thread.NORM_PRIORITY); // 默认优先级

使用getPriority()方法获取线程的优先级:

int priority = thread.getPriority(); // 获取线程的优先级

注意
1. 线程优先级仅提供一个指示给调度器的建议(不是高优先级的线程一定会先执行)
2. 具体的线程调度由操作系统和JVM决定(可能会受到系统负载、线程状态等因素的影)

  • 线程优先级
package thread;

public class PriorityThread {
    public static void main(String[] args) {
        // 最大优先级
        Thread max = new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    System.out.println("max");
                }
            }
        };

        // 最小优先级
        Thread min = new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    System.out.println("min");
                }
            }
        };

        // 默认优先级
        Thread norm = new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    System.out.println("nor");
                }
            }
        };

        // 设置优先级
        max.setPriority(Thread.MAX_PRIORITY);
        min.setPriority(Thread.MIN_PRIORITY);
        max.start();
        norm.start();
        min.start();
    }
}

start()和run()方法
  start()方法:
      start()方法是Thread类中的一个方法,用于启动一个新的线程并执行其代码
      调用start()方法会创建一个新的线程,并在新线程中调用run()方法
在新线程中执行的代码由run()方法定义
  run()方法:
      run()方法也是Thread类中的一个方法,通常用于定义线程的任务或逻辑
      直接调用run()方法不会创建一个新的线程,而是在当前线程中以普通方法的方式执行run()方法的代码
区别
      start()方法会创建一个新的线程来执行任务,而run()方法在当前线程中执行
      调用start()方法后,线程会被添加到线程调度器的线程队列中,由线程调度器决定何时开始执行,而直接调用run()方法会立即开始执行
      使用start()方法可以充分利用多核处理器的并行能力,而直接调用run()方法则只能在单个线程中顺序执行

2. 多线程

线程控制
    sleep( ) : 线程休眠
    join( ) : 线程加入
    yield( ) : 线程礼让
    setDaemon( ) : 线程守护
中断线程
    stop( ): 线程停止
    interrupt( ):线程中断 (首先选用)

2.1 守护线程
守护线程 :不会阻止程序的终止,当所有非守护线程被终止时,守护线程也会被自动终止
临界资源:临界资源(Critical Resource)是指在多线程环境下被多个线程访问和操作的共享资源
常见临界资源
      多线程共享实例变量
      多线程共享静态公共变量
2.2 多线程高并发
高并发问题 :当多个线程并发操作同一临界资源,由于线程切换的时机不确定,导致执行顺序出现混乱产生不良后果
临界资源: 操作该资源的完整过程同一时刻只能被单个线程进行(被多个线程共享的资源)
常见临界资源
      多线程共享实例变量
      多线程共享静态公共变量
每个线程中对全局变量量、静态变量量只有读操作,⽽而⽆无写操作,一般来说,这个全局变量是线程安全的;
若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全
  • 取款问题 利用多线程的并发间隙,在柜台和ATM机同时取款,寻找取款金额大于实际余额的情况
package thread;

public class Bank {
    private int account = 20000;

    public synchronized boolean getMoney(int money) {
        int account = getAccount();
        if (account >= money) {
            account -= money;
            Thread.yield();
            saveAccount(account);
            return true;
        }
        return false;
    }

    public void saveAccount(int account) {
        this.account = account;
    }

    public int getAccount() {
        return account;
    }
}
package thread;

public class BankTest {
    static boolean s1;
    static boolean s2;
    static int sum = 0;

    // 测试
    public static void main(String[] args) {
        Bank bank = new Bank();
        while (true) {
            Thread t1 = new Thread() {
                @Override
                public void run() {
                    s1 = bank.getMoney(20000);
                }
            };
            Thread t2 = new Thread() {
                @Override
                public void run() {
                    s1 = bank.getMoney(20000);
                }
            };

            t1.start();
            t2.start();
            try {
                Thread.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (s1 && s2) {
                System.out.println("产生了漏洞,尝试了:" + sum + "次");
                break;
            } else {
                sum++;
                bank.saveAccount(20000);
                s1 = false;
                s2 = false;
            }
        }
    }
}
2.3 synchronized同步锁
  • 解决线程安全 —— 将异步操作转为同步操作
    • 异步操作:多线程并发,线程的执行互不干扰
    • 同步操作:线程有先后执行顺序,线程执行受到限制,排队等待执行

使用synchronized关键字实现线程之间的同步,同步锁

  • 使用语法:
    synchronized(同步监视器对象(必须是引用数据类型,否则会失效)){
      需要多个线程同步执行的代码片段;
    }

同步监视器对象的选取
  1. 所有需要同步执行同步块代码的线程看到的同步监视器对象必须是同一个
  2. 同步监视器对象只能是引用类型
合适的锁对象在满足锁基本条件
  1. 必须是引用类型
  2. 多个需要同步执行该代码块线程看到的必须是同一个对象
适当的同步有效性
  1. 存在并发安全时可以限制多个线程同步执行(发生并发安全时,多线程看到的该对象是同一个)
  2. 不存在不发安全时允许多个线程异步执行(不存在并发安全时,多线程看到的不应是同一个)
对成员方法(方法)同步时,同步监视器对象不可选,只能是this(当前对象)

  • synchronized同步锁
package thread;

public class Synchronize {
    public static void main(String[] args) {
        Shop shop = new Shop();
        Shop shops = new Shop();
        Thread t1 = new Thread(shop::buy, "王文");

        Thread t2 = new Thread("李雯") {
            @Override
            public void run() {
                shop.buy();
            }
        };

        Thread ts = new Thread(shops::buy, "王菲");

        t1.start();
        t2.start();
        // ts与t1和t2不是同一个店(不是同一个对象),不需要和t1t2排队(不需要同步)
        ts.start();
    }
}

class Shop {
    // 同步方法
//    public synchronized void buy(){
    public void buy() {
        Thread t = Thread.currentThread();
        try {
            System.out.println(t.getName() + "正在挑衣服...");
            Thread.sleep(5000);

            // 添加所进行阻塞等待释放(缩小同步范围),同步代码块,this是合适的锁对象,不同的对象互不干扰
             synchronized (this) {
            // 引用数据类型(字面量,不适合使用)
//            synchronized ("上锁对象") {
                System.out.println(t.getName() + "正在试衣服");
                Thread.sleep(2000);
            }

            System.out.println(t.getName() + "结账离开!");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

    }
}

静态方法使用了synchronized修饰时,一定具有同步效果

  1. 静态方法的调用:类名.方法名(),此时相当于同步锁锁的内容是相同的,执行同步操作
  2. 不同对象:对象1.方法名()
         对象2.方法名(),此时属于不同对象(常规多线程看到的不应是同一个,但是执行同步操作 —— 当做相同对象处理)

原因:
在synchronized修饰方法时,默认的同步锁对象为this,但是静态方法中没有this关键字,只能是类对象(class实例)
即静态方法的同步监视器对象:synchronized(类名.class) {}

  • 静态方法的synchronized同步锁
package thread;

public class SynchronizeStatic {
    public static void main(String[] args) {
        Boo boo1 = new Boo();
        Boo boo2 = new Boo();

        Thread t1 = new Thread(Boo::doSome, "线程1 ");
        Thread t2 = new Thread(() -> Boo.doSome(), "线程2 ");

        Thread t3 = new Thread(() -> boo1.doSome(), "线程3 ");
        Thread t4 = new Thread(() -> boo2.doSome(), "线程4 ");

        t1.start();
        t2.start();
    }
}

class Boo {
    //    public synchronized static void doSome(){
    public static void doSome() {
        synchronized (Boo.class) {
            try {
                Thread t = Thread.currentThread();
                System.out.println(t.getName() + "正在执行doSome方法");
                Thread.sleep(2000);
                System.out.println(t.getName() + "执行完毕");
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

在这里插入图片描述

  • StringBuilder线程不安全,StringBuffer线程安全
package thread;

public class BuiderAndBuffer {
    public static void main(String[] args) {
        // StringBuilder线程不安全
//        StringBuilder builder = new StringBuilder();

        // StringBuffer线程安全
        StringBuffer builder = new StringBuffer();
        Thread t1 = new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    builder.append("aaa");
                }
            }
        };

        Thread t2 = new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    builder.append("bbb");
                }
            }
        };


        // 启动线程
        t1.start();
        t2.start();

        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        System.out.println("数组长度:" + builder.length());
    }
}
  • 集合ArrayList,LinkedList,HashSet等线程不安全,并发安全转换
package thread;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class SyncSet {
    public static void main(String[] args) {
        // 集合ArrayList,HashSet等线程不安全
//        List<Integer> list = new ArrayList<>();

        // 将集合转为并发安全的(此时list不可改变,不可二次赋值)
        List<Integer> list = Collections.synchronizedList(new ArrayList<>());
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                list.add(i);
            }
        });


        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                list.add(i);
            }
        });

        t1.start();
        t2.start();

        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        System.out.println(list.size());
    }
}
2.4 synchronized互斥锁

互斥性:指多个线程执行不同的代码片段,但是这些代码片段不能同一时刻被执行
互斥性使用synchronized实现:
    1. 使用synchronized锁定多个需要互斥的代码片段
    2. 这些synchronized指定的同步监视器必须同一个对象
例:用synchronized分别对两个不同的方法进行上锁,然后使用同一个对象分别调用两个方法

  • 互斥锁的实现
package thread;

public class Mutex {
    public static void main(String[] args) {
        Foo f = new Foo();
        Thread t1 = new Thread(f::methodA);
        Thread t2 = new Thread(f::methodB);

        t1.start();
        t2.start();
    }
}

class Foo{
    public synchronized void methodA(){
        try {
            Thread t = Thread.currentThread();
            System.out.println(t.getName() + ":正在执行A");
            Thread.sleep(5000);
            System.out.println(t.getName() + ":A执行完毕");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    public synchronized void methodB(){
        try {
            Thread t = Thread.currentThread();
            System.out.println(t.getName() + ":正在执行B");
            Thread.sleep(3000);
            System.out.println(t.getName() + ":B执行完毕");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

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

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

相关文章

复制活动工作表和计数未保存工作簿进行

我给VBA下的定义&#xff1a;VBA是个人小型自动化处理的有效工具。可以大大提高自己的劳动效率&#xff0c;而且可以提高数据的准确性。我这里专注VBA,将我多年的经验汇集在VBA系列九套教程中。 作为我的学员要利用我的积木编程思想&#xff0c;积木编程最重要的是积木如何搭建…

基于springboot实现酒店管理系统平台项目【项目源码+论文说明】计算机毕业设计

摘要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&#xff0c;科学化的管理&#xff0c;使信息存…

C++算法前缀和的应用:分割数组的最大值的原理、源码及测试用例

分割数组的最大值 相关知识点 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例&#xff1a;付视频课程 二分 过些天整理基础知识 题目 给定一个非负整数数组 nums 和一个整数 m &#xff0c;你需要将这个数组分成 m 个非空的连续子数组。 设计一个算法…

【前端学习】—bind、call、apply(四)

【前端学习】—bind、call、apply(四) 一、代码题 <script>var name="freeman";function sayAuthor(){var name=

2023.10(u盘刻录iso)主机,vmware,virtualbox安装linux/ubuntu/kali

download 1 kali官网 2 ubuntu官网 3vmware workstation pro(最新版17pro) 4 virtualbox for linux sudo apt install virtualbox-ext-pack 5 win32 disk imger linux dd 刻录iso到u盘 #查看U盘路径 fdisk -l #图形界面 以kali为例会在桌面出现挂载图标 点开之后输入pwd寻…

【Android】 屏幕录制screenrecord为什么这么快?

背景 正常的合成视频是个什么样流程? 主要有三大类方法&#xff1a;MediaMux硬解码&#xff0c;mp4parser&#xff0c;FFmepg三种方式详情传送门, 但是并不能把高帧率、分辨率屏幕视频实时保存下来 那么Android screenrecord命令是怎么做的呢&#xff1f; 一般的android图像流…

【开发心得】Jaxb使用珠玑

前言 Java操作xml转换成javaBean,或者javaBean转换为xml的方式有很多。常见的有dom4j等工具直接操作dom&#xff0c;或者使用jaxb. jaxb介绍: JAXB(Java Architecture for XML Binding简称JAXB)允许Java开发人员将Java类映射为XML表示方式。JAXB提供两种主要特性&#xff1a;…

运动品牌如何做到“全都要”?来看看安踏的答案

文 | 螳螂观察 作者 | 易不二 运动鞋服是兼具高景气和清晰格局的优质消费赛道。 中信证券给出的这一预测&#xff0c;欧睿国际也做出了更具体的测算&#xff1a;预计到2027年&#xff0c;中国运动服饰市场规模有望以约为8.7%的年复合增长率&#xff0c;突破5500亿元人民币。…

零宽空格引发的问题

有人跟我反馈说有bug。 我说&#xff1a;啥bug&#xff1f; 对方说&#xff1a;刚申请的内部用户的账号登录不上去。 我说&#xff1a;还有这种事&#xff0c;报啥错&#xff1f; 登录的时候报了这个错&#xff1a; 我一看还好还好&#xff0c;跟上一次不一样的错&#xff…

有消息称苹果Vision Pro会有廉价版

据外媒爆料&#xff0c;苹果公司苹果正在研发的头显产品Vision Pro&#xff0c;将会有廉价版。据透露&#xff0c;这款产品预计售价在1500美元至2500美元之间&#xff0c;虽然仍不算低&#xff0c;但较现有的Vision Pro 3499美元的起售价&#xff0c;还是有明显降低。 透露廉价…

AcWing 5180. 正方形泳池

原题链接&#xff1a;5180. 正方形泳池 - AcWing题库 说实话题解和视频题解都不太好&#xff0c;有点过于复杂了&#xff0c;那就不得不记录一下我看视频题解衍生出的另一个较为简单的思路了。 根据答案形态出发&#xff0c;枚举所有这种形态找出最大值。 可以发现最大的泳池要…

十分钟了解IPv6升级改造安全优势

相信IPv6的地址数量优势已为大家熟知&#xff0c;丰沛的地址存量是IPv6被选作新一代网络承载协议并逐渐商用部署的根本驱动力。 然而IPv6协议相比于IPv4&#xff0c;不仅地址数量接近无限&#xff0c;还在网络安全性方面更胜一筹。本文将为您集中介绍IPv6的安全优势。 CSDN大…

微信小程序开发之flex布局及轮播图组件与后台Mock.js交互

目录 前言 一.flex布局 1.什么的flex布局 2.容器属性 2.1 flex-direction属性 2.2 flex-wrap属性 2.3 justify-content属性 特点&#xff1a; 二.轮播图 1.配置地址请求信息 2.通过方法加载轮播图数据 3.合法域名纠正 ​编辑 4.通过Mock.js模拟响应数据 5.轮播图前…

前端开发工具有哪些?17款前端工程师必备工具推荐!

软件开发是一个高度专业化的职业分工&#xff0c;根据所使用的编程语言的不同&#xff0c;会细分出多种岗位&#xff1a;前端开发、后端开发、客户端开发、iOS开发、Android开发、数据库开发等等&#xff0c;具体到每一个岗位&#xff0c;工作中常用的工具软件也存在着差别。 …

框架篇

一、Spring中的单例Bean是线程安全的吗 二、AOP相关面试题 三、Spring中的事务 四、Spring中事务失效的场景有 五、Spring bean的生命周期 六、Spring的循环依赖 七、SpringMVC的执行流程 八、自动配置原理 九、Spring框架常见的注解 十、Mybatis的执行流程 十一、MyBatis延迟加…

进阶JAVA篇- Lambda 表达式与 Lambda 表达式的省略规则

目录 1.0 什么是 Lambda 表达式&#xff1f; 1.1 既然跟匿名内部类相关&#xff0c;先来回顾匿名内部类。 1.2 Lambda 表达式与匿名内部类之间的关系。 1.3 函数式接口 1.4 在具体代码中来操作一下 2.0 Lambda 表达式省略规则 1.0 什么是 Lambda 表达式&#xff1f; Lambda 表达…

企业打造VR虚拟展厅,开启商务洽谈新时代!

现代化数字营销中&#xff0c;企业做了虚拟线上展厅和不做虚拟展厅的对比是很明显的&#xff0c;VR虚拟展厅让企业产品、企业环境、企业实力的展示更加真实、直观。虚拟展厅是一种在线展示企业形象和品牌的新型方式&#xff0c;随着VR技术的发展&#xff0c;虚拟展厅正在逐步取…

户外暴晒测试系统太阳光模拟器

概述 户外暴晒测试系统太阳光模拟器是一种设备&#xff0c;用于模拟太阳光以测试和评估材料、产品或设备在户外的耐久性和性能。它由光源、光学系统、试样台和控制系统组成。通过调整参数&#xff0c;模拟不同时间、地点和季节的太阳光条件&#xff0c;对样品进行长时间暴晒&a…

【LeetCode刷题(数据结构与算法)】:用队列实现栈

请你仅使用两个队列实现一个后入先出&#xff08;LIFO&#xff09;的栈&#xff0c;并支持普通栈的全部四种操作&#xff08;push、top、pop 和 empty&#xff09; 实现 MyStack 类&#xff1a; void push(int x) 将元素 x 压入栈顶 int pop() 移除并返回栈顶元素 int top() 返…

python版李相夷

1、我们绘制原图是这张lxy.jpg 2、具体代码如下 运行python main.py lxy.jpg就可以实现绘制过程了&#xff01;&#xff08;可能会比较慢&#xff09; # -*- coding: utf-8 -*-import turtle as te from bs4 import BeautifulSoup import argparse import sys import numpy a…