17 synchronized关键字使用 synchronized方法、synchronized块

synchronized方法、synchronized

线程的同步

并发:同一个对象被多个线程同时操作。

解决方案:让多个线程排队操作对象。

使用队列和锁解决多线程的并发问题。

同进程的多线程共享同一块存储空间,当多个线程同时访问某块内存空间时,就存在并发问题。为了保证数据在方法中被访问时的正确性,在访问时加入锁机制synchronized;当线程获得对象的排它锁,该线程独占资源,其他线程必须等待其使用后释放锁即可。

同时也伴随着一些问题:

  • 一个线程持有锁导致其它线程所有需要此锁的线程挂起
  • 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,导致性能问题
  • 也可能存在优先级高的线程等待优先级低的线程释放锁,而导致性能问题。

不安全的线程

通过下面例子说明,线程是不安全。

重复执行下面方法,执行结果可能会有多种情况。

示例1:

java">//每次执行结果都不是我们预想的结果
public class UnsafeBuyTicket {
    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();
        new Thread(buyTicket,"张三").start();
        new Thread(buyTicket,"李四").start();
        new Thread(buyTicket,"王五").start();

    }
}

class BuyTicket implements Runnable {

    private int ticketNums = 10;
    boolean flag = true;

    @Override
    public void run() {
        while (flag) {
            buy();
        }

    }
    //synchronized 同步方法 锁的是this 即BuyTicket对象
    private /**synchronized*/ void buy() {
        if (ticketNums <= 0) {
            flag = false;
            return;
        }
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "买到票" + ticketNums--);
    }
}
java">张三买到票9
李四买到票8
王五买到票10
李四买到票7
王五买到票6
张三买到票7
张三买到票5
李四买到票4
王五买到票5
李四买到票3
张三买到票3
王五买到票3
王五买到票2
张三买到票2
李四买到票2
李四买到票1
王五买到票1
张三买到票1

示例2

java">public class UnsafeBank {
    public static void main(String[] args) {
        Account funds = new Account("家庭基金", 100);
        new TakeMoney(funds,60,"you").start();
        new TakeMoney(funds,80,"youWife").start();
    }


}

class Account{
    String name;
    int money;

    public Account(String name, int money) {
        this.name = name;
        this.money = money;
    }
}

class TakeMoney extends Thread{
    Account account;
    int takeMoney;//取多钱钱
    int nowMoney;//你口袋里面的钱

    public TakeMoney(Account account, int takeMoney, String name) {
        super(name);
        this.account = account;
        this.takeMoney = takeMoney;
    }

    //synchronized run 是不行的
    @Override
    public void run() {
        //synchronized (account) {
            if ((account.money - takeMoney) < 0) {
                System.out.println(Thread.currentThread().getName() + "余额不足。");
                return;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            account.money = account.money - takeMoney;
            nowMoney = nowMoney + takeMoney;
            System.out.println(account.name + "余额为:" + account.money);
            System.out.println(this.getName() + "口袋里面的钱:" + nowMoney);
        //}
    }
}
java">//执行结果
家庭基金余额为:-40
家庭基金余额为:-40
you口袋里面的钱:60
youWife口袋里面的钱:80

示例3

java">public class UnsafeList {
    public static void main(String[] args) throws InterruptedException {
        List<String> list = new ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(() -> {
                //synchronized (list) {
                    list.add(Thread.currentThread().getName());
               // }
            }).start();
        }
        Thread.sleep(3000);//确保上面线程能够执行完毕后在打印list.size
        System.out.println(list.size());
    }
}
java">//执行结果
9999

针对上面示例,添加synchronized关键字,使其变为线程安全的。

synchronizedsynchronized_175">synchronized方法、synchronized

关键字synchronized可以修饰方法或者以同步块的形式来进行使用,它主要确保多个线程在同一个时刻,只能有一个线程处于方法或者同步块中,它保证了线程对变量访问的可见性和排他性

