java自学笔记————多线程之死锁;

 

在java中多线程显得十分重要,在现实中,我们大家买车票的时候,肯定会接触到多线程,这篇博客将就多线程中的死锁重点讲解一下

要理解多线程,就必须理解线程。而要理解线程,就必须知道进程。

1、 进程

        是一个正在执行的程序。

        每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。

2、线程

        就是进程中的一个独立的控制单元。线程在控制着进程的执行。只要进程中有一个线程在执行,进程就不会结束。

        一个进程中至少有一个线程。

3、多线程

       java虚拟机启动的时候会有一个java.exe的执行程序,也就是一个进程。该进程中至少有一个线程负责java程序的执行。而且这个线程运行的代码存在于main方法中。该线程称之为主线程。JVM启动除了执行一个主线程,还有负责垃圾回收机制的线程。像种在一个进程中有多个线程执行的方式,就叫做多线程。

Java线程死锁是一个经典的多线程问题,因为不同的线程都在等待那些根本不可能被释放的锁,从而导致所有的工作都无法完成。假设有两个线程,分别代表两个饥饿的人,他们必须共享刀叉并轮流吃饭。他们都需要获得两个锁:共享刀和共享叉的锁。

  假如线程 “A”获得了刀,而线程“B”获得了叉。线程“A”就会进入阻塞状态来等待获得叉,而线程“B”则阻塞来等待“A”所拥有的刀。这只是人为设计的例子,但尽管在运行时很难探测到,这类情况却时常发生。虽然要探测或推敲各种情况是非常困难的,但只要按照下面几条规则去设计系统,就能够避免Java线程死锁问题:

  让所有的线程按照同样的顺序获得一组锁。这种方法消除了 X 和 Y 的拥有者分别等待对方的资源的问题。

  将多个锁组成一组并放到同一个锁下。前面Java线程死锁的例子中,可以创建一个银器对象的锁。于是在获得刀或叉之前都必须获得这个银器的锁。

  将那些不会阻塞的可获得资源用变量标志出来。当某个线程获得银器对象的锁时,就可以通过检查变量来判断是否整个银器集合中的对象锁都可获得。如果是,它就可以获得相关的锁,否则,就要释放掉银器这个锁并稍后再尝试。

 

同步中嵌套同步就有可能发生死锁:

死锁案例:



public class Bolg9 {

	/**
	 * @param args
	 */

	/*
	 * 当类的对象flag=1时(T1),先锁定O1,睡眠500毫秒,然后锁定O2;
	 * 而T1在睡眠的时候另一个flag=0的对象(T2)线程启动,先锁定O2,睡眠500毫秒,等待T1释放O1;
	 * T1睡眠结束后需要锁定O2才能继续执行,而此时O2已被T2锁定; T2睡眠结束后需要锁定O1才能继续执行,而此时O1已被T1锁定;
	 * T1、T2相互等待,都需要对方锁定的资源才能继续执行,从而死锁。
	 */

	public static void main(String[] args) {
		
		DeadThread td1 = new DeadThread();
		DeadThread td2 = new DeadThread();
		td1.flag = 1;
		td2.flag = 0;
		new Thread(td1).start();
		new Thread(td2).start();

	}

}

class DeadThread implements Runnable {

	public int flag = 1;
	static Object o1 = new Object(), o2 = new Object();

	@Override
	public void run() {
		System.out.println("flag=" + flag);
		if (flag == 1) {
			synchronized (o1) {
				try {
					Thread.sleep(500);
				} catch (Exception e) {
					e.printStackTrace();
				}
				synchronized (o2) {
					System.out.println("1");
				}
			}
		}
		if (flag == 0) {
			synchronized (o2) {
				try {
					Thread.sleep(500);
				} catch (Exception e) {
					e.printStackTrace();
				}
				synchronized (o1) {
					System.out.println("0");
				}
			}
		}
	}

}

发生死锁:

flag=1
flag=0

     解决方式:
        1)尽量使用tryLock(long timeout, TimeUnit unit)的方法(ReentrantLock、ReentrantReadWriteLock),设置超时时间,超时可以退出防止死锁。
        2)尽量使用java.util.concurrent(jdk 1.5以上)包的并发类代替手写控制并发,比较常用的是ConcurrentHashMap、ConcurrentLinkedQueue、AtomicBoolean等等,实际应用中java.util.concurrent.atomic十分有用,简单方便且效率比使用Lock更高
        3)尽量降低锁的使用粒度,尽量不要几个功能用同一把锁
        4)尽量减少同步的代码块

 

你可能感兴趣的:(java自学笔记————多线程之死锁;)