java线程是很重要的一项,所以作为java程序员必须要掌握的。
理解java线程必须先理解线程在java中的生命周期。、
1.java线程生命周期
1.new 创建一个线程 java中创建线程有三种方式,1.继承Thread类,2.继承Runnable接口,然后重写父类的run方法。
2.Runnable 线程处于就绪状态,随时可以被cpu调用。
3.Running 线程处于运行状态,此时线程正在CPU中执行。
4.Blocked 线程处于阻塞状态,由于某种原因,cpu暂时中断线程的资源。只有线程再次进入就绪状态才能重新被cpu调用,同时阻塞有三种状态
(1)等待阻塞:运行状态中的线程执行wait方法,使本线程进入等待阻塞状态。
(2)同步阻塞:线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
(3)其他阻塞:通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
(4)死亡状态:线程运行完毕或因异常终止线程,该线程结束生命周期。
2.线程的三种创建方式
(1) 继承Thread类
public class MyThread extends Thread{ public MyThread(String name){ super(name); } @Override public void run() { for(int i=0;i<100;i++) System.out.println(Thread.currentThread().getName()+" "+i); } }
(2)继承Runnable接口
public class MyRunnable implements Runnable{ private boolean stop=false; @Override public void run() { for(int i=0;i<100&&!stop;i++){ System.out.println(Thread.currentThread().getName()+" "+i); } } public void stopThread(){ this.stop=true; } }
二者的区别在于Thread继承Runnable类,然后再继承Thread类从而调用线程,而Runnable类是一个接口,通过直接继承Runnable类来实现。
同时二者在创建时还有区别
测试 Thread:
public void testThread() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); if (i == 30) { MyThread myThread1 = new MyThread("窗口1"); MyThread myThread2 = new MyThread("窗口2"); MyThread myThread3 = new MyThread("窗口3"); MyThread myThread4 = new MyThread("窗口4"); myThread1.start(); myThread2.start(); myThread3.start(); myThread4.start(); } } }
测试Runnable方式:
public void testRunnable() { int i = 0; for (i = 0; i <= 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); if (i == 30) { Runnable myRunnable = new MyRunnable(); Thread thread1 = new Thread(myRunnable); Thread thread2 = new Thread(myRunnable); thread1.start(); thread2.start(); } } }
可以看出来在继承Thread类后通过new 一个继承了Thread的MyThread类来实现线程的创建,然后通过调用Thread方法中的start()方法使线程处于就绪状态。
而继承了Runnable方法的类new一个MyRunnable 然后在将myRunnable放入new的一个Thread中,然后在通过start()方法使线程处于就绪状态。
3.继承Callable类
public class MyCallable implements Callable<Integer> { private int i=0; @Override public Integer call() throws Exception { int sum=0; for(;i<100;i++){ System.out.println(Thread.currentThread().getName()+" "+i); sum+=i; } return sum; } }
测试:
public void testCallable() { Callable<Integer> myCallable = new MyCallable(); FutureTask<Integer> ft = new FutureTask<>(myCallable); for (int i = 0; i <= 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); if (i == 30) { Thread thread = new Thread(ft); thread.start(); } } System.out.println("主线程执行完毕"); try { int sum = ft.get(); System.out.println("sum= " + sum); } catch (Exception e) { e.printStackTrace(); } }
通过测试你会发现继承了Callable类之后会通过FutureTask类进行包装,然后再在Thread中调用,查api你会看到FutureTask类既继承了Future类又继承了Runnabl类,同时Callabl类与另外两个的区别在于call方法有返回参数。