Java之线程详解(二)——线程安全概述、synchronized锁

news2024/11/24 22:54:50

一、线程安全概述

  1. 什么是线程安全问题?

当多个线程共享同一个全局变量,做写的操作时(即修改该全局变量),可能会受到其他的线程干扰,发生线程安全问题。
eg:

public class Thread01  implements  Runnable{
    //定义一个全局变量
    private static Integer count = 100;

    @Override
    public void run() {
        while (count >1){
            cal();
        }
    }

    private void cal(){
        try {
            Thread.sleep(30);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        count--;
        System.out.println(Thread.currentThread().getName()+"--"+count);
    }

    public static void main(String[] args) {
        Thread01 thread01 = new Thread01();
        Thread thread1 = new Thread(thread01);
        Thread thread2 = new Thread(thread01);
        thread1.start();
        thread2.start();
    }
}

运行后打印结果出现了线程安全问题,如下图:
在这里插入图片描述

  1. 多线程如何解决线程安全问题(多线程如何实现同步)

当多个线程共享同一个全局变量时,将可能会发生线程安全的代码上锁,保证只有拿到锁的线程才可以执行,没有拿到锁的线程不可以执行,需要阻塞等待。
(1)使用synchronized锁;
(2)使用Lock锁 ,需要自己实现锁的升级过程,底层是基于aqs实现;
(3)使用Threadlocal,需要注意内存泄漏的问题。
(4)原子类CAS 非阻塞式。

二、synchronized锁的基本用法

  1. 修饰代码块
    修饰代码块,指定加锁对象,对指定对象加锁,执行该代码块前要获得指定对象的锁。
    eg:
public class Thread01 implements Runnable{
    private static Integer count = 100;

    @Override
    public void run() {
        while (count > 1) {
            test();
        }
    }

    private void test() {
        synchronized (this) {
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            count--;
            System.out.println(Thread.currentThread().getName() + "," + count);
        }
    }

    public static void main(String[] args) {
        Thread01 thread01 = new Thread01();
        Thread thread1 = new Thread(thread01);
        Thread thread2 = new Thread(thread01);
        thread1.start();
        thread2.start();
    }
}
  1. 修饰实例方法
    修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁,在实例方法上默认加上synchronized 默认使用this锁。
    eg:
public class Thread02 implements Runnable {
    private static Integer count = 100;

    @Override
    public void run() {
        while (count > 1) {
            test();
        }
    }

    private synchronized void test() {
        try {
            Thread.sleep(10);
        } catch (Exception e) {

        }
        count--;
        System.out.println(Thread.currentThread().getName() + "," + count);
    }
    public static void main(String[] args) {
        Thread02 thread02 = new Thread02();
        Thread thread1 = new Thread(thread02);
        Thread thread2 = new Thread(thread02);
        thread1.start();
        thread2.start();
    }
}
  1. 修饰静态方法
    修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁,默认使用当前类的 类名.class 锁。
    eg:
public class Thread03 implements Runnable{
    private static Integer count = 100;
    private static String lock = "lock";

    @Override
    public void run() {
        while (count > 1) {
            test();
        }
    }

    private static void test() {
        synchronized (Thread03.class) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            count--;
            System.out.println(Thread.currentThread().getName() + "," + count);
        }
    }

    public static void main(String[] args) {
        Thread03 Thread031 = new Thread03();
        Thread03 Thread032 = new Thread03();
        Thread thread1 = new Thread(Thread031);
        Thread thread2 = new Thread(Thread032);
        thread1.start();
        thread2.start();
    }
}
  1. synchronized死锁问题
    在使用synchronized时需要注意 synchronized锁嵌套的问题,避免死锁的问题发生。
    eg:
public class ThreadDeadlock implements Runnable{
    private int count = 1;
    private String lock = "lock";

    @Override
    public void run() {
        while (true) {
            count++;
            if (count % 2 == 0) {
                // 线程1需要先获取到自定义对象的lock锁,执行test1方法需要再获取this锁;线程2需要先获取this锁,执行test2方法再获取lock锁
                synchronized (lock) {
                    test1();
                }
            } else {
                synchronized (this) {
                    test2();
                }
            }
        }
    }

