示例1:卖票与买票
@Slf4j(topic = "c.CASE1")
public class CASE1 {
public static Random random = new Random();
public static int getRandom(){
return random.nextInt(5) + 1;
}
public static void main(String[] args) {
TicketWindow ticketWindow = new TicketWindow(2000);
List<Thread> list = new ArrayList<>();
List<Integer> sellCount = new Vector<>();
for (int i = 0; i < 2000; i++) {
Thread t = new Thread(()->{
int sell = ticketWindow.sell(getRandom());
sellCount.add(sell);
});
list.add(t);
t.start();
}
list.forEach((t)->{
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
log.debug("卖出去:" + sellCount.stream().mapToInt(a->a).sum());
log.debug("剩余票数:" + ticketWindow.getCount());
}
}
class TicketWindow{
private int count;
public TicketWindow(int count) {
this.count = count;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public int sell(int amount){
if(amount < count){
count = count - amount;
return amount;
}else{
return 0;
}
}
}
请判断该例子是否存在线程安全?
运行输出结果:
答:存在!
分析:判断是否存在线程安全步骤
解决方法:
可以给共享变量上锁,使用关键字synchronized,可以将锁加在sell方法上
public synchronized int sell(int amount){
if(amount < count){
count = count - amount;
return amount;
}else{
return 0;
}
}
等价于
public int sell(int amount){
synchronized(this){
if(amount < count){
count = count - amount;
return amount;
}else{
return 0;
}
}
}
示例2:ab互相转账
@Slf4j(topic = "c.CASE2")
public class CASE2 {
private static Random random = new Random();
public static int getRandom(){
return random.nextInt(100) + 1;
}
public static void main(String[] args) throws InterruptedException {
Account a = new Account(1000);
Account b = new Account(1000);
Thread t1 = new Thread(()->{
for (int i = 0; i < 1000; i++) {
a.transfer(b,getRandom());
}
});
Thread t2 = new Thread(()->{
for (int i = 0; i < 1000; i++) {
b.transfer(a,getRandom());
}
});
t1.start();
t2.start();
t1.join();
t2.join();
log.debug("账户a :" + a.getMoney() );
log.debug("账户b :" + b.getMoney());
log.debug("total:{}",(a.getMoney() +b.getMoney()));
}
}
class Account{
private int money;
public Account(int money) {
this.money = money;
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
public void transfer(Account target,int amount){
if(this.money > amount){
this.setMoney(this.getMoney() - amount);
target.setMoney(target.getMoney() + amount);
}
}
}
-
临界区
public void transfer(Account target,int amount){
synchronized (Account.class){
if(this.money > amount){
this.setMoney(this.getMoney() - amount);
target.setMoney(target.getMoney() + amount);
}
}
}
-
共享变量
与上一个例子不同的是,该临界区存在的共享变量有两个,分别是this.money和target.money,所以如果在transfer上面加上锁,还能不能锁得住,能 不能解决线程安全问题呢?
public synchronized void transfer(Account target,int amount){
if(this.money > amount){
this.setMoney(this.getMoney() - amount);
target.setMoney(target.getMoney() + amount);
}
}
并不能!
原因是:此时的synchronized只能锁定住当前的对象的共享变量,也就是说谁调用transfer就会锁住谁的money属性,因此并不能解决线程安全问题,那么该如果才能同时锁住两个对象呢?
解决方法:扩大锁的范围,从锁住对象扩大至锁住整个类,因为无论是this.money和target.money它们都属于Account类,因此可以修改临界区的锁范围,如下:
public void transfer(Account target,int amount){
synchronized (Account.class){
if(this.money > amount){
this.setMoney(this.getMoney() - amount);
target.setMoney(target.getMoney() + amount);
}
}
}
重新运行代码:
此时便达到我们的线程安全目的。