线程同步

       在单线程程序中,每次只能做一件事情,后面的事情需要等待前面的事情完成后才可以进行,但是如果使用多线程程序,就会发生两个线程抢占资源的问题,如两个人同时说话.两个人同时过同一个独木桥等。所以在多线程编程中需要防止这些资源访问的冲突。Java提供了线程同步的机制来防止资源访问的冲突。


一.线程安全

实际开发中,使用多线程程序的情况很多,如银行排号系统.火车站售票系统等。这种多线程的程序通常会发生问题,以火车站售票系统为例,在代码中判断当前票数是否大于0,如果大于0则执行将该票出售给乘客的功能,但当两个线程同时访问这段代码时(假如这时只剩下一张票),第一个线程将票售出,与此同时第二个线程也已经执行完成判断是否有票的操作,并得出结论票数大于0,于是它也执行售出操作,这样就会产生负数。所以在编写多线程线程时,应该考虑到线程安全问题。实质上线程安全问题来源于两个线程同时存取单一对象的数据。


下面看个例子:该类实现了Runnable接口,主要实现模拟火车站售票系统的功能:
package kop;
public   class op implements Runnable{
int num=10;
public void run(){
while(true){
if(num>0){
try{
Thread.sleep(100);
}catch(Exception e){
e.printStackTrace();
}
System.out.println("tickets"+num--);
}
}
}
public static void main(String[] args){
op t=new op();
Thread tA=new Thread(t);
Thread tB=new Thread(t);
Thread tC=new Thread(t);
Thread tD=new Thread(t);
tA.start();
tB.start();
tC.start();
tD.start();
}
}

运行后:

tickets10
tickets9
tickets8
tickets7
tickets6
tickets5
tickets4
tickets3
tickets2
tickets1
tickets0
tickets-1
tickets-2


从中可以看出打印剩下的票为负值,这样就出现了问题,这是由于同时创建了4个线程,这4个线程执行run()方法,在num变量为1时,线程1,线程2,线程3,线程4都对num变量有存进入就绪储功能,当线程1执行run()方法时,还没有来得及做递减操作,就指定它调用sleep()方法进入就绪状态,这时线程2,线程3和线程4都进入了run()方法,发现num变量依然大于0,但此时线程1休眠时间已到,将num变量值递减,同时线程2,线程3,线程4也都对num变量进行递减操作,从而产生了负值。


二.线程同步机制

那么该如何解决资源共享的问题呢?基本上所有解决多线程资源冲突问题的方法都是采用给定时间只允许一个线程访问共享资源,这时就需要给共享资源上一道锁。这就好比一个人上洗手间时,他进入洗手间后会将门锁上,出来时再将锁打开,然后其他人才可以进入

1.同步块

在Java中提供了同步机制,可以有效地防止资源冲突。同步机制使用synchronized关键字。

将上述代码改为:
package kop;
public   class op implements Runnable{
int num=10;
public void run(){
while(true){
synchronized(""){
if(num>0){
try{
Thread.sleep(100);
}catch(Exception e){
e.printStackTrace();
}
System.out.println("tickets"+num--);
}
}
}
}
public static void main(String[] args){
op t=new op();
Thread tA=new Thread(t);
Thread tB=new Thread(t);
Thread tC=new Thread(t);
Thread tD=new Thread(t);
tA.start();
tB.start();
tC.start();
tD.start();
}
}

结果:

tickets10
tickets9
tickets8
tickets7
tickets6
tickets5
tickets4
tickets3
tickets2
tickets1

打印到之后票数没有出现负数,这是因为将资源放置在了同步块中。这个同步块也被称为临界区,它使用synchronized关键字建立,其语法如下:

synchronized(Object){

}

通常将共享资源的操作放置在synchronized定义的区域内,这样当其他线程也获取到这个锁时,必须等待锁被释放时才能进入该区域。Object为任意一个对象,每个对象都存在一个标志位,并具有两个值,分别为0和1.一个线程运行到同步块时首先检查该对象的标志位,如果为0状态,表明此同步块中存在其他线程在运行。这时该线程处于就绪状态,直到位于同步块中线程执行完同步块中的代码。这时该对象的标识位被设置为1,该线程才能执行同步块中的代码,并将Object对象的标识位设置为0,防止其他线程执行同步块中的代码。


2.同步方法

同步方法就是在方法前面修饰synchronized关键字的方法,其语法如下:
synchronized  void  f(){}

当某个对象调用了同步方法时,该对象上的其他同步方法必须等待该同步方法执行完毕后才能被执行。必须将每个能访问共享资源的方法修饰为synchronized,否则就会出错。


package kop;
public   class op implements Runnable{
int num=10;
public synchronized void doit(){
if(num>0){
try{
Thread.sleep(100);
}catch(Exception e){
e.printStackTrace();
}
System.out.println("tickets"+num--);
}
}
public void run(){
while(true){
doit();
}
}
public static void main(String[] args){
op t=new op();
Thread tA=new Thread(t);
Thread tB=new Thread(t);
Thread tC=new Thread(t);
Thread tD=new Thread(t);
tA.start();
tB.start();
tC.start();
tD.start();
}
}

将共享资源的操作放置在同步方法中,运行结果与使用同步块的结果一致。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值