    public synchronized void test1() {
        System.out.println(Thread.currentThread().getName() + ",test1方法");
    }

    public void test2() {
        synchronized (lock) {
            System.out.println(Thread.currentThread().getName() + ",test2方法");
        }
    }

    public static void main(String[] args) {
        ThreadDeadlock threadDeadlock = new ThreadDeadlock();
        Thread thread1 = new Thread(threadDeadlock);
        Thread thread2 = new Thread(threadDeadlock);
        thread1.start();
        thread2.start();
    }
}
  1. springmvc 接口中使用
    Spring MVC 的Controller默认是单例的,需要注意线程安全问题。
    eg:
@RestController
public class ThreadService {
    private int count = 0;

    @RequestMapping("/test")
    public synchronized String count() {
        try {
            System.out.println(">count<" + count++);
			Thread.sleep(3000);
        } catch (Exception e) {
			e.printStackTrace();
        }
        return "count";
    }
}
  1. 多线程线程之间通讯

等待/通知机制:
等待/通知的相关方法是任意Java对象都具备的,因为这些方法被定义在所有对象的超类java.lang.Object上,方法如下:
(1)notify():通知一个在对象上等待的线程,使其从main()方法返回,而返回的前提是该线程获取到了对象的锁;
(2)notifyAll():通知所有等待在该对象的线程;
(3)wait():调用该方法的线程进入WAITING状态,只有等待其他线程的通知或者被中断,才会返回。需要注意调用wait()方法后,会释放对象的锁 。

注意:wait()、notify()和notifyAll()方法要与synchronized一起使用。

eg:

public class Thread02 {
    private  Object objectLock = new Object();

    public static void main(String[] args) throws InterruptedException {
        new Thread02().print();
    }

    public void print() throws InterruptedException {
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (objectLock){
                    System.out.println(Thread.currentThread().getName()+"---1---");
                    try {
                        objectLock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"---2---");
                }
            }
        }).start();

        Thread.sleep(3000);
        synchronized (objectLock){
            objectLock.notify();
        }
    }
}
  1. 多线程通讯实现生产者与消费者
    eg:
public class Thread03 {

    class Student {
        public String name;
        public char sex;
        public boolean flag = true;//true:输入,false:输出
    }

    class InputThread extends Thread {
        private Student student;

        public InputThread(Student student) {
            this.student = student;
        }

