Java多线程(图解)

这里总结下java多线程的相关概念java

1、线程的建立和启动

1.扩展java.lang.Thread类
用户的线程类只须要继承Thread类, 覆盖Thread类的run()方法,而后经过该类的实例对象调用start()方法启动线程便可。程序员

// 类A继承了Thread类
    A a = new A();
    a.start() // 启动线程

注意:不要覆盖Thread类的start()方法、一个线程只能被启动一次,屡次调用会抛出IllegalThreadStateException异常。web

2.实现Runnable接口
Java不容许一个类继承多个类,所以一旦一个类继承了Thread类,就不能再继承其余的类。为了解决这一问题,Java提供了java.lang.Runnable接口,一样,重写接口中的run()方法便可。编程

// 类A实现了接口Runnable
    A a = new A();
    Thread t = new Thread(a);
    t.start(); // 启动线程

3.实现Callable接口
此方法与实现Runnable接口类似,只不过是要实现call()方法,不经常使用。多线程

Callable和Runnable接口的区别:
a. Callable规定的方法是call(),而Runnable规定的方法是run()。
b. Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。
c. call()方法可抛出异常,而run()方法是不能抛出异常的。并发

2、线程状态

  1. 新建状态(New)
    用new语句建立的线程对象处于新建状态,此时它和其余Java对象同样,仅在堆区中被分配了内存。
  2. 就绪状态(Runnable)
    当一个线程对象建立后,其余线程调用它的start()方法, 该线程就进入就绪状态,处于这个状态的线程位于可运行池中,等待得到CPU的使用权。
  3. 运行状态(Running)
    处于这个状态的线程占用CPU,执行程序代码。在并发运行环境中, 若是计算机只有一个CPU,那么任什么时候刻只会有一个线程处于这个状态。若是计算机有多个CPU, 那么同一时刻可让几个线程占用不一样的CPU,使它们都处于运行状态。只有处于就绪状态的线程才有机会转到运行状态。
  4. 阻塞状态(Blocked)
    指线程由于某些缘由放弃CPU, 暂时中止运行。当线程处于阻塞状态时,Java虚拟机不会给线程分配CPU,直到线程从新进入就绪状态,它才有机会转到运行状态。
    阻塞状态可分为三种:
    . 位于对象等待池中的阻塞状态(Blocked in objects’ wait pool): 运行状态时,执行某个对象的wait()方法;
    . 位于对象锁池中的阻塞状态(Blocked in object’s lock pool): 当线程处于运行状态,试图得到某个对象的同步锁时,如该对象的同步锁已经被其余线程占用,Java虚拟机就会把这个线程放到这个对象的锁池中;
    . 其余阻塞状态(Otherwise Blocked): 当前线程执行了sleep()方法,或者调用了其余线程的join()方法,或者发出了I/O请求时,就会进入这个状态。svg

    线程运行状态图解
    这里写图片描述性能

3、线程调度

线程的调度不是跨平台的,它不只取决于Java虚拟机,还依赖于操做系统。
在某些操做系统中,只要运行中的线程没有阻塞,就不会放弃CPU;
在某些操做系统中,即便运行中的线程没有遇到阻塞,也会在运行一段时间后放弃CPU,给其余线程运行机会。

这里简单介绍几个线程中的方法:测试

  1. stop()
    Thread类的stop()方法能够强制终止一个线程,但从JDK1.2开始废弃了stop()方法。在实际编程中,通常是在受控制的线程中定义一个标志变量,其余线程经过改变标志变量的值,来控制线程的天然终止、暂停及恢复运行。ui

  2. Thread.sleep(5000);
    放弃CPU, 转到阻塞状态。当结束睡眠后,首先转到就绪状态,若有其它线程在运行,不必定运行,而是在可运行池中等待得到CPU。

  3. Thread.interrupt();
    中断某个线程

  4. Thread.isInterrupted();
    测试某个线程是否被中断,与static boolean interrupted()不一样,对它的调用不会改变该线程的“中断”状态。

  5. join()
    挂起当前线程(通常是主线程),直至它所调用的线程终止才被运行。线程A中调用线程B.join(),是使A线程阻塞,由于是A线程调用的B.join()这个方法谁调用谁阻塞。

4、线程同步

  1. 若是一个同步代码块和非同步代码块同时操纵共享资源,仍然会形成对共享资源的竞争。由于当一个线程执行一个对象的同步代码块时,其余线程仍然能够执行对象的非同步代码块。

  2. 每一个对象都有惟一的同步锁。

  3. 在静态方法前面也可使用synchronized修饰符。此时该同步锁的对象为类对象(类的Class对象)。

  4. 当一个线程开始执行同步代码块时,并不意味着必须以不中断的方式运行。进入同步代码块的线程也能够执行Thread.sleep()或者执行Thread.yield()方法,此时它并无释放锁,只是把运行机会(即CPU)让给了其余的线程。

  5. synchnozied声明不会被继承。

5、线程通讯

锁对象.wait(): 执行该方法的线程释放对象的锁,Java虚拟机把该线程放到该对象的等待池中。该线程等待其它线程将它唤醒;

锁对象.notify(): 执行该方法的线程唤醒在对象的等待池中等待的一个线程。Java虚拟机从对象的等待池中随机选择一个线程,把它转到对象的锁池中。若是对象的等待池中没有任何线程,那么notify()方法什么也不作。

6、线程让步

Thread.yield()静态方法,若是此时具备相同优先级的其余线程处于就绪状态,那么yield()方法将把当前运行的线程放到可运行池中并使另外一个线程运行。若是没有相同优先级的可运行线程,则yield()方法什么也不作。

sleep()和yield()方法都是Thread类的静态方法,区别以下:

sleep()不考虑其余线程优先级;
yield()只会给相同优先级或者更高优先级的线程一个运行的机会。

sleep()转到阻塞状态;
yield()转到就绪状态;

sleep()会抛出InterruptedException异常,
yield()不抛任何异常

sleep()比yield方法具备更好的可移植性。对于大多数程序员来讲,yield()方法的惟一用途是在测试期间人为地提升程序的并发性能,以帮助发现一些隐藏的错误,因此yield()并不经常使用。