zoukankan      html  css  js  c++  java
  • Java-JUC(七):同步锁的几种方式

    为什么要使用同步锁?

    因为当使用多线程同时访问一个变量或对象时,如果这些线程中即有读又有写操作时,会造成导致变量或对象的状态出现混乱。例如:一个银行账户被A/B两个线程同时操作,A线程、B线程同时开始操作:A线程存款100,B线程取款100,此时就会出现账户存款100,然后查询存储结果为0,B取款失败,但是查询余额为100。

    而同步锁出现的目的就是为了解决多线程安全问题。

    上边的举例对应的代码如下:

    银行类:

    /**
     * 银行类
     * */
    public class Bank {
        private int money = 0;
    
        /**
         * 存款
         */
        public void deposit(int money) {
            this.money += money;
            System.out.println(Thread.currentThread().getName() + ":存款" + money + ",賬戶餘額:" + this.money);
        }
    
        /**
         * 取款
         */
        public void withdrawal(int money) {
            if (this.money - money < 0) {
                System.out.println(Thread.currentThread().getName() + ":余额不足");
                return;
            }
            this.money -= money;
            System.out.println(Thread.currentThread().getName() + ":取款" + money + ",賬戶餘額:" + this.money);
        }
    
        /**
         * 查詢賬戶餘額
         */
        public void look() {
            System.out.println(Thread.currentThread().getName() + ":查詢賬戶餘額為:" + this.money);
        }
    }

    取款、存款A、B线程模拟:

    public class LockTest {
        public static void main(String[] args) {
            final Bank bank = new Bank();
    
            Thread threadA = new Thread(new Runnable() {
                public void run() {
                    while (true) {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        bank.deposit(100);
                        bank.look();
    
                        System.out.println("-----------------------");
                    }
                }
            }, "thread-a");
    
            Thread threadB = new Thread(new Runnable() {
                public void run() {
                    while (true) {
                        System.out.println("-----------------------");
                        bank.withdrawal(100);
                        bank.look();
    
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }, "thread-b");
    
            threadB.start();
            threadA.start();
        }
    }

    此时打印的结果为:

    -----------------------
    thread-b:余额不足
    thread-b:查詢賬戶餘額為:0
    -----------------------
    thread-a:存款100,賬戶餘額:100
    thread-a:查詢賬戶餘額為:0
    thread-b:取款100,賬戶餘額:0
    thread-b:查詢賬戶餘額為:0
    -----------------------

    同步代码块:

    注意:同步是一种高开销的操作,因此应该尽量减少同步的内容。通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。

    /**
     * 银行类
     * */
    class Bank {
        private int money = 0;
    
        /**
         * 存款
         */
        public void deposit(int money) {
            synchronized (this) {
                this.money += money;
                System.out.println(Thread.currentThread().getName() + ":存款" + money + ",賬戶餘額:" + this.money);
            }
        }
    
        /**
         * 取款
         */
        public void withdrawal(int money) {
            synchronized (this) {
                if (this.money - money < 0) {
                    System.out.println(Thread.currentThread().getName() + ":余额不足");
                    return;
                }
                this.money -= money;
                System.out.println(Thread.currentThread().getName() + ":取款" + money + ",賬戶餘額:" + this.money);
            }
        }
    
        /**
         * 查詢賬戶餘額
         */
        public void look() {
            synchronized (this) {
                System.out.println(Thread.currentThread().getName() + ":查詢賬戶餘額為:" + this.money);    
            }
        }
    }

    测试:

    -----------------------
    thread-b:余额不足
    thread-b:查詢賬戶餘額為:0
    thread-a:存款100,賬戶餘額:100
    thread-a:查詢賬戶餘額為:100
    -----------------------
    -----------------------
    thread-b:取款100,賬戶餘額:0
    thread-b:查詢賬戶餘額為:0
    thread-a:存款100,賬戶餘額:100
    thread-a:查詢賬戶餘額為:100
    -----------------------
    -----------------------
    thread-b:取款100,賬戶餘額:0
    thread-b:查詢賬戶餘額為:0
    thread-a:存款100,賬戶餘額:100
    thread-a:查詢賬戶餘額為:100

    同步方法:

    同步方法就是使用synchronized关键字修饰某个方法,这个方法就是同步方法。这个同步方法(非static方法)无须显式指定同步监视器,同步方法的同步监视器是this,也就是调用该方法的对象。通过同步方法可以非常方便的实现线程安全的类,线程安全的类有如下特征:

    每个线程调用该对象的任意synchronized方法之后,都能得到正确的结果;
    每个线程调用该对象的任意synchronized方法之后,该对象状态依然能保持合理状态。
    /**
     * 银行类
     */
    class Bank {
        private int money = 0;
    
        /**
         * 存款
         */
        public synchronized void deposit(int money) {
            this.money += money;
            System.out.println(Thread.currentThread().getName() + ":存款" + money + ",賬戶餘額:" + this.money);
        }
    
        /**
         * 取款
         */
        public synchronized void withdrawal(int money) {
            if (this.money - money < 0) {
                System.out.println(Thread.currentThread().getName() + ":余额不足");
                return;
            }
            this.money -= money;
            System.out.println(Thread.currentThread().getName() + ":取款" + money + ",賬戶餘額:" + this.money);
        }
    
        /**
         * 查詢賬戶餘額
         */
        public synchronized void look() {
            System.out.println(Thread.currentThread().getName() + ":查詢賬戶餘額為:" + this.money);
        }
    }

    测试:

    -----------------------
    thread-b:余额不足
    thread-b:查詢賬戶餘額為:0
    -----------------------
    thread-a:存款100,賬戶餘額:100
    thread-b:取款100,賬戶餘額:0
    thread-b:查詢賬戶餘額為:0
    thread-a:查詢賬戶餘額為:0
    -----------------------
    -----------------------
    thread-a:存款100,賬戶餘額:100
    thread-a:查詢賬戶餘額為:100
    -----------------------

    重入锁Lock

    JavaSE5.0中新增了一个java.util.concurrent包来支持同步。ReentrantLock类是可重入、互斥、实现了Lock接口的锁, 它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力。
    ReenreantLock类的常用方法有:

    Lock lock=new ReentrantLock() ;// 创建一个ReentrantLock实例
    lock.lock(); // 获得锁
    lock.unlock();// 释放锁

    注意:ReentrantLock()还有一个可以创建公平锁的构造方法,但由于能大幅度降低程序运行效率,不推荐使用。

    /**
     * 银行类
     */
    class Bank {
        private int money = 0;
        private Lock lock=new ReentrantLock();
        /**
         * 存款
         */
        public void deposit(int money) {
            lock.lock();
            try{
                this.money += money;
                System.out.println(Thread.currentThread().getName() + ":存款" + money + ",賬戶餘額:" + this.money);
            }finally{
                lock.unlock();
            }
        }
    
        /**
         * 取款
         */
        public void withdrawal(int money) {
            lock.lock();
            try{
                if (this.money - money < 0) {
                    System.out.println(Thread.currentThread().getName() + ":余额不足");
                    return;
                }
                this.money -= money;
                System.out.println(Thread.currentThread().getName() + ":取款" + money + ",賬戶餘額:" + this.money);
            }finally{
                lock.unlock();
            }
        }
    
        /**
         * 查詢賬戶餘額
         */
        public void look() {
            lock.lock();
            try{
                System.out.println(Thread.currentThread().getName() + ":查詢賬戶餘額為:" + this.money);
            }finally{
                lock.unlock();
            }
        }
    }

    测试:

    -----------------------
    thread-b:余额不足
    thread-b:查詢賬戶餘額為:0
    thread-a:存款100,賬戶餘額:100
    thread-a:查詢賬戶餘額為:100
    -----------------------
    -----------------------
    thread-b:取款100,賬戶餘額:0
    thread-b:查詢賬戶餘額為:0
    thread-a:存款100,賬戶餘額:100
    thread-a:查詢賬戶餘額為:100
    -----------------------
  • 相关阅读:
    如何去掉myeclipse的web项目启动时出现的 CodeLive Panel
    day35_Spring学习回顾_03
    在CentOS/RHEL上设置SSH免密码登录
    超全Linux备份工具集合,满足你的所有需要!
    使用Gnupg对Linux系统中的文件进行加密
    Linux压缩那些事儿
    或许是 Nginx 上配置 HTTP2 最实在的教程了
    Systemd 三部曲 之 PHP7
    Linux kernel模块管理相关详解
    搜狐视频Redis私有云平台CacheCloud
  • 原文地址:https://www.cnblogs.com/yy3b2007com/p/8921997.html
Copyright © 2011-2022 走看看