多线程安全问题

在上一个卖票实例(买票案例)中,我们在Ticket类中定义了run()方法:

public void run()  
    {  
        while(true)  
        {  
            if(num>0){   
                System.out.println(Thread.currentThread().getName()+"卖"+num--+"号票");  
            }  
        }  
    }  

为了清楚的展示该实例存在安全问题,我们在其中加入能让线程进行切换的代码:

public void run()  
    {  
        while(true)  
        {  
            if(num>0){   
                 //sleep(long millis):在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
		  Thread.sleep(10);                
		  System.out.println(Thread.currentThread().getName()+"卖"+num--+"号票");  
            }  
        }  
    }  

注意:sleep(long millis)方法声明了一个异常InterruptedException(中断异常),所以我们在run()方法中用到这个方法时应在run()里声明或抛出,但是run()方法是覆盖的Runnnable接口中的run()方法,而接口是没有没有声明过异常的,所以覆盖时也不能声明异常,此时只能try-catch。

 
public void run()  
    {  
        while(true)  
        {  
            if(num>0){   
		try
		{
			Thread.sleep(10);
		 }
		catch(InterruptedException e)
		{
			//此时先不做处理
		}
		System.out.println(Thread.currentThread().getName()+"卖"+num--+"号票");  
            }  
        }  
    }

运行如下代码:

class Ticket implements Runnable{
	private int num=100;  //共有100张票
	public void run()
	{
		while(true)
		{
			if(num>0){
				try{Thread.sleep(10); }
				catch(InterruptedException e){}
				System.out.println(Thread.currentThread().getName()+"卖"+num--+"号票");
			}
		}
	}
}
public class Demo {	
	public static void main(String[] args) {
		Ticket t=new Ticket(); //创建一个线程任务对象

		//创建线程
		Thread t1=new Thread(t);  //任务对线程进行初始化
		Thread t2=new Thread(t);
		Thread t3=new Thread(t);
		Thread t4=new Thread(t);

		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}
运行结果(部分):



可以看到,结果中出现了0号、-1号、-2号票,而现实中根本不能存在这样的票号,这就出现了线程中的安全问题。


线程安全问题产生的原因:

1.多个线程在操作共享的数据

2.操作共享数据的线程代码有多条

所以,当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会导致线程安全问题的产生。


为了避免线程安全问题的产生,我们要解决可能发生的隐患,解决思想如下:

就是将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程是不可以参与运算的,必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算。

在java中,用同步代码块来解决这个问题,同步代码块的格式:

synchronized(对象)   //这里的对象可以随便创建,一般我们可以创建Object类的对象
{
	//需要被同步的代码
}
将同步代码块添加到run()方法中,运行如下代码:

class Ticket implements Runnable{
	private int num=100;  //共有100张票
	Object obj=new Object();

	public void run()
	{
		while(true)
		{
			synchronized(obj)  //同步
			{
				if(num>0){
					try{Thread.sleep(10); }	
					catch(InterruptedException e){}
					System.out.println(Thread.currentThread().getName()+"卖"+num--+"号票");
					}
			}
		}
	}
}
public class Demo {	
	public static void main(String[] args) {
		Ticket t=new Ticket(); //创建一个线程任务对象

		//创建线程
		Thread t1=new Thread(t);  //任务对线程进行初始化
		Thread t2=new Thread(t);
		Thread t3=new Thread(t);
		Thread t4=new Thread(t);

		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

运行结果(部分):



可以发现同步解决了线程的安全问题。

 

引申:

同步的弊端:相对降低了效率,因为同步外的线程都会判断同步锁。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值