多线程同步
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导致的异常结束。
- 执行了同步监视器的wait()方法,则当前线程暂停并释放锁定。
(2) 不释放对同步监视器的锁定
- 程序调用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();
}
- 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();
}
}