java多线程之创建线程与死锁

线程创建篇

   java中的线程创建有多种方式,笔者(ymh)这里分别根据线程执行完毕后是否有返回值讨论线程创建的四种方式。

1、通过继承Thread类重写run方法(无返回值)

示例代码:

2、通过实现Runnable接口(无返回值)

示例代码:

  前两种创建线程的方式是java初学者必须掌握的,这两种方式可以说是创建线程最简单的方式了,它们各有优缺点:

   第一种方式Thread类及其子类本身代表一个线程,重写run方法来指定该线程具体执行的任务。优点是在需求不高的情况下编程比较简单,但缺点是受限于java的单继承机制,继承了Thread类的子类不能再继承其他类,这往往在某些情况下可能会带来不便。

   第二种方式实现Runnable接口,则该类其实可以理解任务类,其实例就是线程要执行的某个具体的任务。这种方式的优点是多个线程可以共享一个任务对象,适合多个线程共同处理同一份资源的情况,较好地体现了面向对象的思想。缺点是编程相对复杂一些。

3、通过实现Callable接口(有返回值)

示例代码:

  第三种方式可以获取线程执行完毕后返回的结果。Callable 接口类似于Runnable接口,两者都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。

Callable实现类的实例可以使用FutureTask类(Future接口的实现类)封装,然后通过FutureTask实例构造一个Thread线程,在线程执行完毕时而已使用FutureTask实例尝试获取线程的返回结果,即call方法的返回值。

4、通过Executor框架(有返回值)

示例代码:

  第四种方式通过线程池执行任务,调用submit方法向线程池提交任务。Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结果。

死锁篇

   首先了解下什么是死锁:

  所谓的死锁就是两个即两个以上的线程在执行的过程中,因争夺资源而造成的一种相互等待的现象,若无外力作用,它们都将无法推进下去。死锁是一种很严重的问题,可能会造成程序挂起而无法完成任务,编程时应合理的使用同步锁机制来避免死锁(其实应该是不要滥用同步锁机制才对)。

  死锁产生的四个条件:

1、互斥条件:资源具有排他性,即一份资源同一时刻只能被一个线程占用,直到该资源被释放为止。

2、请求与保持条件:当一个线程因请求被其他线程占用的资源而阻塞时,对已占用的资源保持不释放。

3、不剥夺条件:当一份资源被某个线程占用时,其他线程不能强行剥夺该资源。

4、循环等待条件:若干线程之间形成一种头尾相接的循环等待资源的关系。

  一个简单的死锁案例:

  如何避免死锁?

  避免死锁最简单的方法就是破坏循环等待条件,可以将系统中所有的资源设置标志位、排序,规定所有线程申请资源必须按照一定的顺序访问。

例如有多个线程都需要访问A、B、C三份资源,必须保证所有线程请求资源的顺序都是一致的,例如先访问A,再访问B,最后访问C。

  限定资源访问的顺序是一种简单的避免死锁的机制,但使用这种方式必须清楚每一个线程在其生命周期内都需要访问什么资源,这在实际编程中几乎难以满足。

还有一些其他的避免死锁的机制,如加锁时限、死锁检测等,笔者(ymh)在这里不做讨论,本文的初衷也仅是供初学者学习和抛砖引玉。转载请注明出处!

猜你喜欢

转载自blog.csdn.net/someonemh/article/details/80773264