美文网首页
多线程同步

多线程同步

作者: engineer_tang | 来源:发表于2020-07-15 08:37 被阅读0次

    1. 通过加关键字“synchronized ”的线程同步

    (1) 同步代码块

    synchronized (obj)
    {
      ...
    //此处的代码就是同步代码块
    }
    

    说明:上面语法中的synchronized 后括号里的obj就是同步监视器,在执行同步代码块前必须先对同步监视器进行锁定。通常把共享资源作为同步监视器。
    账号源码

    package com.threadtest.concrent;
    
    public class Account {
    
        private String name;
    
        private int balance;
    
        public Account(String name, int balance) {
            this.name = name;
            this.balance = balance;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getBalance() {
            return balance;
        }
    
        public void setBalance(int balance) {
            this.balance = balance;
        }
    }
    
    

    存取款线程源码

    package com.threadtest.concrent;
    
    public class DrawMoney implements Runnable {
    
        private Account account;
    
        private int actType;  //0-未知行为;1-存钱;2-取钱
    
        private int money;
    
        public DrawMoney(Account account, int actType, int money) {
            this.account = account;
            this.actType = actType;
            this.money = money;
        }
    
        @Override
        public void run() {
            if(money <= 0) {
                throw new IllegalArgumentException("金额格式不正确!");
            }
            synchronized (account) {
                if(actType == 1) {
                    account.setBalance(account.getBalance() + money);
                } else if (actType == 2) {
                    if(account.getBalance() < money) {
                        throw new IllegalArgumentException("余额不足!");
                    }
                    try {
                        Thread.sleep(200);
                    }catch (InterruptedException e){}
                    account.setBalance(account.getBalance() - money);
                }
            }
        }
    }
    
    

    启动线程执行源码

    package com.threadtest.concrent;
    
    public class MoneyAccountTest {
    
        public static void main(String[] args) {
            Account account = new Account("joe", 1000);
            DrawMoney drawMoney = new DrawMoney(account, 2, 800);
            Thread thread1 = new Thread(drawMoney, "取款1");
            Thread thread2 = new Thread(drawMoney, "取款2");
            thread1.start();
            try {
                Thread.sleep(50);
            }catch (InterruptedException e){}
            thread2.start();
            try {
                thread1.join();
                thread2.join();
            }catch (InterruptedException e) {
    
            }
            System.out.println(String.format("最终的余额为:%s", account.getBalance()));
        }
    }
    
    

    2. 同步方法

    使用:使用关键字“synchronized ”修饰方法,同步监视器为this。即当前同步方法的对象。
    账号源码

    package com.threadtest.concrent;
    
    public class Account {
    
        private String name;
    
        private int balance;
    
        public Account(String name, int balance) {
            this.name = name;
            this.balance = balance;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getBalance() {
            return balance;
        }
    
        public synchronized void payMoney(int money) {
            if(money <= 0) {
                throw new IllegalArgumentException("金额格式不正确!");
            }
            if(balance < money) {
                throw new IllegalArgumentException("余额不足!");
            }
            balance = balance - money;
        }
    }
    
    

    取钱线程的源码

    package com.threadtest.concrent;
    
    public class DrawMoney implements Runnable {
    
        private Account account;
    
        private int actType;  //0-未知行为;1-存钱;2-取钱
    
        private int money;
    
        public DrawMoney(Account account, int actType, int money) {
            this.account = account;
            this.actType = actType;
            this.money = money;
        }
    
        @Override
        public void run() {
            if(money <= 0) {
                throw new IllegalArgumentException("金额格式不正确!");
            }
            account.payMoney(money);
        }
    }
    
    

    线程执行源码

    package com.threadtest.concrent;
    
    public class MoneyAccountTest {
    
        public static void main(String[] args) {
            Account account = new Account("joe", 1000);
            DrawMoney drawMoney = new DrawMoney(account, 2, 800);
            Thread thread1 = new Thread(drawMoney, "取款1");
            Thread thread2 = new Thread(drawMoney, "取款2");
            thread1.start();
            try {
                Thread.sleep(50);
            }catch (InterruptedException e){}
            thread2.start();
            try {
                thread1.join();
                thread2.join();
            }catch (InterruptedException e) {
    
            }
            System.out.println(String.format("最终的余额为:%s", account.getBalance()));
        }
    }
    
    

    3. 释放同步监视器的锁定

    (1) 释放对同步监视器的锁定
    1) 当前线程的同步方法、同步代码块执行结束。
    2) 同步代码块或同步方法中遇到break、return终止执行。
    3)执行过程中出现了未处理的Error或Exception导致的异常结束。

    1. 执行了同步监视器的wait()方法,则当前线程暂停并释放锁定。

    (2) 不释放对同步监视器的锁定

    1. 程序调用Thread.sleep()、Thread.yield()方法来暂停当前线程的执行。
      2)执行线程的suspend()方法将该线程挂起。

    4. 同步锁(Lock)

    1. 同步锁介绍
    特点:显式定义同步锁对象来实现同步机制。
    Lock实现提供了比通过synchronized声明方法提供的锁更广泛的使用场景,它允许更灵活的结构,完全不同的属性,并且可能支持多个关联的对象。

    public interface Lock {
        //获取锁,如果获取锁失败,为了线程调度将被设置为不可用,并处于休眠状态,直到获取到锁
        void lock();
    //获取锁,除非当前线程中断,获取锁(如果可用)并立即返回。
        void lockInterruptibly() throws InterruptedException;
        //只有在调用它时,是空闲的时候才能获取到锁,如果锁可用将立即返回true,否则返回false
        boolean tryLock();
        //获取在给定等待时间内空闲的锁,并且如果锁可用,此方法将立即返回true,否则返回false
        boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
        //释放锁
        void unlock();
          //在等待条件之前,当前线程必须持有锁
        Condition newCondition();
    }
    
    1. ReentrantLock
      公平锁的实现
    package com.threadtest.lock;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class FruitInfo {
    
    //默认为false,设置为true代表使用公平锁
        private final ReentrantLock reentrantLock = new ReentrantLock(true);
    
        private String name;
    
        private double price;
    
        public FruitInfo(String name, double price) {
            this.name = name;
            this.price = price;
        }
    
        public String getName() {
            return name;
        }
    
        public double getPrice() {
            return price;
        }
    
        public void updatePrice(double price) {
            for(int i=0; i<2; i++) {
                reentrantLock.lock();
                System.out.println(String.format("线程名 %s 修改了价格.", Thread.currentThread().getName()));
                try {
                    this.price = price;
    //                TimeUnit.SECONDS.sleep(2);
                    sleep(50);
                } finally {
                    reentrantLock.unlock();
                }
            }
        }
    
        private void sleep(long time) {
            try {
                Thread.sleep(time);
            }catch (InterruptedException e) {
    
            }
        }
    }
    
    

    线程执行源码

    package com.threadtest.lock;
    
    import java.util.concurrent.locks.ReentrantLock;
    
    public class ReentrantLockThread implements Runnable {
    
        private FruitInfo fruitInfo;
    
        private double price;
    
        public ReentrantLockThread(FruitInfo fruitInfo, double price) {
            this.fruitInfo = fruitInfo;
            this.price = price;
        }
    
        @Override
        public void run() {
            fruitInfo.updatePrice(price);
        }
    
        public static void main(String[] args) {
            FruitInfo fruitInfo = new FruitInfo("苹果", 4.6);
            ReentrantLockThread reentrantLockThread1 = new ReentrantLockThread(fruitInfo, 3.9);
            ReentrantLockThread reentrantLockThread2 = new ReentrantLockThread(fruitInfo, 8.1);
            ReentrantLockThread reentrantLockThread3 = new ReentrantLockThread(fruitInfo, 6.5);
            ReentrantLockThread reentrantLockThread4 = new ReentrantLockThread(fruitInfo, 2.4);
            ReentrantLockThread reentrantLockThread5 = new ReentrantLockThread(fruitInfo, 5.8);
                new Thread(reentrantLockThread1, "线程A").start();
                new Thread(reentrantLockThread2, "线程B").start();
                new Thread(reentrantLockThread3, "线程C").start();
                new Thread(reentrantLockThread4, "线程D").start();
                new Thread(reentrantLockThread5, "线程E").start();
        }
    }
    
    

    5. 线程通信

    账号信息源码

    package com.threadtest.exchange;
    
    public class Account {
    
        private String accountNo;
    
        private int balance;
    
        private boolean state = false;
    
        public Account(String accountNo, int balance, boolean state) {
            this.accountNo = accountNo;
            this.balance = balance;
            this.state = state;
        }
    
        public synchronized void draw(int money) {
            try {
                if (state == false) {
                    wait();
                } else {
                    balance = balance - money;
                    System.out.println(String.format("%s 花费了 %s, 剩余:%s", Thread.currentThread().getName(), money, balance));
                    state = false;
                    notifyAll();
                }
            } catch (InterruptedException e) {
    
            }
        }
    
        public synchronized void deposit(int money) {
            try {
                if (state == true) {
                    wait();
                } else {
                    balance = balance + money;
                    System.out.println(String.format("%s 存钱了 %s, 剩余:%s", Thread.currentThread().getName(), money, balance));
                    state = true;
                    notifyAll();
                }
            } catch (InterruptedException e) {
    
            }
        }
    }
    

    取钱业务代码

    package com.threadtest.exchange;
    
    public class DrawThread implements Runnable {
    
        private Account account;
    
        public DrawThread(Account account) {
            this.account = account;
        }
    
        @Override
        public void run() {
            for(int i=0; i<5; i++) {
                int money = 300;
                account.draw(money);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
    
                }
            }
        }
    }
    
    

    存钱业务代码

    package com.threadtest.exchange;
    
    public class DispositThread implements Runnable {
    
        private Account account;
    
        public DispositThread(Account account) {
            this.account = account;
        }
    
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                int money = 400;
                account.deposit(money);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
    
                }
            }
        }
    }
    
    

    存取款执行测试

    package com.threadtest.exchange;
    
    public class ExchangeTest {
    
        public static void main(String[] args) {
            Account account = new Account("joe", 700, false);
            DrawThread drawThread = new DrawThread(account);
            DispositThread dispositThread = new DispositThread(account);
            new Thread(drawThread, "取现线程").start();
            new Thread(dispositThread, "存钱线程").start();
        }
    }
    

    相关文章

      网友评论

          本文标题:多线程同步

          本文链接:https://www.haomeiwen.com/subject/etnrhktx.html