在说线程之前,有必要说下线程与进程之间的关系。
附oracle官网链接:https://docs.oracle.com/javase/tutorial/essential/concurrency/procthread.html
这里有原版的关于线程和进程的介绍,我们只简单介绍下,作为知识普及。
一.进程与线程的关系
1.进程:进程一般情况下是程序或者应用程序的同义词,是系统进行资源和调度的基本单位,是操作系统结构的基础。它有自己独立的内存空间,换句话说,它有一套私有的运行时资源。在现代面向线程的计算机结构中,进程作为了线程的容器存在,而进程变为了程序的代名词。但是要注意的是,Java虚拟机的大多数实现都是作为一个进程运行的。
2.线程:在面向线程的计算机结构中,线程有时被称为轻量级的进程。在进程中,各个线程共享进程的资源(内存和文件)。每个应用程序都至少包含一个线程。
3.为什么要使用线程进行并发程序设计?
因为相对于进程来说,线程的切换和调度的成本远远小于进程。
二.线程的生命周期
既然要说线程,就免不了说下线程的生命周期,也就是各个状态之间的转换。
可以看到,在源码Thread类中定义了一个枚举类型State,这个State就是线程生命周期中的各种状态。
根据英文注释,我们可以知道各个状态的含义,这里不细说。
附线程的状态转换图:
后续篇我们逐渐详述介绍这几种状态之间的转换过程。
三.线程的基本操作(这些都很简单,不在这里详细说)
1.新建线程
新建一个线程有两种方式:一种是继承Thread类,并重写Thread类的run方法(因为你要在run方法中实现你的业务逻辑);另外一种是实现Runnable接口。
public class ThreadDemo extends Thread { @Override public void run() { // TODO Auto-generated method stub super.run(); System.out.println("继承Thread实现线程执行"); } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub new ThreadDemo().start(); } }
public class RunnableDemo implements Runnable { @Override public void run() { System.out.println("我的第一个线程程序!"); } public static void main(String[] args) { new Thread(new RunnableDemo()).start(); } }其实通过源码来看,实际就是一种方式,直接实现Runnable接口。我们可以看下Thread源码类中: 所以关于如何新建线程的方式的讨论可以休矣。 2.废弃方法(可以不看了) stop方法:暴力终止,会出现数据不一致,废弃也在情理之中,被带有中断机制的方法代替了。 suspend:挂起方法,也被废弃了,是因为调用此方法后,线程挂起后不释放资源,并且线程的状态是RUNNABLE,可能会让我们误判当前的系统状态。 resume:是与suspend配套使用的,但是如果它比suspend先执行,那线程永远挂着了,图就不截了。 3.中断方法: 有关中断的有三个方法: ①Thread类中的interrupt()方法是实例方法,它可以设置线程的中断标识(也就是说并不是调用这个方法就中断了) ②配合Thread类中的实例方法isInterrupted()来判断当前调用线程是否设置了中断标识。 ③Thread类中的interrupted()也是用来判断当前的中断状态,但是 会清除中断标识位。
public static void main(String[] args) throws InterruptedException { Thread t = new Thread(){ @Override public void run() { while(true){ System.out.println(Thread.currentThread().interrupted()); if(Thread.currentThread().isInterrupted()){ System.out.println("我被中断了!"); break; } } } }; t.start(); t.interrupt(); }另外要注意的是,使用Thread.sleep(long xx)方法时(还有join方法,凡是报中断异常的方法),如果线程被中断,会抛出中断异常,并且标志位也会被清除。
public static void main(String[] args) throws InterruptedException { Thread t = new Thread(){ @Override public void run() { while(true){ try { System.out.println(Thread.currentThread().getName()+"|"+Thread.currentThread().isInterrupted()); Thread.sleep(1000); System.out.println(Thread.currentThread().getName()+"|"+Thread.currentThread().isInterrupted()); } catch (InterruptedException e) { e.printStackTrace(); //如果要正确处理逻辑,这里要恢复中断标志位,重新进行中断 //重新设置中断标志 Thread.currentThread().interrupt(); } if(Thread.currentThread().isInterrupted()){ System.out.println("我被中断了!"); break; } } } }; t.start(); t.interrupt(); }4.等待(wait)和通知(notify) 首先要知道,这两方法是Object类中的方法,也就是说任何类都继承了这两方法。 在一个线程中,当一个对象调用wait()方法后,当前线程就会等待在这个对象上。直到另外一个线程调用了obj.notify()方法后,才能解除等待。要注意的第一点,这里的对象是同一个!第二点,这个对象必须被用作监视器锁!第三点,等待的线程必须获得了监视器锁后才能执行,并不是说另一个线程执行了notify()方法后就能被唤醒了!
public class WaitNotifyDemo{ final static Object obj = new Object(); static class T1 extends Thread { @Override public void run() { synchronized (obj) { System.out.println(System.currentTimeMillis()+":T1 start!"); try { obj.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(System.currentTimeMillis()+":T1 end!"); } } } static class T2 extends Thread { @Override public void run() { synchronized (obj) { System.out.println(System.currentTimeMillis()+":T2 start!"); obj.notify(); try { Thread.sleep(2000);//为了看的明显,等待2s } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(System.currentTimeMillis()+":T2 end!"); } } } public static void main(String[] args) { new T1().start(); new T2().start(); } }5.线程的加入(join)和谦让(yield) 线程的加入:在一个线程A中调用线程B的join()方法,那么线程A就必须等线程B的逻辑执行完成后,才能继续往下执行自己A的逻辑。加入实际就是一起走的意思!下面例子,最后i肯定是100000.
public class ThreadJoinDemo{ public volatile static int i = 0; static class T1 extends Thread { @Override public void run() { for(i=0;i<100000;i++); } } public static void main(String[] args) throws InterruptedException { T1 t1 = new T1(); t1.start(); t1.join(); System.out.println(i); } }线程资源让出-yield:Thread.yield()是一个静态的本地方法。让出的意思很明确,让出CPU资源。但是,让出了我还要争夺的! 四.线程的分类管理 1.线程的分组-线程组 给线程分个组,起个好听的名字吧,方便管理,默认新建的线程所在的线程组是创建线程的父组。
public class ThreadGroupDemo implements Runnable{ /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub ThreadGroup tg = new ThreadGroup("zhaodf"); Thread t1 = new Thread(tg, new ThreadGroupDemo(), "T1"); Thread t2 = new Thread(tg, new ThreadGroupDemo(), "T2"); Thread t3 = new Thread(new ThreadGroupDemo()); t1.start(); t2.start(); t3.start(); } @Override public void run() { // TODO Auto-generated method stub System.out.println(Thread.currentThread().getThreadGroup().getName()+"_"+Thread.currentThread().getName()); try { Thread.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }2.守护线程:守护线程守护的是谁?守护的是用户线程,用户线程不存在了,它就没有意义了,因此也就停掉了。
public class Daemon { public static class DaemonT extends Thread{ @Override public void run() { while(true){ System.out.println(System.currentTimeMillis()+"_"+Thread.currentThread().getName()+"_i am alive"); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } /** * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { // TODO Auto-generated method stub Thread t = new DaemonT(); //必须在start方法之前设置守护线程,这样t变为守护线程,而主线程main成为用户线程。当主线程休眠2秒后,守护线程也退出 //如果放在start之后,会抛出Exception in thread "main" java.lang.IllegalThreadStateException异常 t.setDaemon(true); t.start(); System.out.println(System.currentTimeMillis()+"_"+"主线程名称:"+Thread.currentThread().getName()); Thread.sleep(2000); } }3.线程的优先级: 线程的优先级并不能保证线程优先执行,这点要注意。
public class PriorityDemo{ public static class HightPriority extends Thread{ static int count = 0; @Override public void run() { // TODO Auto-generated method stub while(true){ synchronized (PriorityDemo.class) { count++; if(count>10000){ System.out.println("HightPriority is complete"); break; } } } } } public static class LowerPriority extends Thread{ static int count = 0; @Override public void run() { // TODO Auto-generated method stub while(true){ synchronized (PriorityDemo.class) { count++; if(count>10000){ System.out.println("LowerPriority is complete"); break; } } } } } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Thread high = new HightPriority(); Thread low = new LowerPriority(); //设置优先级并不能保证线程一定优先执行 high.setPriority(Thread.MAX_PRIORITY); low.setPriority(Thread.MIN_PRIORITY); high.start(); low.start(); } }