线程的同步与死锁

一、同步

当多个线程访问同一资源的时候一定需要考虑到同步问题,那么下面首先通过一个简单的卖票程序,来观察不同步的情况。

范例:观察问题

class MyThread implements Runnable {
	private int ticket = 8; // 一共8张票

	@Override
	public void run() { // 线程的主方法
		for (int x = 0; x < 20; x++) {
			if (this.ticket > 0) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + ",ticket = " + this.ticket--);
			}
		}
	}
}

public class TestDemo {
	public static void main(String[] args) {
		MyThread mt = new MyThread();
		new Thread(mt, "票贩子A").start();
		new Thread(mt, "票贩子B").start();
		new Thread(mt, "票贩子C").start();
		new Thread(mt, "票贩子D").start();
		new Thread(mt, "票贩子E").start();
		new Thread(mt, "票贩子F").start();
	}
}

那么在这个时候就发生了不同步的情况,而发生的原因也很简单。对于整个卖票过程实际上是两步完成的:

  • 第一步:使用if语句判断是否有票;
  • 第二步:进行票数的修改。

但是此时有可能会出现这样一种情况:如果说现在只有最后一张票了,可以满足if条件判断,所以线程可以通过if语句拦截,可是在修改票之前出现了一个延迟操作,那么有可能在这个延迟的过程之中,又出现了其它线程进入到方法之中,由于此时没有修改票数,那么这个线程也可以满足if条件判断,后面的线程依次类推,所以当休眠时间一过,进行票数修改的时候,都将在已有的票数上修改,自然就有可能出现负数。

那么既然已经清楚了问题的产生原因,下面就必须进行问题的解决,问题的解决关键是需要一把锁。如果要想上好责把“锁”,则可以采用两种方式完成:同步代码块、同步方法。

范例:使用同步代码块

同步代码块是使用synchronized关键字定义的代码块,但是在进行同步的时候一定要设置好一个同步对象,所以这个同步对象一般使用当前对象this表示。

class MyThread implements Runnable {
	private int ticket = 8; // 一共8张票

	@Override
	public void run() { // 线程的主方法
		for (int x = 0; x < 20; x++) {
			synchronized (this) {
				if (this.ticket > 0) {
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() + ",ticket = " + this.ticket--);
				}
			}
		}
	}
}

public class TestDemo {
	public static void main(String[] args) {
		MyThread mt = new MyThread();
		new Thread(mt, "票贩子A").start();
		new Thread(mt, "票贩子B").start();
		new Thread(mt, "票贩子C").start();
		new Thread(mt, "票贩子D").start();
		new Thread(mt, "票贩子E").start();
		new Thread(mt, "票贩子F").start();
	}
}

此时的程序执行速度变得明显缓慢了,因为需要一个个排队进行,就好比你们上网和去银行取钱。一定是上网快。虽然性能变慢了,但是数据的安全性提高了,也就是说异步处理(不加synchronized)和同步处理(使用synchronized)的区别也在于此。

范例:同步方法实现

如果在一个方法的声明上使用了synchronized关键字,则表示此方法是一个同步方法。

class MyThread implements Runnable {
	private int ticket = 8; // 一共8张票

	@Override
	public void run() { // 线程的主方法
		for (int x = 0; x < 20; x++) {
			this.sale();
		}
	}

	public synchronized void sale() {
		if (this.ticket > 0) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + ",ticket = " + this.ticket--);
		}
	}
}

public class TestDemo {
	public static void main(String[] args) {
		MyThread mt = new MyThread();
		new Thread(mt, "票贩子A").start();
		new Thread(mt, "票贩子B").start();
		new Thread(mt, "票贩子C").start();
		new Thread(mt, "票贩子D").start();
		new Thread(mt, "票贩子E").start();
		new Thread(mt, "票贩子F").start();
	}
}

 同步的核心意义:一个线程要等待另一个线程执行完毕。


二、死锁

死锁实际上是在项目运行过程之中产生的一种问题,所以来讲下面的代码只是为大家演示死锁出现的状况,但是代码本身没有任何研究的意义。

所谓的死锁就是指的互相等待的情况,下面编写代码观察。

class JinBo {
	public synchronized void tell(XiePengXiang xpx) {
		System.out.println("小金子说:给我5000W,放了你儿子,见钱放人!");
		xpx.get();
	}

	public synchronized void get() {
		System.out.println("小金子得到钱,放了小刁。");
	}
}

class XiePengXiang {
	public synchronized void tell(JinBo j) {
		System.out.println("小谢子说:先放了我儿子,给你5000W,见人给钱!");
		j.get();
	}

	public synchronized void get() {
		System.out.println("小谢子亏了钱,救了儿子。");
	}
}

public class TestDemo implements Runnable {
	private JinBo jin = new JinBo();
	private XiePengXiang xpx = new XiePengXiang();

	public static void main(String[] args) {
		new TestDemo();
	}

	public TestDemo() {
		new Thread(this).start();
		jin.tell(xpx);
	}

	@Override
	public void run() {
		xpx.tell(jin);
	}
}

结论:多个线程访问同一资源的时候一定要考虑到线程的同步,但是同步会影响程序的性能,同时会提升数据的安全性。过多的同步(synchronized)会有可能出现死锁。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值