从零学Java 线程的状态

news2024/9/29 15:25:06

Java 线程的状态

文章目录

  • Java 线程的状态
    • 线程的基础状态
    • 1 常见方法
      • 1.1 休眠
      • 1.2 放弃
      • 1.3 加入
      • 1.4 优先级
      • 1.5 线程打断
      • 1.6 守护线程
      • 1.7 线程的状态 - 等待
    • 2 线程安全问题
      • 2.1 线程同步: 同步代码块
      • 2.2 线程同步: 同步方法
      • 2.3 同步规则
      • 2.4 线程的状态 - 阻塞
      • 2.5 特殊现象: 死锁

线程的基础状态

在这里插入图片描述

初始状态(New) : 线程对象被创建,即为初始状态。只在堆中开辟内存,与常规对象无异。

就绪状态(Ready) : 调用start()之后,进入就绪状态。等待OS选中,并分配时间片。

运行状态(Running) : 获得时间片之后,进入运行状态,如果时间片到期,则回到就绪状态。

终止状态(Terminated) : 主线程main()或独立线程run()结束,进入终止状态,并释放持有的时间片。

1 常见方法

1.1 休眠

  • public static void sleep(long millis)
  • 当前线程主动休眠 millis 毫秒,不再参与CPU竞争,直达休眠结束。

eg:

public class TestSleep {
    public static void main(String[] args) {
        SleepThread s1 = new SleepThread();
        SleepThread s2 = new SleepThread();
        s1.start();
        s2.start();
    }

    static class SleepThread extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println(
                    Thread.currentThread().getName()+"..."+i
                );
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

1.2 放弃

  • public static void yield()
  • 当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片。

eg:

public class TestYield {
    public static void main(String[] args) {
        YieldThread y1 = new YieldThread();
        YieldThread y2 = new YieldThread();
        y1.start();
        y2.start();
    }

    static class YieldThread extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println(
                    Thread.currentThread().getName()+"..."+i
                );
                //主动放弃CPU
                Thread.yield();
            }
        }
    }
}

1.3 加入

  • public final void join()
  • 允许其他线程加入到当前线程中。当前线程会阻塞,直到加入线程执行完毕。

内存分析:

在这里插入图片描述

eg:

public class TestJoin {
    public static void main(String[] args) throws InterruptedException {
        JoinThread j1 = new JoinThread();
        j1.start();
        //把j1加入到主线程中,造成主线程阻塞,直到j1执行完毕
        j1.join();
        for (int i = 0; i < 10; i++) {
            System.out.println("主线程........"+i);
        }
    }

    static class JoinThread extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println(
                    Thread.currentThread().getName()+"..."+i
                );
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

1.4 优先级

  • 线程对象.setPriority(int)
  • 线程优先级为1-10,默认为5,优先级越高,表示获取CPU机会越多。

eg:

public class TestPriority {
    public static void main(String[] args) {
        PriorityThread p1 = new PriorityThread();
        PriorityThread p2 = new PriorityThread();
        PriorityThread p3 = new PriorityThread();
        //线程优先级为1-10,默认为5,优先级越高,表示获取CPU机会越多
        p1.setPriority(1);
        p3.setPriority(10);
        //优先级设置要在线程开始之前
        p1.start();
        p2.start();
        p3.start();
    }

    static class PriorityThread extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println(
                    Thread.currentThread().getName()+"..."+i
                );
            }
        }
    }
}

1.5 线程打断

  • 线程对象.interrupt();
  • 打断线程,被打断线程抛出InterruptedException异常。

eg:

public class TestInterrupt {
    public static void main(String[] args) {
        InterruptThread thread = new InterruptThread();
        thread.start();
        System.out.println("10秒内输入任意字符打断子进程休眠");
        Scanner input = new Scanner(System.in);
        input.next();
        thread.interrupt();
    }

    static class InterruptThread extends Thread{
        @Override
        public void run() {
            System.out.println("子线程开始休眠...");
            try {
                Thread.sleep(10000);
                System.out.println("正常醒来");
            } catch (InterruptedException e) {
                System.out.println("被打醒了");
            }
        }
    }
}

1.6 守护线程

  • 线程有两类:用户线程(前台线程)、守护线程(后台线程)。
  • 如果程序中所有前台线程都执行完毕了,后台线程会自动结束。
  • 垃圾回收器线程属于守护线程。
  • setDaemon(true)设置为守护线程。

eg:

public class TestDaemon {
    public static void main(String[] args) throws InterruptedException {
        DaemonThread daemonThread = new DaemonThread();
        //设置守护线程
        daemonThread.setDaemon(true);//默认false,关闭
        daemonThread.start();
        for (int i = 0; i < 10; i++) {
            System.out.println(
                    "主线程...."+i
            );
            Thread.sleep(1000);
        }
    }