synchronized方法控制对象的访问,每个对象对应一把锁,每个synchronized方法都必须获得该方法对象的锁才能执行,否则会阻塞线程,方法执行时独占该锁,知道方法执行完毕才会释放锁。

同步块:synchronized(object){}

synchronized修饰的代码块,会在object对象上加一个监视器。而同步方法会在当前对象this上增加一个监视器。

synchronized同步代码块一般加载共享资源对象上。

java"> //示例1 将buy方法改为同步方法
 private synchronized void buy()
 //示例2 试图按照示例1的方案 将run方法改为同步方法,验证是不行的,因为加上run方法的synchronized将对TakeMoney类添加的监视器,但最终操作的确是account对象,
 //此示例 多线程操作的共共享资源是account,并非TakeMoney
 //示例3 在共享资源list增加synchronized关键字,为其添加监视器

http://www.niftyadmin.cn/n/4926248.html

相关文章

【Java-16】动态代理的使用方法及原理实现

代理模式&#xff1a;静态代理 目标 了解静态代理模式实现 路径 静态代理概述静态代理案例 静态代理概述 静态代理&#xff1a; 是由程序员创建或工具生成代理类的源码&#xff0c;再编译成为字节码 &#xff08;字节码文件在没有运行java之前就存在了&#xff09; 在编译…

docker 安装mongodb 虚拟机安装mongodb

生产环境直接安装比较好&#xff0c;以及使用集群环境&#xff0c;本文仅测试交流使用&#xff0c;我用来写分布式im测试使用&#xff1a; nami-im: 分布式im, 集群 zookeeper netty kafka nacos rpc主要为gate&#xff08;长连接服务&#xff09; logic &#xff08;业务&…

SpringBoot 底层机制分析【Tomcat 启动+Spring 容器初始化+Tomcat 如何关联Spring 容器】【下】

&#x1f600;前言 本篇博文是关于SpringBoot 底层机制分析实现&#xff0c;希望能够帮助你更好的了解SpringBoot &#x1f60a; &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可以帮助到大…

【100天精通python】Day30:使用python操作数据库_数据库基础入门

专栏导读 专栏订阅地址&#xff1a;https://blog.csdn.net/qq_35831906/category_12375510.html 1 数据库基础知识介绍 1.1 什么是数据库&#xff1f; 数据库是一个结构化存储和组织数据的集合&#xff0c;它可以被有效地访问、管理和更新。数据库的目的是为了提供一种可靠的…

美国Linux服务器安装Grafana和配置zabbix数据源的教程

美国Linux服务器的Grafana工具是跨平台、开源、时序和可视化面板Dashboard监控平台工具&#xff0c;是在日常管理中帮忙提高效率的实用工具&#xff0c;可以通过将采集的美国Linux服务器系统数据查询后&#xff0c;进行可视化的展示及通知&#xff0c;本文小编就来介绍下美国Li…

ubuntu16.04环境下qemu模拟开发板vexpress-a9

B站课程学习记录&#xff0c;原视频 BV1NJ411m75T 若按此教程学习&#xff0c;不要使用版本差异太大的源码&#xff0c;会有问题&#xff0c;已经踩过坑。 如有错误感谢指正。 文章目录 一、安装交叉编译链二、安装qemu三、内核编译四、busybox制作文件系统五、uboot启动内核 …

后端开发7.轮播图模块【mongdb开发】

概述 轮播图模块数据库采用mongdb开发 效果图 数据库设计 创建数据库 use sc; 添加数据 db.banner.insertMany([ {bannerId:"1",bannerName:"商城轮播图1",bannerUrl:"http://xx:8020/img/轮播图/shop1.png"}, {bannerId:"2"…

若依管理系统后端将 Mybatis 升级为 Mybatis-Plus

文章目录 说明流程增加依赖修改配置文件注释掉MybatisConfig里面的Bean 代码生成使用IDEA生成代码注意 Controller文件 说明 若依管理系统是一个非常完善的管理系统模板&#xff0c;里面含有代码生成的方法&#xff0c;可以帮助用户快速进行开发&#xff0c;但是项目使用的是m…