1、线程的生命周期java
线程是一个动态执行的过程,它也有一个从产生到死亡的过程。多线程
下图显示了一个线程完整的生命周期this
使用 new 关键字和 Thread 类或其子类创建一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序start() 这个线程。spa
当线程对象调用了start()方法以后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。线程
若是就绪状态的线程获取 CPU 资源,就能够执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它能够变为阻塞状态、就绪状态和死亡状态。3d
若是一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源以后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或得到设备资源后能够从新进入就绪状态。能够分为三种:code
等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。对象
同步阻塞:线程在获取 synchronized 同步锁失败(由于同步锁被其余线程占用)。blog
其余阻塞:经过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程从新转入就绪状态。继承
一个运行状态的线程完成任务或者其余终止条件发生时,该线程就切换到终止状态
2、线程的优先级
具备较高优先级的线程对程序更重要,而且应该在低优先级的线程以前分配处理器资源。可是,线程优先级不能保证线程执行的顺序,并且很是依赖于平台
3、建立一个线程
Java 提供了三种建立线程的方法:
建立一个线程,最简单的方法是建立一个实现 Runnable 接口的类,为了实现 Runnable,一个类只须要执行一个方法调用 run()
1、建立一个对象类 package test_synthronized; public class Foo { int x=100; public int getX() { return x; } public int fix(int y) { synchronized(this){ x=x-y; System.out.println("线程"+Thread.currentThread().getName() + "运行结束,减小“" + y + "”,当前值为:" + x);} return x; } } 2、建立一个线程类 package test_synthronized; /** * 线程的同步与锁 */ public class Thread_synchronized_01 implements Runnable{ private Foo foo=new Foo(); public static void main(String[] args) { Thread_synchronized_01 syn=new Thread_synchronized_01(); Thread t1=new Thread(syn,"t1"); Thread t2=new Thread(syn,"t2"); t1.start(); t2.start(); } /* (non-Javadoc) * @see java.lang.Runnable#run() */ public void run() { for (int i = 0; i < 3; i++) { this.fix(30); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } /* * t1 : 当前foo对象的x值= 40 t2 : 当前foo对象的x值= 40 t1 : 当前foo对象的x值= -20 t2 : 当前foo对象的x值= -50 t1 : 当前foo对象的x值= -80 t2 : 当前foo对象的x值= -80 * 从结果发现,这样的输出值明显是不合理的。缘由是两个线程不加控制的访问Foo对象并修改其数据所致。 若是要保持结果的合理性,只须要达到一个目的,就是将对Foo的访问加以限制,每次只能有一个线程在访问。这样就能保证Foo对象中数据的合理性了。 在具体的Java代码中须要完成一下两个操做: 把竞争访问的资源类Foo变量x标识为private; 同步哪些修改变量的代码,使用synchronized关键字同步方法或代码。 */ System.out.println(Thread.currentThread().getName() + " : 当前foo对象的x值= " + foo.getX()); } } public int fix(int y) { return foo.fix(y); } } 3、结果 线程t1运行结束,减小“30”,当前值为:70 t1 : 当前foo对象的x值= 70 线程t1运行结束,减小“30”,当前值为:40 t1 : 当前foo对象的x值= 40 线程t1运行结束,减小“30”,当前值为:10 t1 : 当前foo对象的x值= 10 线程t2运行结束,减小“30”,当前值为:-20 t2 : 当前foo对象的x值= -20 线程t2运行结束,减小“30”,当前值为:-50 t2 : 当前foo对象的x值= -50 线程t2运行结束,减小“30”,当前值为:-80 t2 : 当前foo对象的x值= -80
2) 继承Thread来建立线程
第二种方法是建立一个新的类,该类继承 Thread 类,而后建立一个该类的实例。
继承类必须重写 run() 方法,该方法是新线程的入口点。它也必须调用 start() 方法才能执行。
该方法尽管被列为一种多线程实现方式,可是本质上也是实现了 Runnable 接口的一个实例
package test_synthronized; public class Thread_synchronized_02 {
class MyThread extends Thread{ private Foo foo; /**当前值*/ private int y = 0; MyThread(String name, Foo foo, int y) { super(name); this.foo = foo; this.y = y; } public void run() { foo.fix(y); } } public static void main(String[] args) { Thread_synchronized_02 run = new Thread_synchronized_02(); Foo foo=new Foo(); MyThread t1 = run.new MyThread("线程A", foo, 10); MyThread t2 = run.new MyThread("线程B", foo, 2); MyThread t3 = run.new MyThread("线程C", foo, 3); MyThread t4 = run.new MyThread("线程D", foo, 5); t1.start(); t2.start(); t3.start(); t4.start(); } } 结果 线程线程A运行结束,减小“10”,当前值为:90 线程线程C运行结束,减小“3”,当前值为:87 线程线程B运行结束,减小“2”,当前值为:85 线程线程D运行结束,减小“5”,当前值为:80
3) 经过 Callable 和 Future 建立线程
1. 建立 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将做为线程执行体,而且有返回值。
2. 建立 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。
3. 使用 FutureTask 对象做为 Thread 对象的 target 建立并启动新线程。
4. 调用 FutureTask 对象的 get() 方法来得到子线程执行结束后的返回值
package test_synthronized; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class CallableThread implements Callable<Integer> { public Integer call() throws Exception { int i = 0; for(;i<8;i++) { System.out.println(Thread.currentThread().getName()+" "+i); } return i; } public static void main(String[] args) { CallableThread callableThread=new CallableThread(); FutureTask<Integer> ft=new FutureTask<Integer>(callableThread); for(int i = 0;i < 8;i++) { System.out.println(Thread.currentThread().getName()+" 的循环变量i的值"+i); if(i==2) { new Thread(ft,"有返回值的线程").start(); } } try { System.out.println("子线程的返回值:"+ft.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } 结果 main 的循环变量i的值0 main 的循环变量i的值1 main 的循环变量i的值2 main 的循环变量i的值3 有返回值的线程 0 main 的循环变量i的值4 有返回值的线程 1 main 的循环变量i的值5 有返回值的线程 2 main 的循环变量i的值6 有返回值的线程 3 main 的循环变量i的值7 有返回值的线程 4 有返回值的线程 5 有返回值的线程 6 有返回值的线程 7 子线程的返回值:8
4、线程的三种方式的对比
1. 采用实现 Runnable、Callable 接口的方式创见多线程时,线程类只是实现了 Runnable 接口或 Callable 接口,还能够继承其余类。
2. 使用继承 Thread 类的方式建立多线程时,编写简单,若是须要访问当前线程,则无需使用 Thread.currentThread() 方法,直接使用 this 便可得到当前线程。