多个线程之间共享数据的方式(卖票问题,存取款问题)
2015-07-15 17:00
423 查看
多线程共享数据,其实要分为两种情况:
1.多线程执行相同的代码处理数据,最经典的问题就是卖票;
2.多线程执行不同的代码处理数据,最经典的问题就是银行存取钱。
卖票问题探究:
最初的代码是:
以上代码创建了两个线程,因为创建这两个线程时传的是同一个Runnable对象,所以如果这个Runnable对象有成员变量的话,这两个线程就可都操作这个Runnable对象的成员变量。
执行上面代码多次,发现基本上每次打印的结果都不相同,这就是线程不安全。所谓线程安全就是多线程每次执行的结果都是固定的、可控的。为解决线程安全问题,就要用到同步监视器,synchronized或者
Lock。
如果用传统的synchronized的话,有两种方式:
1.synchronized同步代码块:synchronized(this){...}或者synchronized(Object obj){...}。同步代码块放在run方法体中,相对于下面的synchronized同步方法,推荐优先使用synchronized同步代码块。
2.synchronized同步方法:用synchronized修饰普通方法,可以有返回值,也可以没有返回值,在run方法中调用此同步方法。
用synchronized同步代码块解决卖票问题:
银行存取款问题探究:
存取款问题,因为存款与取款是对同一账户的成员变量进行操作,但是执行方法体不一样,所以需要创建两种线程。首先创建一个账户类,此类有一个余额成员变量。实例化这两种线程时要传同一个账户类对象,这样这两种线程操作的就是同一个账户对象也就是同一个账户的余额成员变量了。
如果在这两种线程的run方法中用synchronized同步代码块的话,则需要有2种synchronized同步代码块。因为同步代码块中必然会操作到余额变量,但是没有在线程类中声明此余额变量,只能通过账户的get方法得到余额,进而进行操作:
/**
* 账户类,包含余额成员变量
*
*/
class Account {
private double balance;
private boolean flag = false;
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public Account(double balance) {
super();
this.balance = balance;
}
}
class DrawThread extends Thread {
private Account account;
private double drawAmount;
public DrawThread(String name, Account account, double drawAmount) {
super(name);
this.account = account;
this.drawAmount = drawAmount;
}
@Override
public void run() {
while (true) {
synchronized (account) {
try {
if (!account.isFlag()) {
account.wait();
} else {
account.setBalance(account.getBalance() - drawAmount);
System.out.println(Thread.currentThread().getName() + "取现" + drawAmount + ",账户余额为:"
+ account.getBalance());
account.setFlag(false);
account.notifyAll();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
// 存款
class DepositThread extends Thread {
private Account account;
private double depositAmount;
public DepositThread(String name, Account account, double depositAmount) {
super(name);
this.account = account;
this.depositAmount = depositAmount;
}
@Override
public void run() {
while (true) {
synchronized (account) {
try {
if (account.isFlag()) {
account.wait();
} else {
account.setBalance(account.getBalance() + depositAmount);
System.out.println(Thread.currentThread().getName() + "存款" + depositAmount + ",账户余额为:"
+ account.getBalance());
account.setFlag(true);
account.notifyAll();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
Account acct = new Account(0);
for (int i = 1; i <= 2; i++) {
new DrawThread("取款者" + i, acct, 500).start();
new DepositThread("存款者" + i, acct, 500).start();
}
}
} 如果使用synchronized同步方法的话,需要有两种synchronized同步方法。在run方法中调用synchronized同步方法,为了避免上面的共享变量的声明的麻烦,可以把synchronized同步方法声明在账户类中,这样在线程类中就不用声明该共享变量了:
/**
* 账户类,包含余额成员变量
*
*/
class Account {
private double balance;
private boolean flag = false;
public Account(double balance) {
super();
this.balance = balance;
}
// 取款
public synchronized void draw(double drawAmount) {
try {
if (!flag) {
this.wait();
} else {
balance -= drawAmount;
System.out.println(Thread.currentThread().getName() + "取现" + drawAmount + ",账户余额为:" + balance);
flag = false;
this.notifyAll();
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 存款
public synchronized void deposit(double depositAmount) {
try {
if (flag) {
this.wait();
} else {
balance += depositAmount;
System.out.println(Thread.currentThread().getName() + "存款" + depositAmount + ",账户余额为:" + balance);
flag = true;
this.notifyAll();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class DrawThread extends Thread {
private Account account;
private double drawAmount;
public DrawThread(String name, Account account, double drawAmount) {
super(name);
this.account = account;
this.drawAmount = drawAmount;
}
@Override
public void run() {
while (true) {
account.draw(drawAmount);
}
}
}
// 存款
class DepositThread extends Thread {
private Account account;
private double depositAmount;
public DepositThread(String name, Account account, double depositAmount) {
super(name);
this.account = account;
this.depositAmount = depositAmount;
}
@Override
public void run() {
while (true) {
account.deposit(depositAmount);
}
}
}
public class ThreadTest {
public static void main(String[] args) {
Account acct = new Account(0);
for (int i = 1; i <= 2; i++) {
new DrawThread("取款者" + i, acct, 500).start();
new DepositThread("存款者" + i, acct, 500).start();
}
}
}
1.多线程执行相同的代码处理数据,最经典的问题就是卖票;
2.多线程执行不同的代码处理数据,最经典的问题就是银行存取钱。
卖票问题探究:
最初的代码是:
public class Test1 { public static void main(String[] args) { Ticket target = new Ticket(); Thread threadA = new Thread(target, "A"); Thread threadB = new Thread(target, "B"); threadA.start(); threadB.start(); } } class Ticket implements Runnable { private int leftTicket = 500; @Override public void run() { while (leftTicket > 0) { leftTicket = leftTicket - 1; System.out.println(Thread.currentThread().getName() + "处理后还剩" + leftTicket + "张票"); } } }
以上代码创建了两个线程,因为创建这两个线程时传的是同一个Runnable对象,所以如果这个Runnable对象有成员变量的话,这两个线程就可都操作这个Runnable对象的成员变量。
执行上面代码多次,发现基本上每次打印的结果都不相同,这就是线程不安全。所谓线程安全就是多线程每次执行的结果都是固定的、可控的。为解决线程安全问题,就要用到同步监视器,synchronized或者
Lock。
如果用传统的synchronized的话,有两种方式:
1.synchronized同步代码块:synchronized(this){...}或者synchronized(Object obj){...}。同步代码块放在run方法体中,相对于下面的synchronized同步方法,推荐优先使用synchronized同步代码块。
2.synchronized同步方法:用synchronized修饰普通方法,可以有返回值,也可以没有返回值,在run方法中调用此同步方法。
用synchronized同步代码块解决卖票问题:
public class Test1 { public static void main(String[] args) { Ticket target = new Ticket(); Thread threadA = new Thread(target, "A"); Thread threadB = new Thread(target, "B"); threadA.start(); threadB.start(); } } class Ticket implements Runnable { private int leftTicket = 500; @Override public void run() { while (leftTicket > 0) { synchronized (this) { leftTicket = leftTicket - 1; System.out.println(Thread.currentThread().getName() + "处理后还剩" + leftTicket + "张票"); } } } }这样,每次执行的结果都一样,且leftTicket 的值是依次减小的。
银行存取款问题探究:
存取款问题,因为存款与取款是对同一账户的成员变量进行操作,但是执行方法体不一样,所以需要创建两种线程。首先创建一个账户类,此类有一个余额成员变量。实例化这两种线程时要传同一个账户类对象,这样这两种线程操作的就是同一个账户对象也就是同一个账户的余额成员变量了。
如果在这两种线程的run方法中用synchronized同步代码块的话,则需要有2种synchronized同步代码块。因为同步代码块中必然会操作到余额变量,但是没有在线程类中声明此余额变量,只能通过账户的get方法得到余额,进而进行操作:
/**
* 账户类,包含余额成员变量
*
*/
class Account {
private double balance;
private boolean flag = false;
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public Account(double balance) {
super();
this.balance = balance;
}
}
class DrawThread extends Thread {
private Account account;
private double drawAmount;
public DrawThread(String name, Account account, double drawAmount) {
super(name);
this.account = account;
this.drawAmount = drawAmount;
}
@Override
public void run() {
while (true) {
synchronized (account) {
try {
if (!account.isFlag()) {
account.wait();
} else {
account.setBalance(account.getBalance() - drawAmount);
System.out.println(Thread.currentThread().getName() + "取现" + drawAmount + ",账户余额为:"
+ account.getBalance());
account.setFlag(false);
account.notifyAll();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
// 存款
class DepositThread extends Thread {
private Account account;
private double depositAmount;
public DepositThread(String name, Account account, double depositAmount) {
super(name);
this.account = account;
this.depositAmount = depositAmount;
}
@Override
public void run() {
while (true) {
synchronized (account) {
try {
if (account.isFlag()) {
account.wait();
} else {
account.setBalance(account.getBalance() + depositAmount);
System.out.println(Thread.currentThread().getName() + "存款" + depositAmount + ",账户余额为:"
+ account.getBalance());
account.setFlag(true);
account.notifyAll();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
Account acct = new Account(0);
for (int i = 1; i <= 2; i++) {
new DrawThread("取款者" + i, acct, 500).start();
new DepositThread("存款者" + i, acct, 500).start();
}
}
} 如果使用synchronized同步方法的话,需要有两种synchronized同步方法。在run方法中调用synchronized同步方法,为了避免上面的共享变量的声明的麻烦,可以把synchronized同步方法声明在账户类中,这样在线程类中就不用声明该共享变量了:
/**
* 账户类,包含余额成员变量
*
*/
class Account {
private double balance;
private boolean flag = false;
public Account(double balance) {
super();
this.balance = balance;
}
// 取款
public synchronized void draw(double drawAmount) {
try {
if (!flag) {
this.wait();
} else {
balance -= drawAmount;
System.out.println(Thread.currentThread().getName() + "取现" + drawAmount + ",账户余额为:" + balance);
flag = false;
this.notifyAll();
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 存款
public synchronized void deposit(double depositAmount) {
try {
if (flag) {
this.wait();
} else {
balance += depositAmount;
System.out.println(Thread.currentThread().getName() + "存款" + depositAmount + ",账户余额为:" + balance);
flag = true;
this.notifyAll();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class DrawThread extends Thread {
private Account account;
private double drawAmount;
public DrawThread(String name, Account account, double drawAmount) {
super(name);
this.account = account;
this.drawAmount = drawAmount;
}
@Override
public void run() {
while (true) {
account.draw(drawAmount);
}
}
}
// 存款
class DepositThread extends Thread {
private Account account;
private double depositAmount;
public DepositThread(String name, Account account, double depositAmount) {
super(name);
this.account = account;
this.depositAmount = depositAmount;
}
@Override
public void run() {
while (true) {
account.deposit(depositAmount);
}
}
}
public class ThreadTest {
public static void main(String[] args) {
Account acct = new Account(0);
for (int i = 1; i <= 2; i++) {
new DrawThread("取款者" + i, acct, 500).start();
new DepositThread("存款者" + i, acct, 500).start();
}
}
}
相关文章推荐
- 一张图了解所有USB接口和线序
- 算法的时间复杂度计算
- 解决struts2过滤器冲突的简单方法
- ZOJ 1002 FZU 1098 HDU 1045 Fire Net
- 【sqlserver】记录
- 分享:面相对象的建模(封装业务层)
- dll劫持技术
- redis学习-5种数据类型和相关命令
- 联合体的巧用
- 利用反射机制访问类中的私有方法
- 多个线程之间共享数据的方式(卖票问题,存取款问题)
- C#实现按数据库邮件列表发送邮件的方法
- 友盟集成
- CSS中使用Flexbox来达到居中效果的实例
- Log4j日志级别
- 学习c++的五十条忠告
- iOS中 imageNamed方法 很多图片占用大量内存问题
- MySQL Cluster Windows 安装
- asp.net调用JAVA的webservice时注意
- OpenHCI - 4.2 Endpoint Descriptor