首先想到线程同步就会想到线程异步
- 所谓同步:就是一段时间内只做一件事,等这件事做完之后再去做另一件事
- 所谓异步:就是一段时间内可以并发的做多件事
我们可以拿生活中的事来解释:比如我们做饭(不仅仅只是煲饭这一个线程,还包括做菜),同步就是当我们把饭放在电饭锅里插上电,然后我们不用做任何事情,等饭堡熟了,我们再洗菜炒菜;异步就是但我们把饭堡在锅里之后,饭还在煮的同时我们可以洗菜,炒菜。生活中有好多例子可以来解释某些专业名称,这样更易于理解,我们这里主要来讲将线程的同步
在大多数需要运行多线程的应用程序中,两个或多个线程需要共享对同一个数据的访问,如果每个线程都会调用一个修改该数据的方法,那么这些线程将会相互影响对方的行为,为了避免这种情况的发生,那么同步机制就产生了
举一个示例来说明这个问题,有一奥运门票销售系统,它有5个销售点,共通销售100张奥运门票,用多线程来模拟这个销售系统的代码如下:
public class TicketOfficeTest {
public static void main(String[] args) {
TicketOffice off=new TicketOffice();
Thread t1=new Thread(off);
t1.setName("销售点1");
t1.start();
Thread t2=new Thread(off);
t2.setName("销售点2");
t2.start();
Thread t3=new Thread(off);
t3.setName("销售点3");
t3.start();
Thread t4=new Thread(off);
t4.setName("销售点4");
t4.start();
Thread t5=new Thread(off);
t5.setName("销售点5");
t5.start();
}
}
class TicketOffice implements Runnable{
private int tickets=0;
public void run() {
boolean flag=true;
while(flag) {
flag=sell();
}
}
public boolean sell() {
boolean flag=true;
if(tickets<100) {
tickets=tickets+1;
try {
Thread.sleep(105);
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+"卖出第"+tickets+"张票");
}else {
flag=false;
}
return flag;
}
}
其结果为:
销售点4卖出第5张票
销售点3卖出第5张票
销售点5卖出第5张票
销售点1卖出第5张票
销售点2卖出第5张票
销售点2卖出第10张票
销售点4卖出第10张票
销售点3卖出第10张票
销售点5卖出第10张票
销售点1卖出第10张票
销售点2卖出第15张票
销售点4卖出第15张票
...
观察这个结果,发现不同销售点会重复售出同一张门票,这个显然不符合要求
所以为了解决这种问题,我们用同步机制,能保证当多个线程同时访问临界资源时,一个线程在执行的时候不会被打断,通过用关键字synchronized来加保护伞,synchronized主要用于应用于同步代码块和同步方法中
- 同步方法:synchronized放在方法声明中,表示整个方法为同步方法
public synchronized boolean sell() {
boolean flag=true;
if(tickets<100) {
tickets=tickets+1;
try {
Thread.sleep(105);
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+"卖出第"+tickets+"张票");
}else {
flag=false;
}
return flag;
}
其结果为:
销售点2卖出第1张票
销售点2卖出第2张票
销售点2卖出第3张票
销售点2卖出第4张票
销售点5卖出第5张票
销售点5卖出第6张票
销售点1卖出第7张票
销售点1卖出第8张票
销售点1卖出第9张票
销售点1卖出第10张票
...
- 同步代码块:把线程体内的执行的方法中会操作到共享数据的语句封装在{}之内,然后用synchronized放在某个对象的前面修饰这个代码块
public boolean sell() {
boolean flag=true;
synchronized (this) {
if(tickets<100) {
tickets=tickets+1;
try {
Thread.sleep(105);
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+"卖出第"+tickets+"张票");
}else {
flag=false;
}
}
return flag;
}
其结果和同步方法一样,只是同步了会操作到共享数据的代码,这种方法比同步整个方法会更有效