本来想写一个程序,模拟12306抢票,但是最后发现Synchronized锁不住Integer,记录一下。
public class TicketConsumer implements Runnable{
private Integer i;
public TicketConsumer(int i) {
super();
this.i = i;
}
@Override
public void run() {
while(true) {
synchronized (i) {
if(i>0) {
try {
Thread.sleep(100); // 模拟抢票延迟
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到了第"+i--+"张票");
}else {
return;
}
}
}
}
}
public class Test1 {
public static void main(String[] args) {
TicketConsumer ticket = new TicketConsumer(new Integer(10));
Thread t1 = new Thread(ticket,"张三");
Thread t2 = new Thread(ticket,"李四");
t1.start();
t2.start();
}
}
大眼一瞟,似乎没问题,但是运行就出了问题,两人抢到了同一张票
运行结果 |
---|
张三抢到了第10张票 李四抢到了第9张票 张三抢到了第9张票 李四抢到了第8张票 李四抢到了第7张票 张三抢到了第6张票 李四抢到了第5张票 张三抢到了第4张票 李四抢到了第4张票 张三抢到了第3张票 张三抢到了第2张票 李四抢到了第1张票 |
原因就是,Integer取值如果是在[-128,127]之间,系统会从常量池中取值,如果超出这个范围,系统会自动new一个新对象,所以每次对象都不一样
Integer i = 1;
System.out.println(System.identityHashCode(i));
System.out.println(System.identityHashCode(++i));
StringBuilder sb = new StringBuilder("张三");
System.out.println(System.identityHashCode(sb));
sb.append("李四");
System.out.println(System.identityHashCode(sb));
运行结果 |
---|
366712642 1829164700 2018699554 2018699554 |
Integer在自增后,内存地址就变了,而StringBuilder却不会变,一旦变化Synchronized每次锁的就不是同一个对象,所以锁不住。
解决办法:
可以把Integer单独放到一个类中,Synchronized这个类的对象,就可以了
public class Ticket {
int count;
public Ticket(int count) {
super();
this.count = count;
}
}
public class TicketConsumer implements Runnable{
private Ticket ticket;
public TicketConsumer(Ticket ticket) {
super();
this.ticket = ticket;
}
@Override
public void run() {
while(true) {
try {
Thread.sleep(100); // 模拟抢票延迟
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (ticket) {
if(ticket.count>0) {
System.out.println(Thread.currentThread().getName()+"抢到了第"+ticket.count--+"张票");
}else {
return;
}
}
}
}
}
public class Test1 {
public static void main(String[] args) {
TicketConsumer ticket = new TicketConsumer(new Ticket(10));
Thread t1 = new Thread(ticket,"张三");
Thread t2 = new Thread(ticket,"李四");
t1.start();
t2.start();
}
}
运行结果 |
---|
李四抢到了第10张票 张三抢到了第9张票 张三抢到了第8张票 李四抢到了第7张票 张三抢到了第6张票 李四抢到了第5张票 李四抢到了第4张票 张三抢到了第3张票 张三抢到了第2张票 李四抢到了第1张票 |
O了…