多线程同步

2020-07-15  本文已影响0人  engineer_tang

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();
    }
}
上一篇下一篇

猜你喜欢

热点阅读