• Java多线程-synchronized同步方法及同步块简述


    前言

    synchronized是隐示锁,出了作用域自动释放(同步方法或者同步代码块),Lock是手显示锁(手动加锁和释放锁,lock和unLock,别忘记关闭锁,不然可能造成死锁)

    队列+锁 synchronized 两种用法synchronized方法和synchronized块

    1 同步方法,给方法增加synchronized关键字

    public synchronized void method(int args){}
    
    
    • 1
    • 2

    synchronized方法控制对对象的访问,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就会独占该锁,知道该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行

    缺陷:若一个大的方法声明为synchronized将会影响效率,方法里面需要修改的内容才需要锁,锁的太多,资源浪费,这时候可以使用synchronized同步块

    2 同步块:synchronized(obj){}

    obj称之为同步监视器

    1. obj可以是任何对象.但是推荐使用共享资源作为同步监视器
    2. 同步方法中无需指定同步监视器.因为同步的方法的同步监听器就是this,也就是这个对象本身.或者是class(反射)
      同步监视器的执行过程
    3. 第一个线程访问,锁定同步监视器,执行其中代码
    4. 第二个线程访问,发现同步监视器被锁定,无法访问
    5. 第一个线程访问完毕,解锁同步监视器
    6. 第二个线程访问,发现同步监视器没有锁,然后锁定并访问

    我们对上文描述的例子进行更改

    上文链接:https://editor.csdn.net/md/?articleId=126755984

    通过synchronized方法使代码安全

    package com.wyh.thread;
    
    /**
     * @program: Thread
     * @description: 线程不安全的案例 买票
     * @author: 魏一鹤
     * @createDate: 2022-01-09 21:55
     **/
    
    //不安全的买票
    //线程不安全 有负数 有重复
    public class TestThreadUnSafeBuyTicket {
        //主线程 用户线程
        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 {
    
        //票
        int tickNum=10;
        //外部停止方式
        // 是否停止标识 true不停止 false停止
        boolean flag=true;
        @Override
        public void run() {
            //买票
            while (flag) {
                try {
                    buy();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        //买票方法
        //synchronized同步方法.锁的是this
        private synchronized void buy() throws InterruptedException {
            //判断是否有票 没有票直接return不做处理
            if(tickNum<=0){
                //标识符为false 线程停止
                flag=false;
                return;
            }
            //模拟延时 放大问题
            Thread.sleep(100);
            //有票的话就直接自减
            System.out.println(Thread.currentThread().getName() + "买到了第"+tickNum--+"张票");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    通过synchronized同步块使代码安全

    package com.wyh.thread;
    
    /**
     * @program: Thread
     * @description: 线程不安全例子 银行取钱
     * @author: 魏一鹤
     * @createDate: 2022-01-09 22:19
     **/
    
    //不安全的取钱 两个人去银行取钱
    public class TestThreadUnSafeBank  {
        public static void main(String[] args){
            //账户
            Account account = new Account(100,"结婚基金");
            //我要取钱50
            Drawing you=new Drawing(account,50,"你");
            //女朋友取100
            Drawing girlFriend=new Drawing(account,100,"girlFriend");
            //多线程取钱
            you.start();
            girlFriend.start();
        }
    }
    //账户
    class Account{
        //余额
        int money;
        //卡号
        String name;
        //有参构造方法
        public Account(int money, String name) {
            this.money = money;
            this.name = name;
        }
    }
    //银行 模拟取款
    class Drawing  extends Thread{
        //账户
        Account account;
        //取了多少钱
        int drawingMoney;
        //现在手里有多少钱
        int nowMoney;
        //有参构造方法
       public Drawing(Account account,int drawingMoney,String name){
           super(name);
           this.account=account;
           this.drawingMoney=drawingMoney;
       }
       //重写run方法
        //取钱
        //synchronized默认锁的是this
        @Override
        public void run() {
           //对账户进行同步代码块处理
           //锁的对象是就是变化的量,需要增删改的对象
           synchronized (account) {
               //判断有没有钱
               if(account.money-drawingMoney<0){
                   System.out.println(Thread.currentThread().getName() + "钱不够了,取不了");
                   return;
               }
    
               //模拟延时 放大问题
               try {
                   Thread.sleep(100);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               //银行卡内余额=余额-你取得钱
               account.money=account.money-drawingMoney;
               //你手里的钱
               nowMoney=nowMoney+drawingMoney;
               //打印账户余额
               System.out.println(account.name +"的余额为:"+account.money);
               //打印你手里的钱  this.getName()=Thread.currentThread().getName()  也是当前线程的名称
               System.out.println(this.getName()+"手里的钱:"+nowMoney);
           }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    通过synchronized同步块使代码安全

    package com.wyh.thread;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Vector;
    
    /**
     * @program: Thread
     * @description: 线程不安全list(arrayList)
     * @author: 魏一鹤
     * @createDate: 2022-01-09 22:50
     **/
    
    //线程不安全的集合 ArrayList线程就是不安全的 与之对应的list实现Vector是线程安全的
    public class TestThreadUnSafeList {
       public static void main(String[] args) throws InterruptedException {
           List<String> arrayList = new ArrayList<String>();
           //使用线程循环插入元素
           for (int i = 0; i < 10000; i++) {
               //10000个线程
               new Thread(()->{
                  synchronized (arrayList){
                      //把线程的名字添加到list中
                      arrayList.add(Thread.currentThread().getName());
                  }
               }).start();
           }
           Thread.sleep(300);
           //list的大小
           System.out.println(arrayList.size());
           //ArrayList线程不安全的原因是什么呢
           //多个线程操作同一个位置,把两个数组添加到同一位置,就会把元素进行覆盖,进行数据丢失
       }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    synchronized同步方法和synchronized同步代码块的区别

    1. 同步方法默认使用this或者当前类做为锁。

    2. 同步代码块可以选择以什么来加锁,比同步方法更精确,我们可以选择只有会在同步发生同步问题的代码加锁,而并不是整个方法。

    3. 同步方法使用synchronized修饰,而同步代码块使用synchronized(obj){}修饰。

    总结

    使用方式不同,synchronized使整个方法同步,方法特别大的时候整体同步会降低效率,synchronized使用的是synchronized(obj){}的方式得需要改动的对象的代码进行同步, obj可以是任何对象,一般锁的事这个对象是变化的量,是需要增删改的对象,使用起来更加方便

  • 相关阅读:
    谷歌新大楼正式开放:“龙鳞”顶棚、100%电动、最大地热桩系统······
    uniapp制作多选下拉框和富文本(短信页面)
    k8s-9.部署elk日志收集
    云原生之快速使用Nacos Spring Cloud
    在做事上面体现出来,您之心胸所在,心有惊雷而面不改色,可拜上将军---程序员
    [附源码]计算机毕业设计JAVA基于jsp的网上点餐系统
    Google protobuf使用技巧和经验总结
    Vue非父子组件之间的通信
    Spark Rdd之mapToPair,flatMapToPair
    IM即时通讯系统[SpringBoot+Netty]——梳理(总)
  • 原文地址:https://blog.csdn.net/weixin_46713508/article/details/126756005