本篇重点:多线程共享资源时发生的互斥问题
一般的我们售卖电影票或者火车票时会有多个窗口同时买票,
我们来看测试代码:主方法new一个Ticket(一个堆),之后三个线程来启动(三个窗口买票)
class Ticket implements Runnable{
private static int ticket=10;
@Override
public void run() {
for(int i=1;i<=100;i++){
System.out.println(Thread.currentThread().getName()+” – 开始买票”);
synchronized(this){ //同步代码块+对象锁
System.out.println(Thread.currentThread().getName()+” – 买了第”+ticket+”张票”);
ticket–;
}
System.out.println(Thread.currentThread().getName()+” – 结束买票”);
if(ticket<=0){break;}
}
}
}
public class Demo {
public static void main(String[] args) {
Ticket ticket=new Ticket();
new Thread(ticket,”1号窗口”).start();
new Thread(ticket,”2号窗口”).start();
new Thread(ticket,”3号窗口”).start();
}
}
同步块内的代码是原子性的,在没有执行完所有语句时是不会出让CPU的。
在分析以上代码前,我们先简化模型。
class Ticket implements Runnable{
@Override
public void run() {
for(int i=1;i<=5;i++){
System.out.println(Thread.currentThread().getName()+” – A”);
System.out.println(Thread.currentThread().getName()+” – B”);
System.out.println(Thread.currentThread().getName()+” – C”);
}
}
}
public class Demo {
public static void main(String[] args) {
Ticket ticket=new Ticket();
new Thread(ticket,”t1″).start();
new Thread(ticket,”t2″).start();
new Thread(ticket,”t3″).start();
}
}
运行如图:
t1 – A
t2 – A
t3 – A
t3 – B
t1 – B
t3 – C
t2 – B
t2 – C
t3 – A
t1 – C
t1 – A
t1 – B
t1 – C
t3 – B
t3 – C
t3 – A
t2 – A
t3 – B
t1 – A
t3 – C
t2 – B
t3 – A
t1 – B
t3 – B
t2 – C
t3 – C
t1 – C
t3 – A
t2 – A
t2 – B
t2 – C
t2 – A
t2 – B
t2 – C
t2 – A
t2 – B
t3 – B
t1 – A
t3 – C
t2 – C
t1 – B
t1 – C
t1 – A
t1 – B
t1 – C
每次运行结果都会不一样,因为轮流抢占CPU不是我们能控制的。
图解分析:
由分析我们得出大概是以一条语句作为基本单位来执行,若多条语句需要作为一个原子性的整理,就需要加互斥锁。
原理大致如图:
加锁的这段区域被称为“互斥区”,里面的代码必须整理执行完毕才会释放锁,让其他线程切入进来。
synchronized具有加锁的功能,实现比较简单。
我们再看卖票的代码:
class Ticket implements Runnable{
private static int ticket=10;
@Override
public void run() {
for(int i=1;i<=100;i++){
try {
Thread.sleep(500); //线程休眠500毫秒,以便观察输出
} catch (InterruptedException e) { //需要处理异常
e.printStackTrace();
}
synchronized(this){ //同步代码块+对象锁(this表示对象锁)
if(ticket<=0){break;}
System.out.println(Thread.currentThread().getName()+” 买了第”+ticket+”张票”);
ticket–;
}
}
}
}
public class Demo {
public static void main(String[] args) {
Ticket ticket=new Ticket();
new Thread(ticket,”1号窗口”).start();
new Thread(ticket,”2号窗口”).start();
new Thread(ticket,”3号窗口”).start();
}
}
运行如图: