Java多线程核心技术一-多线程基础其他内容

news2024/11/18 15:42:50

接上篇:

Java多线程核心技术一-基础篇synchronzied同步方法

Java多线程核心技术一-基础篇synchronzied同步语句块

1 String常量池特性与同步问题

        JVM具有String常量池的功能,如下示例:

public class Test01 {
    public static void main(String[] args) {
        String a = "a";
        String b = "a";
        System.out.println(a == b);
    }
}

6e186de4f2434c528d535466cef09f70.png

        在把synchronzied(string)同步块与String联合使用时,要注意常量池会带来一些意外。

public class Service {
    public static void print(String param){
        try {
            synchronized (param){
                while(true){
                    System.out.println(Thread.currentThread().getName());
                    Thread.sleep(500);
                    if(Thread.currentThread().getName().equals("B")){
                        break;
                    }
                }
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

 

public class Run1 {
    public static void main(String[] args) {
        Service service = new Service();
        ThreadA a = new ThreadA(service);
        a.setName("A");
        a.start();
        ThreadB b = new ThreadB(service);
        b.setName("B");
        b.start();
    }
}
public class ThreadA extends Thread{
    private Service service;

    public ThreadA(Service service) {
        this.service = service;
    }
    @Override
    public void run(){
        service.print("A");
    }
}
public class ThreadB extends Thread{
    private Service service;

    public ThreadB(Service service) {
        this.service = service;
    }

    @Override
    public  void run(){
        service.print("A");
    }
}

17c030f20af94fc1855e987f2491cb1e.png

        出现这种情况就是因为String的两个值都是"AA",两个线程是持有相同的锁,造成线程B不能执行。这就是String常量池所带来的问题,所以大多数情况下,同步synchronzied都不使用String作为锁对象,而改用其他的。例如 new Object()实例化一个新的object对象时,它并不放入缓存池中,或者执行new String()创建不同的字符串对象,形成不同的锁。下面把synchronzied代码块的锁的对象改成object。

public class Service1 {
    public static void print(Object obj){
        try{
            synchronized (obj){
                while(true){
                    System.out.println(Thread.currentThread().getName());
                    Thread.sleep(500);

                }
            }

        }catch (InterruptedException e){
            e.printStackTrace();
        }


    }
}

 

public class ThreadA1 extends Thread{
    private Service1 service1;

    public ThreadA1(Service1 service1) {
        this.service1 = service1;
    }
    @Override
    public void run(){
        service1.print(new Object());
    }
}
public class ThreadB1 extends Thread{
    private Service1 service1;

    public ThreadB1(Service1 service1) {
        this.service1 = service1;
    }

    @Override
    public void run(){
        service1.print(new Object());
    }
}
public class Run2 {
    public static void main(String[] args) {
        Service1 service1 = new Service1();
        ThreadA1 a = new ThreadA1(service1);
        a.setName("A");
        a.start();
        ThreadB1 b = new ThreadB1(service1);
        b.setName("B");
        b.start();
    }
}

321cfc012d5048bda2cb2329ecd1b2da.png

A 和B 交替输出的原因是持有的锁不是同一个。

2 多线程死锁

        Java线程死锁是一个经典的多线程问题,因为不同的线程都在等待根本不可能被释放的锁,导致所有的任务都无法继续完成。在多线程技术中,死锁是必须要避免的,因为这会造成线程的“假死”。示例:

public class DealThread implements Runnable{
    public String username;
    public Object lock1 = new Object();
    public Object lock2 = new Object();
    public void setFlag(String username){
        this.username = username;
    }

    @Override
    public void run() {
        if(username.equals("a")){
            synchronized (lock1){
                try {
                    System.out.println("username = " + username);
                    Thread.sleep(3000);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
                synchronized (lock2){
                    System.out.println("按lock1 -> lock2代码顺序执行了");
                }
            }
        }
        if(username.equals("b")){
            synchronized (lock2){
                try {
                    System.out.println("username = " + username);
                    Thread.sleep(3000);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
                synchronized (lock1){
                    System.out.println("按lock2 -> lock1代码顺序执行");
                }
            }
        }
    }
}

 

public class Run1 {
    public static void main(String[] args) {
        try {
            DealThread t1 = new DealThread();
            Thread a = new Thread(t1);
            t1.setFlag("a");
            a.start();
            Thread.sleep(100);
            t1.setFlag("b");
            Thread b = new Thread(t1);
            b.start();
        }catch (InterruptedException e){
            e.printStackTrace();
        }

    }
}

4b42cb22783141c0be1d515638044908.png

        可以使用JDK自带的工具来检测是否有死锁现象,进入jdk/bin目录下,执行jps命令:

3b4e10bdc2254d059e755c7ac5deb595.png

         看到运行线程Run1的id值是12880,在执行jstack命令,查看结果:

d0cb584ba07f40e59e6c8e7a7053013e.png

        如上图,检测出有死锁 。

        死锁是程序设计的bug,在设计程序时就要避免双方互相持有对方的锁,只要互相等待对方释放锁,就有可能出现死锁。

3 内置类与静态内置类

        synchronzied关键字的知识点还涉及内置类的使用,先来看一个简单的内置类的测试。

public class PublicClass {
    private String username;
    private String password;

    class PrivateClass{
        private String age;
        private String address;

        public String getAge() {
            return age;
        }

        public void setAge(String age) {
            this.age = age;
        }

        public String getAddress() {
            return address;
        }

        public void setAddress(String address) {
            this.address = address;
        }
        public void printPublicProperty(){
            System.out.println(username + ";"+password);
        }
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}
public class Run1 {
    public static void main(String[] args) {
        PublicClass publicClass = new PublicClass();
        publicClass.setUsername("usernameValue");
        publicClass.setPassword("passwordValue");
        System.out.println(publicClass.getUsername() + ";" + publicClass.getPassword());
        PublicClass.PrivateClass privateClass = publicClass.new PrivateClass();
        privateClass.setAge("ageValue");
        privateClass.setAddress("addressValue");
        System.out.println(privateClass.getAge() + ";" + privateClass.getAddress());
    }
}

1f9ffda55d414362958d658c75c649ff.png

        如果PublicClass.java类和Run.java类不在同一个包中,则需要将PrivateClass内置类声明为public。想要实例化内置类,必须使用如下代码:

PublicClass.PrivateClass privateClass = publicClass.new PrivateClass();

         还有一种内置类叫静态内置类。

public class PublicClass1 {
    static private String username;
    static private String password;

    static class PrivateClass{
        private String age;
        private String address;

        public String getAge() {
            return age;
        }

        public void setAge(String age) {
            this.age = age;
        }

        public String getAddress() {
            return address;
        }

        public void setAddress(String address) {
            this.address = address;
        }

        public void printPublicProperty(){
            System.out.println(username + ";"+password);
        }
    }

    public static String getUsername() {
        return username;
    }

    public static void setUsername(String username) {
        PublicClass1.username = username;
    }

    public static String getPassword() {
        return password;
    }

    public static void setPassword(String password) {
        PublicClass1.password = password;
    }
}
public class Run2 {
    public static void main(String[] args) {
        PublicClass publicClass = new PublicClass();
        publicClass.setUsername("usernameValue");
        publicClass.setPassword("passwordValue");
        System.out.println(publicClass.getUsername() + ";" + publicClass.getPassword());

        PublicClass1.PrivateClass privateClass = new PublicClass1.PrivateClass();
        privateClass.setAge("ageValue");
        privateClass.setAddress("addressValue");
        System.out.println(privateClass.getAge() + ";" + privateClass.getAddress());
    }
}

4 内置类的同步

        本节测试的案例是内置类中有两个同步方法,但使用不同的锁,输出的结果也是异步的。

public class Inner1 {
    public void method1(){
        synchronized ("其他的锁"){
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + "; i = " +(i + 1));
                try {
                    Thread.sleep(100);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        }
    }

    synchronized public void method2(){
        for (int i = 11; i < 20; i++) {
            System.out.println(Thread.currentThread().getName() + "; i = " + (i + 1));
            try {
                Thread.sleep(100);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}
public class Run1 {
    public static void main(String[] args) {
        final Inner1 inner1 = new Inner1();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                inner1.method1();
            }
        },"A");

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                inner1.method2();
            }
        }, "B");
        t1.start();
        t2.start();
    }
}

0ed42276ddfe43e3a0a422df0e8e5c41.png

由于持有不同的锁,所有输出结果是乱序的。

 5 锁对象改变导致异步执行

        在把任何数据类型作为同步锁时,需要注意是否有多个线程同时争抢锁对象,如果同时争抢相同的锁对象,则这些线程之间就是同步的;如果分别获得自己的锁,那么这些线程之间就是异步的。通常情况下,一旦持有锁后就不再对锁对象进行更改,因为一旦更改就有可能出现一些错误。

public class MyService {
    private String lock = "123";
    public void testMethod(){
        try {
            synchronized (lock){
                System.out.println(Thread.currentThread().getName() + "开始时间是:" + System.currentTimeMillis());
                lock = "456";
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName() + "结束时间时: " + System.currentTimeMillis());
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}
public class ThreadA extends Thread{

    private MyService myService;

    public ThreadA(MyService myService) {
        this.myService = myService;
    }

    @Override
    public void run(){
        myService.testMethod();
    }
}
public class ThreadB extends Thread{
    private MyService myService;

    public ThreadB(MyService myService) {
        this.myService = myService;
    }

    @Override
    public void run(){
        myService.testMethod();
    }
}
public class Run1 {
    public static void main(String[] args) throws InterruptedException {

        MyService service = new MyService();
        ThreadA a = new ThreadA(service);
        ThreadB b = new ThreadB(service);
        a.setName("A");
        b.setName("B");
        a.start();
        Thread.sleep(50);
        b.start();
    }
}

4010886680b1434682b5b21f6c84f1fa.png

        因为50毫秒后,B现成取得锁时456.

        继续实现,修改Run1.java,去掉代码中的Thread.sleep(50) 

public class Run2 {
    public static void main(String[] args) throws InterruptedException {

        MyService service = new MyService();
        ThreadA a = new ThreadA(service);
        ThreadB b = new ThreadB(service);
        a.setName("A");
        b.setName("B");
        a.start();
        b.start();
    }
}

 169b77164abe4ef3b508574512c19b29.png

       需要注意的是,String类型是不可变的,都是创建新的内存空间来解决存储新的字符。

        控制台输出的信息说明A线程和B线程检测到锁对象的值为123,且存储到内存空间X的位置,虽然将锁改成了456,并存储内存空间Y的位置,但结果还是同步的,因为A线程和B线程共同争抢的锁是X空间的123,不是Y空间的456。但是,还是会有很小的概率出现一起输出两个begin的情况,因为A线程将锁的值改成456后,B现成才启动区执行run方法,不存在A和B争抢同一把锁的情况,导致B线程获取的是更改后的锁的值(456),并连续输出两个begin。

 

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

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

相关文章

TZOJ 1367 计算两点间的距离

答案&#xff1a; #include <stdio.h> #include<math.h> //引用数学的库函数 int main() {double x1 0.0, y1 0.0, x2 0.0, y2 0.0; //由于输入的是实数&#xff0c;实数包括小数&#xff0c;所以不能 用int类型&#xff0c;只能用double类型while (sc…

elementui中table进行表单验证

<el-form :model"ruleForm" ref"ruleForm" class"demo-ruleForm"><el-table :data"ruleForm.tableDataShou" border style"width: 100%;"><el-table-column type"index" label"序号" wi…

Python - Real-ESRGAN 提升图像、视频清晰度 - 最高可达 4 K

目录 一.引言 二.Real-ESRGAN 理论 1.模型简介 2.经典退化模型 ◆ 退化过程全览 ◆ K - 高斯滤波 ◆ N - 噪声 ◆ ↓r - Resize ◆ jpeg - 压缩 3.高阶退化模型 4.环形和超调伪影 5.网络结构 ◆ ESRGAN 生成器 ◆ U-Net 鉴别器 三.Real-ESRGAN 实战 1.快速体验…

CNS0项目创建交货单增加销售办事处

1、业务需求 1.1、销售办事处介绍 销售办事处是指在企业中负责销售活动的区域性单位或部门。在SD模块中&#xff0c;可以表示企业的不同销售地点、销售办公室、分销中心或分公司。 销售办事处扮演着多种角色和职责&#xff0c;例如&#xff1a; 销售活动管理&#xff1a;销售…

福州大学《嵌入式系统综合设计》 实验十二:图像压缩标准JPEG编解码

一、实验目的 掌握基于算能平台的JPEG压缩编码方法以及开发环境&#xff0c;包括开发主机环境搭建&#xff0c;硬件嵌入式开发板的连接&#xff0c;云平台的配置&#xff0c;编码程序的编译、运行等。 二、实验内容 搭建实验开发环境&#xff0c;并编写静止图像jpeg格式编解…

Linux常用命令——badblocks命令

在线Linux命令查询工具 badblocks 查找磁盘中损坏的区块 补充说明 badblock命令用于查找磁盘中损坏的区块。 硬盘是一个损耗设备&#xff0c;当使用一段时间后可能会出现坏道等物理故障。电脑硬盘出现坏道后&#xff0c;如果不及时更换或进行技术处理&#xff0c;坏道就会越…

渗透测试考核(靶机1)

信息收集 主机发现 nbtscan -r 172.16.17.0/24 发现在局域网内&#xff0c;有两台主机名字比较可疑&#xff0c;177和134&#xff0c;猜测其为目标主机&#xff0c;其余的应该是局域网内的其他用户&#xff0c;因为其主机名字比较显眼&#xff0c;有姓名的拼音和笔记本电脑的…

AI搜索相关性在网站和APP上的应用

设定场景&#xff1a;您在寻找一件新衣服&#xff0c;所以在浏览最喜欢的网店。您跳到搜索栏上&#xff0c;输入您要找的东西。您期待出现什么结果&#xff1f; 高度准确、相关和即时的结果。 无论在什么网站上搜索&#xff0c;寻找什么&#xff0c;甚至在打错字或使用了错误的…

【算法刷题】Day9

文章目录 611. 有效三角形的个数![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/9d627e680e9144a2b67474a1d80aa030.png)题解&#xff1a;代码&#xff1a; LCR 179. 查找总价格为目标值的两个商品题解&#xff1a;代码&#xff1a; 611. 有效三角形的个数 原题链…

双向ESD保护 汽车级TVS二极管 ESD9B3.3ST5G工作原理、特性参数、封装形式

什么是汽车级TVS二极管&#xff1f; TVS二极管是一种用于保护电子电路的电子元件。它主要用于电路中的过电压保护&#xff0c;防止电压过高而损坏其他部件。TVS二极管通常被称为“汽车级”是因为它们能够满足汽车电子系统的特殊要求。 在汽车电子系统中&#xff0c;由于车辆启…

零基础自学编程,中文编程工具下载,中文编程工具构件之弹出菜单构件简介

一、前言&#xff1a; 零基础自学编程&#xff0c;中文编程工具下载&#xff0c;中文编程工具构件之弹出菜单构件简介 编程系统化教程链接 https://jywxz.blog.csdn.net/article/details/134073098?spm1001.2014.3001.5502 给大家分享一款中文编程工具&#xff0c;零基础…

二阶龙格塔库积分法求解混沌产生方程(求助)

最近论文中常常接触到激光产生混沌的方程&#xff0c;激光器作为非线性元件&#xff0c;在信息处理中具有非常大的潜力&#xff0c;其中激光产生混沌应用在通信中很有用处。论文中对于模拟数据部分&#xff0c;采用了以下公式来产生混沌&#xff1a;以此公式产生混沌的方法应用…

【HTML】VScode不打开浏览器实时预览html

1. 问题描述 预览HTML时&#xff0c;不想打开浏览器&#xff0c;想在VScode中直接实时预览 2. 解决方案 下载Microsoft官方的Live Preview 点击预览按钮即可预览

Linux设置Nginx开机自启

文章目录 获取linux系统是多少位: getconf LONG_BIT获取CentOS版本: lsb_release -a获取nginx的版本: nginx -version第一步配置文件 vim /etc/rc.local最底部增加这一行&#xff1a; /usr/local/nginx/sbin/nginx 第二步注册systemctl服务 在/usr/lib/systemd/system目录…

OpenAI神秘项目“Q星”浮出水面,它会威胁人类吗?

来源&#xff1a; 现代快报全媒体 2023-11-26 23:55:15 百年之后&#xff0c;人类再看这段OpenAI的“宫斗大戏”&#xff0c;或许会从商战之外&#xff0c;看到2023年的人类面对未知世界忧心忡忡。 是否要继续投入资源&#xff0c;催动AI进化&#xff1f;身处2023年的人类…

numpy知识库:numpy数据类型转换技巧

需求背景 基于numpy和opencv生成一个随机噪声灰度图像&#xff0c;像素值是范围[0, 256)内的整数&#xff0c;图像形状为(512, 512)&#xff0c;并显示图像&#xff0c;源码如下 import numpy as np import cv2img np.random.randint(0, 256, size[512, 512]) cv2.imshow(&q…

[架构之路-254]:目标系统 - 设计方法 - 软件工程 - 软件设计 - 架构设计 - 全程概述

目录 一、软件架构概述 1.1 什么是软件架构 1.2 为什么需要软件架构设计 1.3 软件架构设计在软件设计中位置 &#xff08;1&#xff09;软件架构设计&#xff08;层次划分、模块划分、职责分工&#xff09;&#xff1a; &#xff08;2&#xff09;软件高层设计、概要设计…

同旺科技 分布式数字温度传感器 -- Modbus Poll测试

内附链接 1、数字温度传感器 主要特性有&#xff1a; ● 支持PT100 / PT1000 两种铂电阻&#xff1b; ● 支持 2线 / 3线 / 4线 制接线方式&#xff1b; ● 支持5V&#xff5e;17V DC电源供电&#xff1b; ● 支持电源反接保护&#xff1b; ● 支持通讯波特率1200bps、2…

一文秒懂|Linux字符设备驱动

我的圈子&#xff1a; 高级工程师聚集地 我是董哥&#xff0c;高级嵌入式软件开发工程师&#xff0c;从事嵌入式Linux驱动开发和系统开发&#xff0c;曾就职于世界500强公司&#xff01; 创作理念&#xff1a;专注分享高质量嵌入式文章&#xff0c;让大家读有所得&#xff01; …

亚马逊云与生成式 AI 的融合:未来展望与综述

文章目录 前言生成式AI的定义生成式 AI应用领域AI办公软件AI创意工具AI企业服务AI网络安全AIIT 运维AI软件开发AI数据智能AI数字代理AI金融AI医疗AI教育AI工业AI汽车AI机器人 后记 前言 在当今数据时代&#xff0c;人工智能和云计算已经成为了企业发展和创新的必不可少的工具。…