        @Override
        public void run() {
            int count = 0;
            while (true) {
                synchronized (student) {
                    if (!student.flag) {
                        try {
                            student.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    if (count == 0) {
                        student.name = "毛毛";
                        student.sex = '男';
                    } else {
                        student.name = "天天";
                        student.sex = '女';
                    }
                    count = (count + 1) % 2;
                    student.flag = false;
                    student.notify();
                }
            }
        }
    }

    class PrintThread extends Thread {
        private Student student;

        public PrintThread(Student student) {
            this.student = student;
        }

        @Override
        public void run() {
            while (true) {
                synchronized (student) {
                    if (student.flag) {
                        try {
                            student.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("姓名:" + student.name + ",性别:" + student.sex);
                    student.flag = true;
                    student.notify();
                }
            }
        }
    }

    public void print() {
        Student student = new Student();
        InputThread inputThread = new InputThread(student);
        PrintThread printThread = new PrintThread(student);
        inputThread.start();
        printThread.start();
    }

    public static void main(String[] args) {
        new Thread03().print();
    }

}

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

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

相关文章

丝裂原活化蛋白激酶TaMPK3抑制植物对ABA的反应

文章信息 题目&#xff1a;Mitogen-activated protein kinase TaMPK3 suppresses ABA response by destabilising TaPYL4 receptor in wheat 刊名&#xff1a;New Phytologist 作者&#xff1a;Ying Liu&#xff0c;You-Zhi Ma, Zhao-Shi Xu et al. 单位&#xff1a;Instit…

【Linux】如何在Linux下提交代码到gittee

文章目录使用 git 命令行创建项目三板斧第一招: git add三板斧第二招: git commit三板斧第三招: git push其他几个重要的命令git pull&#xff08;将远端同步到本地&#xff09;git rm&#xff08;删除&#xff09;git log&#xff08;查看提交日志&#xff09;使用 git 命令行…

GPT语言模型

GPT&#xff1a;GPT采用了大量的无监督的数据进行预训练&#xff08;Pre-training&#xff09;&#xff0c;并在有监督的数据上进行微调&#xff08;Fine-tuning&#xff09;来做具体的NLP的任务。结构&#xff1a;GPT使用了12层12头&#xff0c;单向transformer的decoder部分&…

2001-2020年全国31省城镇居民人均可支配收入/居民实际收入水平

1、时间区间为&#xff1a;2001-2020年 2、范围包括&#xff1a;全国31省 3、基期&#xff1a;以2001年为基期 4、来源&#xff1a;国家统计J 5、指标包括&#xff1a; 城市居民消费价格指数(上年100)、城镇居民人均可支配收入(元)、实际可支配收入&#xff08;2001年为基…

LabVIEW开发LabVIEW类

LabVIEW开发LabVIEW类 从概念上来说&#xff0c;LabVIEW面向对象编程和其它面向对象编程语言相似。但由于LabVIEW是数据流图形化编程环境&#xff0c;LabVIEW对类数据的操作和交互&#xff0c;以及LabVIEW类代码的调试方法和其它语言有所不同。 LabVIEW中的对象由值来传递&a…

02【SpringMVC的工作流程】

文章目录二、SpringMVC的工作流程2.1 SpringMVC的三大组件2.1.1 HandlerMapping&#xff1a;2.1.2 HandlerAdapter&#xff1a;2.1.3 ViewResolver&#xff1a;2.2 SpringMVC源码分析2.2.1 准备工作2.2.2 查看DispatcherServlet的继承体系&#xff1a;2.2.3 通过映射器获取执行…

工业机械设备设计与艺术设计

众所周知&#xff0c;外观设计是目前工业设计的一种&#xff0c;也是展示产品外观性能的最佳途径。如果外观设计做得好&#xff0c;产品可以在第一时间吸引顾客。说到外观设计&#xff0c;我们不得不说&#xff0c;工业机械的外观设计&#xff0c;因为机械工业是工业设计的主要…

Java客户关系系统(含源码+论文+答辩PPT等)

该项目采用技术Struts2代理注解反射MyBatisjqueryjscss等技术&#xff0c;项目含有源码、文档、配套开发软件、软件安装教程、项目发布教程等 项目功能介绍&#xff1a; 系统管理&#xff1a;用户登录、用户退出 员工管理&#xff1a;主要实现对员工的增、删、查、改功能 客户信…

ZMQ之面向服务的可靠队列(管家模式)

管家模式协议&#xff08;MDP&#xff09;在扩展PPP协议时引入了一个有趣的特性&#xff1a;client发送的每一个请求都有一个“服务名称”&#xff0c;而worker在像队列装置注册时需要告知自己的服务类型。MDP的优势在于它来源于现实编程&#xff0c;协议简单&#xff0c;且容易…

孩子到底是食物过敏?还是食物不耐受?

虾蟹过敏&#xff0c;牛奶过敏&#xff0c;鸡蛋过敏&#xff0c;甚至花生过敏……近年来&#xff0c;儿童食物过敏的发病率逐年上升。食物过敏对儿童危害很大&#xff0c;其临床症状会使儿童患病&#xff0c;影响身心健康&#xff1b;长期禁食&#xff0c;影响均衡营养摄入&…

Segger RTT深度使用说明-移植-Jlink rtt viewer显示-输出到Secure CRT

简介 RTT&#xff08; Real Time Terminal&#xff09;是SEGGER公司新出的可以在嵌入式应用中与用户进行交互的实时终端。J-Link驱动4.90之后的版本都支持RTT。RTT既可以从MCU上输出信息、也可以向应用程序发送信息&#xff0c;由于其高速的特性&#xff0c;所以不影响MCU的实…

重磅消息:Lazada和Shopee通过中国执照就可以开通本地店铺,享受更多的流量和资源扶持

目前Lazada平台是可以做跨境店和本地店铺的&#xff0c;跨境店铺指的是中国卖家通过国内的营业执照开店&#xff0c;本地店指的是东南亚本地的商家提供个人身份证或当地的营业执照开的店铺&#xff0c;那么有什么区别和优劣势 1.跨境店/本地店开店所需资料 跨境店&#xff1a…

横向的Excel输出为pdf自动分成两页怎么办?不分页,铺满整张纸的方法来了

我们工作中有时候会需要把Excel转换成pdf&#xff0c;一般我们用WPS的“输出为pdf”功能就可以转了。但是有些横向的Excel转换的时候&#xff0c;会自动分成两页&#xff0c;这并不是我们想要的效果。怎么才能不分成两页呢&#xff1f; 首先我们有一个Excel&#xff1a; 使用…

什么牌子蓝牙耳机通话质量好?通话质量好的蓝牙耳机推荐

蓝牙耳机作为手机的最佳伴侣&#xff0c;已经成为老百姓日常生活必备。每次有大品牌发布新款蓝牙耳机&#xff0c;几乎都能够得到很好的反响&#xff0c;蓝牙耳机不仅在音质上有了很大的提升&#xff0c;并且在其他功能也在不断的提升&#xff0c;使用蓝牙耳机通话避免不了电话…

m基于matlab的光通信误码率仿真,分别对比了OFDM+BPSK和OFDM+4QAM的误码率和星座图

目录 1.算法描述 2.matlab算法仿真效果 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 光通信系统中&#xff0c;QAM调制应用广泛&#xff0c;因为其利用幅度和相位同时传递i信息&#xff0c;提高了频带利用率。目前&#xff0c;最高阶数的QAM已达到1024-QAM即星座图有1024个点…

网站风格变黑白的方法,用css或javascript方法将网站改为灰色

如何把网站颜色去掉&#xff1f;用下面的方法即可 1.通用方法 body { -webkit-filter: grayscale(100%);-moz-filter: grayscale(100%);-ms-filter: grayscale(100%);-o-filter: grayscale(100%);filter: grayscale(100%);filter: gray; }html {-webkit-filter: grayscale(10…

harbor私有仓库部署

一、Harbor的概念 Harbor是VMware公司开源的企业级Docker Registry项目&#xff0c;其目标是帮助用户迅速搭建一个企业级的Docker Registry服务 Harbor以 Docker 公司开源的Registry 为基础&#xff0c;提供了图形管理UI、基于角色的访问控制(Role Based AccessControl)、AD/LD…

python习题004--使用python实现ATM机效果

相信每一个人在日常生活中使用ATM机的次数不少&#xff0c;尤其是现在微信支付&#xff0c;支付宝支付的普及&#xff0c;就需要用到ATM机并将现金存入银行卡内。 接下来我就使用python并结合面向对象的知识编写一个简易的ATM机存取款的效果【仅供参考】。 题目 简易的ATM机…

K8S-1.18.20高可用集群之部署集群监控系统kube-prometheus插件

K8S-1.18.20高可用集群之部署集群插件-KUBE-PROMETHEUS插件 一、简介 kube-prometheus 是一整套监控解决方案&#xff0c;它使用 Prometheus 采集集群指标&#xff0c;Grafana 做展示&#xff0c;包含如下组件&#xff1a; The Prometheus Operator Highly available Promet…

某车app登录参数分析

目标app: Y29tLmNoZTE2OC51Y2RlYWxlcg== 抓取登录包如下: 提交的data参数: _appid (app标识,固定值) _sign(加密) appversion(app版本,固定值) channelid(固定值) pass (密码加密) udid (加密) user (手机号) 共有_sign、 pass、udid三处加密, 其中_sign预估md5加密, pa…