同步机制
多线程开发过程中,我们经常会提到同步这个词,我们知道一个多线程应用系统在操作系统的进程(线程)机制下可以同时有多个进程(线程)并发运行,这此进程(线程)要完成的任务可能是互不相关的,但也可能是有联系的。那么当一个进程(线程)要和另一个进程(线程)交流信息时同步就有可能发生了。
JAVA同步机制
JAVA中cpu分给每个线程的时间片是随机的并且在java中好多都是多个线程共用一个资源,JAVA为了实现同步机制提供了synchronized关键字,我们可以使用它来定义被同步的对象以及临界区,临界区的范围是由一组大括号来标识的。而进出临界区时必要的加锁和解锁操作则是由Java内置支持的。
接下来,我们举一个简单的火车购票的例子窥探一下JAVA中synchronized关键字的作用。在这个例子中,火车站有3个窗口售票,总共的票数有10张。
为了方便对比,我们先看一下没有使用synchronized关键字时的效果
这是窗口线程:
package com.test;
public class TicketSource implements Runnable
{
//票的总数
private int ticket=10;
public void run()
{
for(int i=1;i<50;i++)
{
if(ticket>0)
{
//休眠1s秒中,为了使效果更明显,否则可能出不了效果
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"号窗口卖出"+this.ticket--+"号票");
}
}
}
}
这是测试类:
package com.test;
public class Test {
public static void main(String args[])
{
TicketSource mt=new TicketSource();
//基于火车票创建三个窗口
new Thread(mt,"a").start();
new Thread(mt,"b").start();
new Thread(mt,"c").start();
}
}
我们来看一下输出:
我们可以看到a号窗口和和b号窗口都卖出了4号票,并且c号窗口卖出了0号。
造成这种情况的原因是:
(1)a线程和b线程在ticket=4的时候,a线程取出4号票以后,ticket还没来的及减1,b线程就取出了ticket,此时ticket还等于4。
(2)在ticket=1时,a线程取出了1号票,ticket还没来的及减1,c线程就进入了if判断语句,这时回到了a线程中,ticket减1了,那么当c线程取票的时候就取到了0号票。
那么,当出现了这样的情况,我们可以这样做:当一个线程要使用火车票这个资源时,我们就交给它一把锁,等它把事情做完后在把锁给另一个要用这个资源的线程。这样就不会出现上述情况。
实现这个锁的功能就需要用到synchronized这个关键字。
synchronized这个关键字有两种用法:
(1)放方法名前形成同步方法
(2)放在块前构成同步块
1.使用同步方法
新的窗口线程:
package com.test;
public class TicketSource2 implements Runnable
{
//票的总数
private int ticket=10;
public void run()
{
for(int i=1;i<50;i++)
{
try {
//休眠1s秒中,为了使效果更明显,否则可能出不了效果
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.sale();
}
}
public synchronized void sale()
{
if(ticket>0)
{
System.out.println(Thread.currentThread().getName()+"号窗口卖出"+this.ticket--+"号票");
}
}
}
同样的测试类,我们看一下输出:
这样就符合情况了!
2.使用同步块
新的窗口线程:
package com.test;
public class TicketSource3 implements Runnable
{
//票的总数
private int ticket=10;
public void run()
{
for(int i=1;i<50;i++)
{
synchronized(this){
if(ticket>0)
{
//休眠1s秒中,为了使效果更明显,否则可能出不了效果
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"号窗口卖出"+this.ticket--+"号票");
}
}
}
}
}
同样的测试类,我们看一下输出:
这样就符合情况了!
在下一篇文章中,笔者会提供一些JAVA同步中更为细节的问题分享给大家。