zoukankan      html  css  js  c++  java
  • 第七天学习多线程同步和锁

    多线程的线程同步和锁

    线程同步

    当多个线程访问同一个对象,并且线程还想修改对象,这时候就需要线程同步,线程同步其实就是一个等待机制,多个需要访问此对象的线程进入对象的等待池形成队列,等待前一个线程使用完毕,下一个线程再使用。

    • 线程同步的形成条件:队列+锁

    队列

    • 线程排队。

    锁_synchronized隐式定义锁

    • 一个线程持有锁会导致其他所有需要此锁的线程挂起。
    • 多线程竞争下,加锁,释放锁对导致较多的上下文切换和调度问题,引起性能问题。
    • 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题。
    • 方法里需要修改的内容才需要锁,锁太多,资源浪费。
    • 总之就是性能下降!
    public synchronized void method(){}
    
    • 默认的synchronized修饰词锁的是this(类本身)。
    • synchronized同步块锁的是任何对象。

    怎么判断该锁谁!

    package syn;
    
    //不安全取钱
    //两个人去银行取钱
    //银行需要账户
    public class UnsafeBank {
    
        public static void main(String[] args) {
            Account account = new Account(1000,"结婚基金");
            Drawing you = new Drawing(account,50,"你");
            Drawing girl = new Drawing(account,100,"你老婆");
            you.start();
            girl.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(100);
                } catch (InterruptedException e) {
                }
                account.money = account.money-drawingMoney;
                nowMoney = nowMoney +drawingMoney;
                System.out.println(account.name+"余额为:"+account.money);
                System.out.println(Thread.currentThread().getName()+"手里的钱:"+nowMoney);
    
            }
            
        }
    }
    
    
    • 首先应该为进行数据增、删、改的地方加锁。
    • 上述代码中run方法中加锁的话默认锁住了本类(银行),但银行并没有变化,变化的是账户的钱。
    • 这时使用同步代码块,参数放入账户的钱,修改语句放在块里即可锁住。

    死锁

    • 多个线程互相抱着对方需要的资源,然后形成僵持
    package syn;
    
    //死锁:多个线程互相抱着对方需要的资源,然后形成僵持
    public class DeadLock {
        public static void main(String[] args) {
            Makeup g1 = new Makeup(0, "灰姑娘");
            Makeup g2 = new Makeup(1, "白雪公主");
            g1.start();
            g2.start();
        }
    }
    
    //口红
    class Lipstick {
    }
    
    //镜子
    class Mirror {
    }
    
    class Makeup extends Thread {
        //需要的资源,只有一份(static保证唯一)
        static Lipstick lipstick = new Lipstick();
        static Mirror mirror = new Mirror();
    
        int choise; //选择
        String girlName;//使用化妆品的人
    
        Makeup(int choise, String girlName) {
            this.choise = choise;
            this.girlName = girlName;
        }
    
        @Override
        public void run() {
            try {
                makeup();
            } catch (InterruptedException e) {
    
            }
        }
    
        //互相持有对方的锁(需要拿到对方的资源)
        private void makeup() throws InterruptedException {
    
            if (choise == 0) {
                synchronized (lipstick) {
                    //口红的锁
                    System.out.println(this.girlName + "获得了口红的锁");
                    Thread.sleep(1000);
                    
                    synchronized (mirror) {//一秒钟后获得了镜子
                        //镜子的锁
                        System.out.println(this.girlName + "获得了镜子的锁");
                    }
                }
            } else {
                synchronized (mirror) {
                    //镜子的锁
                    System.out.println(this.girlName + "获得了镜子的锁");
                    Thread.sleep(2000);
    
                    synchronized (lipstick) {//一秒钟后获得了口红
                        //口红的锁
                        System.out.println(this.girlName + "获得了口红的锁");
                    }
                }
            }
        }
    }
    
    • 互斥条件:一个资源每次只能被一个进程使用。
    • 请求与保持条件:一个进程因请求资源而阻塞时,对以获得的资源保持不放。
    • 不剥夺条件:进程已获得的资源,在未使用之前,不能强行剥夺。
    • 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

    只要解决以上一条即可避免死锁(锁不能嵌套锁!)

    Lock锁_显式定义锁

    ReentrantLock(可重入锁)类实现了Lock

    class A {
        private final ReentrantLock lock = new ReenTrantLock();
        
        public void m(){
            lock.lock();//上锁
            try{
                //保证线程安全的代码
            }finally{
                lock.unlock();//开锁
            }
        }
    }
    

    synchronized与Lock对比

    1. Lock是显式锁,手动开启后需要关闭,synchronized是隐式锁,出了作用域自动释放。
    2. Lock只有代码块锁,synchronized有代码块锁和方法锁。
    3. Lock锁性能更好,JVM花费的调度时间更少,性能和扩展性都更好。
    4. 优先使用顺序:Lock>synchronized代码块>synchronized方法锁
  • 相关阅读:
    python变量及简单数据类型
    python函数小案例
    python字符串和列表小案例
    python循环输出
    Jmeter测试工具
    操作系统-进程管理~
    操作系统~
    组成原理第一章笔记~
    分时间段(年份或月份)统计,没有数字补0 Java(替代 MYSQL) 做法
    组成原理复习概略
  • 原文地址:https://www.cnblogs.com/sxblogs/p/12896724.html
Copyright © 2011-2022 走看看