Java多线程04_线程同步问题
关键字 synchronized 可以保证在同一时刻,只有一个线程可以执行某个方法或某个代码块
案例1:不安全的买票 (多个线程访问同一个对象的同一个方法):
public class ByTicket {
public static void main(String[] args) {
BuyThread buyThread = new BuyThread();
new Thread(buyThread,"zhangsan").start();
new Thread(buyThread,"lisi").start();
new Thread(buyThread,"wangwu").start();
}
}
class BuyThread implements Runnable{
private int ticketNums = 10;
boolean flag = true;
@Override
public void run() {
while(flag) {
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void buy() throws InterruptedException {
if(ticketNums<=0) {
flag = false;
return;
}
Thread.sleep(200);
System.out.println(Thread.currentThread().getName()+"买到了"+ ticketNums--);
}
}
wangwu买到了10
zhangsan买到了8
lisi买到了9
lisi买到了7
wangwu买到了7
zhangsan买到了7
zhangsan买到了6
lisi买到了4
wangwu买到了5
lisi买到了3
wangwu买到了3
zhangsan买到了2
wangwu买到了1
lisi买到了0
zhangsan买到了-1
解决办法:在 buy() 方法上增加 synchronized 修饰,使其成为同步方法
private synchronized void buy() throws InterruptedException {
}
zhangsan买到了10
zhangsan买到了9
zhangsan买到了8
wangwu买到了7
wangwu买到了6
wangwu买到了5
lisi买到了4
lisi买到了3
lisi买到了2
lisi买到了1
案例2:不安全的取钱(多个线程操作同一个对象account):
public class Bank {
public static void main(String[] args) {
Account account = new Account(100,"存款");
DrawMoney blu = new DrawMoney(account,20,"BLU");
DrawMoney gf = new DrawMoney(account,100,"gf");
blu.start();
gf.start();
}
}
class Account{
int money;
String name;
public Account(int money, String name) {
super();
this.money = money;
this.name = name;
}
}
class DrawMoney extends Thread{
Account account;
int draw;
int nowmoney;
public DrawMoney(Account account, int draw, String name) {
super(name);
this.account = account;
this.draw = draw;
this.nowmoney = nowmoney;
}
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(account.money-draw<0) {
System.out.println(Thread.currentThread().getName()+"账户余额不足");
return;
}
account.money = account.money - draw;
nowmoney = nowmoney + draw;
System.out.println(account.name+"余额为:"+account.money);
System.out.println(Thread.currentThread().getName()+"手里的钱:"+nowmoney);
}
}
存款余额为:80
gf手里的钱:100
存款余额为:80
BLU手里的钱:20
解决办法:使用synchronized (account) {} 对给定对象account加锁,进入同步代码块前要获得给定对象的锁
@Override
public void run() {
synchronized (account) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(account.money-draw<0) {
System.out.println(Thread.currentThread().getName()+"账户余额不足");
return;
}
account.money = account.money - draw;
nowmoney = nowmoney + draw;
System.out.println(account.name+"余额为:"+account.money);
System.out.println(Thread.currentThread().getName()+"手里的钱:"+nowmoney);
}
}
存款余额为:80
BLU手里的钱:20
gf账户余额不足
案例3:不安全的集合(原因是两个线程可能会在同一瞬间操作了同一位置):
public class UnsafeList {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
for(int i=0;i<10000;i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
System.out.println(list.size());
}
}
9998
解决办法:锁住 list 对象
import java.util.ArrayList;
import java.util.List;
public class UnsafeList {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
for(int i=0;i<10000;i++) {
new Thread(()->{
synchronized (list) {
list.add(Thread.currentThread().getName());
}
}).start();
}
System.out.println(list.size());
}
}
10000