java多线程㈡—线程安全问题(同步代码块or同步函数)

摘要:上一篇文章,写了实现java多线程的方式,以售票为示例来说明,可是我们看到和我们实际有很大的问题,这一骗文章主要用同步块和同步方法,解决售票问题,以实现Runnable接口为例。


为什么要用同步块或同步方法?

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

 

同步代码块的格式:

synchronized(对象)
{
         需要被同步的代码 ;
}

同步函数的格式:

Synchronized 返回值类型 方法名([参数1,……]){}

同步代码块实现同步原理:

同步代码块中传入的对象是一个锁对象。当线程执行同步代码块时,首先线程会检查对象的标志位,默认情况下标志位为1,此时线程会执行同步代码块,同时锁对象的标志位置为0.当一个新线程执行到这段同步代码块时,有哟锁对象的标志位为0,新线程会发生阻塞,等待当前线程执行完同步代码块后,锁对象的标志位被置为1,新线程才能进入同步代码块执行其中的代码。循环往复,直到共享资源被处理完为止。

class Ticket implements Runnable{
	private  int num = 10;
	Object lock = new Object();
	public void run(){
		
		while(true){	
			synchronized(lock){			
				try{
					Thread.sleep(10);
				}catch(InterruptedException e){
					e.printStackTrace();
				}
				if(num>0){
					System.out.println(Thread.currentThread().getName()+"....."+num--);
				}else{
					break;//小于等于0跳出循环
				}
				
			}
		}	
	}
}

class SynDemo1{
	public static void main(String[] args){
		Ticket t = new Ticket();
		new Thread(t).start();
		new Thread(t).start();
		new Thread(t).start();
		new Thread(t).start();
	}
}
运行结果:


注意:同步代码块中的锁对象可以是任意类型的对象,但多个线程共享的锁对象必须是唯一的,”任意”说的是共享锁对象的类型。所以锁对象的创建代码不能放到run()方法中,否则每个线程运行到run()方法都会创建一个新对象,这每个线程都会有一个不同的锁,每个锁都有自己的标志位。线程直接便不能产生同步效果。


同步方法实现同步原理:

同步方法实现原理和同步代码块实现原理相同,所不同的是,锁对象。

同步方法的锁是当前调用该方法的对象,也就是this指向的对象。这样做的好处是,同步方法被所有线程所共享,方法所在的对象相对于所有线程来时是唯一的,从而保证了锁的唯一性,当一个线程执行该方法时,其他线程就不能进去改方法中,直到这个线程执行完该方法为止,从而达到了线程同步的效果。

class Ticket implements Runnable{
	private  int num = 10;

	public void run(){	
		while(true){
			saleTicket();
			try{
				Thread.sleep(10);
			}catch(InterruptedException e){
				e.printStackTrace();
			}
			if(num<=0)
			break;
		}	
	}
	
	private synchronized void saleTicket(){
		if(num>0)
		System.out.println(Thread.currentThread().getName()+"....."+num--);
	}
}

class SynDemo2{
	public static void main(String[] args){
		Ticket t = new Ticket();
		new Thread(t).start();
		new Thread(t).start();
		new Thread(t).start();
		new Thread(t).start();
	}
}

运行结果:


有时候需要同步的方法是静态方法,静态方法不需要创建对象就可以直接用“类名.方法名()”的方式调用。这时java中静态方法的锁是改方法所在类的class对象,获取方式用getClass()或“类名.class”。

class Ticket implements Runnable{
	private static  int num = 10;

	boolean flag = true;
	public void run(){
		if(flag)
			while(true)
			{
				synchronized(Ticket.class){//(this.getClass())
					if(num>0){
						try{
							Thread.sleep(10);
						}catch (InterruptedException e){
							e.printStackTrace();
						}						
						System.out.println(Thread.currentThread().getName()+".....obj...."+num--);
					}
				}
			}
		else
			while(true)
				this.show();
	}

	public static synchronized void show(){
		if(num>0){
			try{
				Thread.sleep(10);
			}catch (InterruptedException e){
				e.printStackTrace();
			}				
			System.out.println(Thread.currentThread().getName()+".....function...."+num--);
		}
	}
}

class SynDemo3 {
	public static void main(String[] args) {
		Ticket t = new Ticket();
		new Thread(t).start();
		try{
			Thread.sleep(10);
		}catch (InterruptedException e){
			e.printStackTrace();
		}	
		t.flag = false;
		new Thread(t).start();
	}
}

运行结果:





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值