• 多线程基础同步(四)


    同步方法:

    • 由于我们可以通过private关键字来保证数据对象只能被方法访问,所以我们只需要针对方法
      提出一套机制,这套机制就是synchronized关键字,它包括两种用法:
      synchronized方法和synchronized块;
    同步方法:public synchronized void method(int ages){}
    
    • synchronized方法控制对“对象”的访问,每个对象对应一把锁,每个
      synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,
      方法一旦执行,就独占该锁,直到该方法返回才释放锁,后面被阻塞的线程才能获得这个
      锁,继续执行;
    缺陷:若将一个大的方法申明为synchronized将会影响效率
    

    同步方法的弊端

    • 方法里面需要修改的内容才需要锁,锁的太多,浪费资源
    • 同步块:synchronized(Obj) {}
    • Obj称之为同步监视器
      • Obj可以是任何对象,但是推荐使用共享资源作为同步监视器
      • 同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,
        就是这个对象本身,或者是class反射中讲解

    同步监视器的执行过程

    1. 第一个线程访问,锁定同步监视器,执行其中代码
    2. 第二个线程访问,发现同步监视器被锁定,无法访问
    3. 第一个线程访问完毕,解锁同步监视器
    4. 第二个线程访问,发现同步监视器没有锁,然后锁定并访问

    例子1:银行取钱不安全

    package com.kaiyuan.syn;
    
    
    import org.apache.commons.io.input.TaggedReader;
    
    // 不安全的取钱
    // 两个人去银行取钱,账户
    public class UnsafeBank {
    
        public static void main(String[] args) {
    
            // 账户
            Account account = new Account(100,"结婚基金");
    
            Drawing you = new Drawing(account, 50,"你");
            Drawing girlFriend = new Drawing(account, 500,"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;
    
        }
    
        // 取钱
        @Override
        public void run() {
            synchronized (account) {
                // 判断有没有钱
                if (account.money-drawingMoney<0) {
                    System.out.println(Thread.currentThread().getName()+"钱不够,取不了");
    
                    return;
                }
                // 模拟延时,放大问题的发生性
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
                // 卡内余额 = 余额 - 你取的钱
                account.money = account.money - drawingMoney;
    
                // 你手里的钱
                nowMoney = nowMoney + drawingMoney;
    
                System.out.println(account.name+"余额为:"+account.money);
                // Thread.currentThread().getName() = this.getName();
                System.out.println(this.getName()+"手里的钱:"+ nowMoney);
            }
    
    
    
    
        }
    }
    

    例子2不安全list的同步

    package com.kaiyuan.syn;
    
    import java.util.ArrayList;
    import java.util.List;
    
    // 线程不安全的集合
    public class UnsafeList {
        public static void main(String[] args) {
            List<String> list = new ArrayList<String>();
    
                for (int i = 0; i < 10000; i++) {
                    new Thread(() -> {
                        synchronized (list) {
                            list.add(Thread.currentThread().getName());
                        }
                    }).start();
    
                }
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(list.size());
    
    
    
    
    
        }
    }
    
  • 相关阅读:
    Linux:grep 命令
    Linux:sort
    Java中路径相关的获取方式
    Spring boot 配置 Tomcat 临时文件缓存目录
    zabbix :web 界面显示的监控项值为0或者空
    Maven:禁止编码指定类型的资源文件
    Maven:element '******' cannot have character [children]
    MySQL:数据库名或者数据表名包含-
    Linux:sed
    Lucene 6.0下使用IK分词器
  • 原文地址:https://www.cnblogs.com/spider3658/p/15913336.html
Copyright © 2020-2023  润新知