Java中的线程同步及线程死锁复习

1 线程安全

在单线程程序中,每次只能做一件事情,后面的事情需要等待前面的事情完成后才可以进行,但是如果使用多线程程序,就会发生多个线程抢占资源的问题,从而产生线程安全问题。实质上线程安全问题来源于多个线程在操作共享的数据,并且操作共享数据的代码有多条,即当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算。解决思路:将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程是不可以参与运算的,必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算。Java提供了线程同步的机制来防止资源访问的冲突,同步机制使用synchronized关键字。同步的好处:解决了线程安全问题。同步的弊端:相对地降低了效率,因为同步外的线程都会判断同步锁。

2 同步块

同步块也被称为临界区,它使用synchronized关键字建立,语法格式为:synchronized(object){需要被同步的代码;}。通常是将共享资源的操作代码放置在synchronized定义的区域内,这样当其他线程也获取到这个锁时,必须等待锁被释放时才能进入该区域。object为任意一个对象,每个对象都存在一个标志位,并具有两个值,分别为0和1。一个线程运行到同步块时,首先检查该对象的标志位,如果为0状态,表明此同步块中存在其他线程在运行。这时该线程处于就绪状态,直到处于同步块中的线程执行完同步块中的代码为止。这时该对象的标志位被设置为1,该线程才能执行同步块中的代码,并将object对象的标志位设置为0,防止其他线程执行同步块中的代码。
同步块程序示例:
package captain;

public class ThreadSafeDemo implements Runnable{
	int num = 10;
	Object obj = new Object();//同步锁对象obj必须作为类的成员,才能保证锁的唯一性。
	public void run(){
		//Object obj = new Object();//假如在run()方法内创建同步锁对象obj,则并不能保证线程同步。
		while(true){
			
			//同步块
			synchronized(obj){//obj对象为同步锁
				if(num > 0){
					/*
					 * 线程休眠会抛出中断异常,但由于Runnable接口中的run()方法没有声明抛出该异常,
					 * 故实现接口的类中的run()方法也不能声明抛出该异常,因此只能在方法内try-catch捕捉异常。
					 * */
					try{
						Thread.sleep(1000);
					}
					catch(InterruptedException e){
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+"..."+num--);
				}
			}
			
		}
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ThreadSafeDemo tsd = new ThreadSafeDemo();
		Thread t1 = new Thread(tsd);
		Thread t2 = new Thread(tsd);
		Thread t3 = new Thread(tsd);
		Thread t4 = new Thread(tsd);
		t1.start();
		t2.start();
		t3.start();
		t4.start();

	}

}

3 同步方法

同步方法就是在方法前面修饰synchronized关键字的方法。当某个对象调用了同步方法时,该对象上的其他同步方法必须等待该同步方法执行完毕后才能被执行。必须将每个能访问共享资源的方法修饰为synchronized,否则就会出错。同步方法,与同步块的效果一样,不同的是同步块的锁是任意一个对象,而同步方法的锁其实是this。如果是静态同步方法,则其锁是方法所属类的字节码对象this.getClass()或者用类的静态属性 类名.class 来获取。
同步方法程序示例:
package captain;

public class ThreadSafeDemo2 implements Runnable{
	int num = 10;
	
	/*同步方法,与同步块的效果一样,不同的是同步块的锁是任意一个对象,而同步方法的锁其实是this。
	 *如果是静态同步方法,则其锁是方法所属类的字节码对象this.getClass()或者用类的静态属性 类名.class 来获取。
	 * */
	public synchronized void doit(){
		if(num > 0){
			/*
			 * 线程休眠会抛出中断异常,但由于Runnable接口中的run()方法没有声明抛出该异常,
			 * 故实现接口的类中的run()方法也不能声明抛出该异常,因此只能在方法内try-catch捕捉异常。
			 * */
			try{
				Thread.sleep(1000);
			}
			catch(InterruptedException e){
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"..."+num--);
		}
	}
	public void run(){
		while(true){
			doit();
		}
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ThreadSafeDemo2 tsd = new ThreadSafeDemo2();
		Thread t1 = new Thread(tsd);
		Thread t2 = new Thread(tsd);
		Thread t3 = new Thread(tsd);
		Thread t4 = new Thread(tsd);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}

}

4 线程死锁

死锁程序示例:
package captain;

//线程死锁示例。两个同步块(或者同步方法)相互嵌套可以导致线程死锁。
class DeadLock implements Runnable{
	private boolean flag;//布尔变量使不同的线程执行不同的线程任务
	DeadLock(boolean flag){
		this.flag = flag;
	}
	
	//创建两个不同的同步锁对象。
	Object obj1 = new Object();
	Object obj2 = new Object();
	
	public void run(){
		if(flag){
			synchronized(obj1){
				System.out.println(Thread.currentThread().getName()+"..if..obj1");
				synchronized(obj2){
					System.out.println(Thread.currentThread().getName()+"..if..obj2");
				}
			}
		}
		else{
			synchronized(obj2){
				System.out.println(Thread.currentThread().getName()+"..else..obj2");
				synchronized(obj1){
					System.out.println(Thread.currentThread().getName()+"..else..obj1");
				}
			}
		}	
	}
}

public class DeadLockDemo {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		DeadLock dl1 = new DeadLock(true);
		DeadLock dl2 = new DeadLock(false);
		Thread t1 = new Thread(dl1);
		Thread t2 = new Thread(dl2);
		t1.start();
		t2.start();
	}

}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值