解密线程安全问题

线程基础->->深入理解java多线程

1.多线程操作同一数据

* 多线程并发操作同一数据时, 就有可能出现线程安全问题
* 使用同步技术可以解决这种问题, 把操作数据的代码进行同步, 不要多个线程一起操作

例子:火车站卖票问题,四个线程同时卖100张票

package 测试区;



public class Demo3_Ticket {

	/**
	 * 需求:铁路售票,一共100张,通过四个窗口卖完.
	 */
	public static void main(String[] args) {
		new Ticket().start();
		new Ticket().start();
		new Ticket().start();
		new Ticket().start();
	}

}

class Ticket extends Thread {
	private static int ticket = 100;                    //这里注意要加静态
	//private static Object obj = new Object();		//如果用引用数据类型成员变量当作锁对象,必须是静态的
	public void run() {
		while(true) {
			{
				if(ticket <= 0) {
					break;
				}
				try {
					Thread.sleep(10);				//线程1睡,线程2睡,线程3睡,线程4睡,用来模拟中间的其他操作,表示间歇
				} catch (InterruptedException e) {
					
					e.printStackTrace();
				}
				System.out.println(getName() + "...这是第" + ticket-- + "号票");
			}
		}
	}
}


输出结果:
在这里插入图片描述

为什么会卖-1张票呢?原因是因为这里的线程方法并不是同步的,就是有可能在线程2进入while循环且快执行到ticket–这个一步时,线程1抢先一步将ticket减到0,这样线程2并不会退出,而是继续执行,因此就会产生-1这样的结果。为了解决这一问题,我们可以采用同步代码块的方法。注意这里的锁对象不能是this对象,因为每个线程都有一个this,而他们并不一样,也不能在run方法中创建对象同步,应为每个线程里面的对象也是独立的,但是可以用静态对象来解决这个问题。推荐使用字节码对象,例如上面的Ticket.class。
在这里插入图片描述
加上了以后输出就正常了:
在这里插入图片描述

2. 多线程死锁

当线程的代码块同步嵌套,就可能会发生锁死的情况

例子:假设有两个人分别叫做thread1,和thread2,他们去抢左筷子和有筷子,但是只有当都拿到了一双筷子才能吃一口饭,且只有一方吃过一口饭后才能把筷子交给对方。这样当两个人一人拿到一双筷子后就会产生一种悖论,对应到计算机中就是线程的锁死。

package 测试区;

 

public class Demo5_DeadLock {

	/**
	 * @param args
	 */
	private static String s1 = "筷子左";
	private static String s2 = "筷子右";

	public static void main(String[] args) {
		new Thread() {
			public void run() {
				while(true) {
					synchronized(s1) {
						System.out.println(getName() + "...获取" + s1 + "等待" + s2);
						synchronized(s2) {
							System.out.println(getName() + "...拿到" + s2 + "开吃");
						}
					}
				}
			}
		}.start();
		
		new Thread() {
			public void run() {
				while(true) {
					synchronized(s2) {
						System.out.println(getName() + "...获取" + s2 + "等待" + s1);
						synchronized(s1) {
							System.out.println(getName() + "...拿到" + s1 + "开吃");
						}
					}
				}
			}
		}.start();
	}
}

输出结果
在这里插入图片描述
这里前面几句是因为Thread-0启动的比较早,但是当Thread-1启动后,双方互不释放信号,这样就会发生死锁,进程停止,正常情况下,程序会一直进行。当然也会出现互相按顺序执行的情况机率非常小。

所以,不要使用有嵌套的同步
在这里插入图片描述

3.回顾常用类线程安全

①Vector 是线程安全的,ArrayList 是线程不安全的
在这里插入图片描述
在这里插入图片描述
同理查看源码可知,
②StringBuffer线程安全,StringBuilder线程不安全
③HashTable 线程安全,HashMap线程不安全
但是没关系,Collection中有三个对应的方法可以把线程不安全的变成线程安全的。

在这里插入图片描述

发布了79 篇原创文章 · 获赞 514 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/i6223671/article/details/89419686