首先说一下为什么要使用多线程。
线程是CPU调度的基本单元,进程是系统资源分配的基本单元。一个程序作为一个进程来执行,程序运行过程中能够创建多个线程,而一个线程在一个时刻只能运行在一个处理器核心上。也就是说单线程程序只能使用一个处理器核心,那么加入再多的处理器核心也无法显著提升程序的执行效率。相反,如果该程序使用多线程技术,将计算逻辑分配到多个处理器核心上,就会显著减少程序的处理时间,并且随着更多处理器核心的加入而变得更有效率。
线程的创建,有3种方式:继承Thread类、实现Runnable接口、实现Callable接口。
Thread类常用的静态方法:
1.Thread.activeCount(),得到存活的线程数,返回值是int类型;
2.Thread.currentThread(),得到当前线程,返回值是当前Thread实例;
3.Thread.sleep(long millis),让当前线程睡眠指定时间,以毫秒为单位;
4.Thread.yield(),让当前线程释放掉调度器的调度,使得调度器重新调度。一般来说只有优先级比当前线程高或者至少跟当前线程优先级相同的线程才可能得到调度器的调用,否则还是该线程被调度器调用。
Thread类常用的非静态方法:
1.thread1.get/setName(),thread1.get/setPriority(),thread1.isAlive() 是否存活,thread1.isDaemon() 是否是后台线程,thread1.getState() 得到线程的状态,getTheadGroup() 得到线程组。
2.thread1.join(),thread1.join(long millis):当前线程等待thread1线程执行后再执行。注意,join()方法要在start()之后再调用,否则不生效。
示例:
public class JoinTest { public static void main(String[] args) throws InterruptedException { Thread threadA = new Thread(new PrintThread(), "线程A"); threadA.start(); threadA.join(); System.out.println("主线程代码执行"); } } class PrintThread implements Runnable { @Override public void run() { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 1; i <= 10; i++) { System.out.println(i); } } }
如上,在main线程中执行threadA.join(),就告诉了main线程要等待threadA线程执行完,才能执行main线程后面的代码。如果join()方法有参数,则main线程会至多等待threadA线程指定时间,如果超过了这个时间threadA线程还没执行完,那么main线程也不等了,开始执行main线程后面的代码。
3.wait() 使当前线程等待,notify() 唤醒在此同步监视器上等待的单个线程,notifyAll() 唤醒在此同步监视器上等待的所有线程:这三个方法不是Thread类定义的,而是继承Object类得到的,这三个方法只能由同步监视器对象调用。synchronized同步方法,this就是同步监视器对象。synchronized同步代码块,synchronized后面括号里的对象就是同步监视器对象。
线程的状态
线程一共有6种状态,分别是NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED,在Thread.State枚举中可以看到。
new:初始状态,线程创建后还没有调用start()方法时的状态。
runnable:运行状态,调用start()方法之后的状态,包括running 运行中状态和ready 就绪状态,获取到时间分片就是running 状态,获取不到时间分片就是ready 状态。
blocked:阻塞状态,线程遇到synchronized 同步代码块或者同步方法,但是获取不到锁的状态。
waiting:等待状态,进入该状态表示当前线程需要等待其他线程做出一些特定动作,通知或中断,变为runnable 状态或者
timed_waiting:限时等待状态,同waiting 状态不同,处于此状态的线程等待超过一段时间后会自动变为runnable 状态。
terminated:终止状态,线程执行完毕后的状态。
线程不是固定地处于某个状态,而是随着代码的执行在不同的状态之间进行切换。见下图,图有点小问题,需要修改下。
1、线程创建之后,进入new 状态,调用start()方法后,进入runnable 状态。
2、线程可以在runnable 状态和waiting 状态间切换:
⑴runnable 状态线程在以下三种情况下会变为waiting 状态:
①锁对象调用wait()方法。当前线程立即释放锁,且变为waiting 状态,等待notify/notifyAll。
②Thread对象调用join()方法
③调用LockSupport类的park()静态方法
⑵waiting 状态线程在以下三种情况下会变回runnable 状态:
①锁对象调用notify()方法。当前线程不会立即释放锁,等执行结束后才会释放锁。
②锁对象调用notifyAll()方法。
③调用LockSupport类的unpark(Thread thread)方法
3、线程可以在runnable 状态和timed_waiting 状态间切换:
⑴runnable 状态线程在以下五种情况下回变为timed_waiting 状态:
①调用Thread类的sleep(long millis)方法
②锁对象调用wait(long timeout)方法。
③执行另一个线程对象的join(long millis)方法
④调用LockSupport类的parkNanos(long nanos)方法
⑤调用LockSupport类的parkUntil(long deadline)方法
⑵timed_waiting 状态线程变回runnable 状态的条件和waiting 状态线程变回runnable 状态的条件一样,除此之外,在等待时间超过之后,也会由timed_waiting 状态变为runnable 状态。
4、线程可以在runnable 状态和blocked 状态间切换:
⑴runnable 状态线程在遇到synchronized 同步代码块或者同步方法时,如果获取不到锁,就会变为blocked 状态。
⑵blocked 状态线程如果获取到锁进入synchronized 同步代码块或者同步方法了,就会变为runnable 状态。
5、runnable 状态线程在run()方法执行完之后就会变为terminated 状态。
需要指出的是:
wait()、wait(long timeout)、notify()、notifyAll()方法是Object的实例方法,虽然看起来所有对象都可以调用,但是非锁对象调用时会报java.lang.IllegalMonitorStateException异常。
blocked状态仅仅和synchronized 同步代码块或者同步方法有关,与Lock接口无关。与Lock接口有关的是waiting状态和timed_waiting状态,因为Lock接口实现类使用的是LockSupport类的相关方法。
thread.join()的使用
如果一个线程A执行了thread.join()方法,则线程A会变为waiting 状态,直到thread线程执行完之后再变为runnable 状态。thread.join(long millis)方法在此基础上,如果超过了millis时间thread还没执行完,则线程A就会立即由waiting状态变为runnable状态。
public class NotifyTest2 { private String flag[] = {"true"}; class NotifyThread extends Thread { public NotifyThread(String name) { super(name); } @Override public void run() { try { System.out.println(getName() + " 111"); sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(getName() + " 222"); synchronized (flag) { System.out.println(getName() + " 333"); flag[0] = "false"; flag.notifyAll(); } } } class WaitThread extends Thread { public WaitThread(String name) { super(name); } @Override public void run() { System.out.println(getName() + " aaa"); synchronized (flag) { while (!"false".equals(flag[0])) { System.out.println(getName() + " begin waiting!"); long waitTime = System.currentTimeMillis(); try { flag.wait(); } catch (InterruptedException e) { e.printStackTrace(); } waitTime = System.currentTimeMillis() - waitTime; System.out.println(getName() + " wait time :" + waitTime); } System.out.println(getName() + " end waiting!"); } } } public static void main(String[] args) { System.out.println("Main Thread Run!"); NotifyTest2 test = new NotifyTest2(); test.new WaitThread("waiter01").start(); test.new WaitThread("waiter02").start(); test.new WaitThread("waiter03").start(); test.new NotifyThread("notify01").start(); } }