    static class DaemonThread extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                System.out.println(
                        Thread.currentThread().getName()+"..."+i
                );
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

1.7 线程的状态 - 等待

在这里插入图片描述


2 线程安全问题

在这里插入图片描述

需求:A线程将“Hello”存入数组;B线程将“World”存入数组。

线程不安全

  • 当多线程并发访问临界资源时,如果破坏原子操作,可能会造成数据不一致。
  • 临界资源:共享资源,一次仅允许一个线程使用,才可保证其正确性。
  • 原子操作:不可分割的多步操作,被视作一个整体,其顺序和步骤不可打乱或缺省。

2.1 线程同步: 同步代码块

同步:一个线程接着一个线程等待执行。异步:多个线程并发同时执行。

语法:

同步代码块:
synchronized(锁对象){ //使用共享资源对象加锁
	//同步代码(原子操作)
}

eg:

4个窗口共卖1000张票

Ticket:

public class Ticket implements Runnable{
    private int count = 1000; //票数
    @Override
    public void run() {
        while (true) {
            //一般选择共享资源
            synchronized (this) { //锁, 任何引用类型的对象都可以作为锁,但要保证唯一性
                if (count<=0) {
                    break;
                }
                System.out.println(
                        Thread.currentThread().getName() +
                                "卖了第" + count + "张票"
                );
                count--;
            }
        }
    }
}

Test:

public class Test {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();

        new Thread(ticket,"窗口1").start();
        new Thread(ticket,"窗口2").start();
        new Thread(ticket,"窗口3").start();
        new Thread(ticket,"窗口4").start();
    }
}

注意:

  1. 任何的引用类型对象都可以作为锁,但是保证多个线程使用唯一对象,一般使用共享资源作为锁。
  2. 每个对象都有一个互斥锁标记,用来分配给线程的,只有拥有对象互斥锁标记的线程,才能进入同步代码
  3. 线程退出同步代码块时,会释放相应的互斥锁标记。

2.2 线程同步: 同步方法

语法:

同步方法:
synchronized 返回值类型 方法名称(形参列表){ //对当前对象(this)加锁
	// 代码(原子操作)
}

eg:

4个窗口共卖1000张票

Ticket:

public class Ticket implements Runnable{
    private int count = 1000;
    @Override
    public void run() {
        while (true) {
            if (!sale()) {
                break;
            }
        }
    }
    //同步方法
    public synchronized boolean sale() {
        if (count<=0) {
            return false;
        }
        System.out.println(
                Thread.currentThread().getName() +
                "卖了第" + count + "张票"
        );
        count--;
        return true;
    }
}

注意:

  1. 只有拥有对象互斥锁标记的线程,才能进入该对象加锁的同步方法中
  2. 线程退出同步方法时,会释放相应的互斥锁标记。
  3. 如果当前方法是非静态方法锁是"this",如果方法是静态方法锁是"类名.class"

2.3 同步规则

注意

  • 只有在调用包含同步代码块的方法,或者同步方法时,才需要对象的锁标记。
  • 如调用不包含同步代码块的方法,或普通方法时,则不需要锁标记,可直接调用。

已知JDK中线程安全的类

  • StringBuffer
  • Vector
  • Hashtable
  • 以上类中的公开方法,均为synchonized修饰的同步方法。

eg:

你和你女朋友共用一张银行卡,你向卡中存钱,你女朋友从卡中取钱,使用程序模拟过程?

BankCard:

public class BankCard {
    private double money;

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }
}

Test:

public class Test {
    public static void main(String[] args) {
        BankCard card = new BankCard();
		//匿名内部类
        Runnable save = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    synchronized (card) {
                        card.setMoney(card.getMoney() + 1000);
                        System.out.println(
                                Thread.currentThread().getName()+
                                        "存了1000, 余额为"+card.getMoney()
                        );
                    }
                }
            }
        };
		//匿名内部类
        Runnable take = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    synchronized (card) {
                        if (card.getMoney() >= 1000) {
                            card.setMoney(card.getMoney() - 1000);
                            System.out.println(
                                    Thread.currentThread().getName()+
                                            "取了1000, 余额为"+card.getMoney()
                            );
                        } else {
                            System.out.println("赶紧存钱...");
                            i--;
                        }
                    }
                }
            }
        };
        new Thread(save,"小明").start();
        new Thread(take,"小红").start();
    }
}

2.4 线程的状态 - 阻塞

在这里插入图片描述

注:JDK5之后就绪、运行统称Runnable

2.5 特殊现象: 死锁

死锁:

  • 当第一个线程拥有A对象锁标记,并等待B对象锁标记,同时第二个线程拥有B对象锁标记,并等待A对象锁标记时,产生死锁。
  • 一个线程可以同时拥有多个对象的锁标记,当线程阻塞时,不会释放已经拥有的锁标记,由此可能造成死锁。

eg:

吃饭问题

Test:

public class TestDeadLock {
    public static void main(String[] args) {
        new Boy().start();
        new Girl().start();
    }

    static class Lock {
        static Object LockA = new Object();
        static Object LockB = new Object();
    }

    static class Boy extends Thread {
        @Override
        public void run() {
            synchronized (Lock.LockA) {
                System.out.println("Boy拿到了A锁");
                synchronized (Lock.LockB) {
                    System.out.println("Boy拿到了B锁");
                    System.out.println("Boy可以吃饭了");
                }
            }
        }
    }

    static class Girl extends Thread {
        @Override
        public void run() {
            synchronized (Lock.LockB) {
                System.out.println("Girl拿到了B锁");
                synchronized (Lock.LockA) {
                    System.out.println("Girl拿到了A锁");
                    System.out.println("Girl可以吃饭了");
                }
            }
        }
    }
}

情况一: 死锁

Boy拿到了A锁
Girl拿到了B锁

情况二: 未死锁

Boy拿到了ABoy拿到了BBoy可以吃饭了
Girl拿到了BGirl拿到了AGirl可以吃饭了

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

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

相关文章

用python提取PDF中各类文本内容的方法

从PDF文档中提取信息&#xff0c;是很多类似RAG这样的应用第一步要处理的事情&#xff0c;这里需要做好三件事&#xff1a; 提取出来的文本要保持信息完整性&#xff0c;也就是准确性提出的结果需要有附加信息&#xff0c;也就是要保存元数据提取过程要完成自动化&#xff0c;…

APM传感器校准

文章目录 前言一、校准加速度计二、校准罗盘三、校准陀螺仪四、校平地平线 前言 固件&#xff1a;rover 4.2.3 地面站&#xff1a;独家汉化版QGC 一、校准加速度计 点击左上角软件图标-》载具设置-》传感器-》加速度计 飞控方向默认为None即可&#xff0c;点击确定 点击确…

做品牌,怎么挖掘用户深层需求?

品牌想要长久发展&#xff0c;就需要去挖掘用户深层需求&#xff0c;什么是用户深层需求&#xff0c;比如做美业的认为用户想要变美是深层次的需求&#xff0c;但其实由美貌带来的附加利益比如说更上镜、竞争优势更大等才属于深层需求&#xff0c;今天媒介盒子就来和大家聊聊&a…

MySQL——SQL语句进阶

select * from 表 where 条件 group by 条件 order by 排序 limit 分组 Group by select * from 表 group by 条件 结果为每个分组的第一条记录&#xff0c;该条记录作为该组的标志 select * from subject GROUP BY gradeidselect count(1),gradeid from subject GROUP B…

图书管理系统:从数据库设计到前端展示的实战经验分享

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

Pytorch函数——torch.gather详解

在学习强化学习时&#xff0c;顺便复习复习pytorch的基本内容&#xff0c;遇到了 torch.gather()函数&#xff0c;参考图解PyTorch中的torch.gather函数 - 知乎 (zhihu.com)进行解释。 pytorch官网对函数给出的解释&#xff1a; 即input是一个矩阵&#xff0c;根据dim的值&…

网络文件共享服务、FTP和yum仓库

目录 一、存储类型 1、存储类型一共分为三种&#xff1a; 2、三种存储架构的应用场景 二、FTP文本传输协议 1、FTP工作原理介绍 2、FTP数据连接模式 3、svftpd的安装和配置 4、vsftpd的配置作用 5、黑名单和白名单的使用&#xff08;简要介绍&#xff09; 三、YUM 1…

1.15 作业

使用计数型信号量设计 2&#xff0c;相关函数的API 一、队列&#xff1a; 1&#xff0c;创建队列函数 osMessageQueueId_t osMessageQueueNew (uint32_t msg_count, uint32_t msg_size, const osMessageQueueAttr_t *attr); msg_count : 队列中消息的最大数量&#xff0c;即…

Docker安全基线检查需要修复的一些问题

一、可能出现的漏洞 限制容器之间的网络流量 限制容器的内存使用量 为Docker启用内容信任 将容器的根文件系统挂载为只读 审核Docker文件和目录 默认情况下&#xff0c;同一主机上的容器之间允许所有网络通信。 如果不需要&#xff0c;请限制所有容器间的通信。 将需要相互通…

【LeetCode题目详解】59. 螺旋矩阵 II 54. 螺旋矩阵 LCR 146. 螺旋遍历二维数组(c++)

这篇文章的题目稍微难一点 题目建议&#xff1a; 本题关键还是在转圈的逻辑&#xff0c;在二分搜索中提到的区间定义&#xff0c;在这里又用上了。 一、59. 螺旋矩阵 II 题目&#xff1a; 给你一个正整数 n &#xff0c;生成一个包含 1 到 n2 所有元素&#xff0c;且元素按…

原生IP代理如何帮助跨境电商店铺做谷歌广告投放业务的?

随着全球化的发展&#xff0c;越来越多的电商店铺开始拓展跨境业务&#xff0c;而谷歌广告作为全球最大的广告平台之一&#xff0c;为跨境电商店铺带来了巨大的收益和商机。 然而&#xff0c;由于谷歌广告的地域限制和审查机制&#xff0c;店铺很难直接进行投放业务&#xff0…

gradle版本中-bin与-all区别

打开android studio下载的gradle文件&#xff0c;发现-all比-bin多了一个docs文件夹和一个src文件夹。-bin是编译后的二进制发布版&#xff0c;-all还包含了源码和文档&#xff0c;比-bin大了几十兆&#xff0c;两者其余没有区别。 android开发只关注gradle功能不关注实现的情况…

Mingw32编译opencv库

文章目录 1. 准备工作2. 编译cmake构建程序mingw32-make编译 3. 安装4. 安装完的结果 注意&#xff1a; mingw32-make编译的库和MSVC编译的库不兼容&#xff0c;MSVC和mingw-make生成的动态库使用的是不同的ABI&#xff08;Application Binary Interface&#xff09;&#xff0…

如何实现无公网ip固定TCP端口地址远程连接Oracle数据库

文章目录 前言1. 数据库搭建2. 内网穿透2.1 安装cpolar内网穿透2.2 创建隧道映射 3. 公网远程访问4. 配置固定TCP端口地址4.1 保留一个固定的公网TCP端口地址4.2 配置固定公网TCP端口地址4.3 测试使用固定TCP端口地址远程Oracle 前言 Oracle&#xff0c;是甲骨文公司的一款关系…

64位ATT汇编语言学习第一课:汇编和链接

源文件exitTest.s内容如下&#xff1a; # This is the first program .global _start .section .text _start:movq $60,%raxmovq $9,%rdisyscall源文件里边放的就是源代码&#xff0c;而我这里源代码是使用汇编语言写的&#xff0c;都是一些人类都可以阅读的字符。之后需要经过…

【UE Niagara】网格体渲染器初识

目录 效果 步骤 一、创建网格体粒子 二、设置粒子生成速率 三、设置粒子初始大小 四、设置粒子生成的初始位置 五、设置粒子移动速度 六、设置粒子旋转 七、 设置粒子大小变化 效果 步骤 一、创建网格体粒子 新建一个Niagara粒子系统 选择“Simple Sprite Burs…

Ubuntu配置NFS客户端和服务端详解——手把手配置

Ubuntu配置NFS客户端和服务端 如果您想实现远程访问并修改 ROS 主机中 Ubuntu 上的文件&#xff0c;可以通过 NFS挂载的方式。虚拟机上的 Ubuntu 系统可以通过 NFS 的方式来访问 ROS 主机中Ubuntu 系统的文件&#xff0c;NFS 分为服务器挂载和客户端访问。这里虚拟机上的 Ubun…

小型洗衣机怎么用?好用不贵的小型洗衣机推荐

近期&#xff0c;有不少小伙伴都在议论“对于内衣是机洗好&#xff0c;还是手洗”这个问题&#xff0c;对于机洗党认为家用的洗衣机就能清洁干净内衣物&#xff0c;而坚定的手洗党则是认为应该用手去洗&#xff0c;因为机洗的话&#xff0c;其他大件衣服混在一起洗&#xff0c;…

Picturesocial | 开发实践:如何在15分钟内将应用容器化

在常见的软件架构体系中&#xff0c;容器无疑是一个技术热点。有些开发者在工作中熟练使用容器技术&#xff0c;有些可能刚刚开始容器之旅。 面对容器使用经验不同的各类开发者&#xff0c;我们希望通过这个系列文章&#xff0c;由浅入深地介绍如何使用容器技术来构建&#xf…

小白从事光伏行业:如何快速入行?

鹧鸪云 在政策引导和产业链不断优化的背景下&#xff0c;国内光伏市场不断扩大&#xff0c;国际市场中也崭露头角&#xff0c;许多人纷纷进入光伏行业&#xff0c;但是苦于不知如何入手。本文着重于为刚刚进入光伏行业的新手小白介绍进入光伏行业的好办法。 光伏行业利用太阳…