参考:http://lavasoft.blog.51cto.com/62575/99150
http://blog.csdn.net/baby_newstar/article/details/6783752
http://www.runoob.com/java/java-multithreading.html
1.操作系统中的进程和线程
进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程。比如在Windows系统中,一个运行的exe就是一个进程。
线程是指进程中的一个执行流程,一个进程中可以运行多个线程。比如java.exe进程中可以运行很多线程。线程总是属于某个进程,进程中的多个线程共享进程的内存。
2.java中的线程
一个Java应用总是从main()方法开始运行,mian()方法运行在一个线程内,它被称为主线程。
其他线程通过使用java.lang.Thread类或者java.lang.Runnable接口编写代码来定义、实例化和启动新线程。
线程又分用户线程和守护线程。只有通过设置setDaemon(true)的线程才是守护线程。用户线程的生命周期由该线程自定义,比如while(true)一直执行。守护线程的生命周期是由创造它的线程决定的,父线程死掉了,它也就立即死亡而不管是否有任务还没有执行。抽象的理解就是:守护线程是工蜂,蜂后死掉后也会跟着死掉。
3.线程的生命周期
- 新建状态:使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。
- 就绪状态:当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
- 运行状态:如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
- 阻塞状态:如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。
- 死亡状态:一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
- 如图:
4.创建一个线程
Java提供了两种创建线程方法:
- 通过实现Runable接口;
- 通过继承Thread类本身。
1、如果是扩展java.lang.Thread类的线程,则直接new即可。
Thread(Runnable target, String name)
Thread(ThreadGroup group, Runnable target)
Thread(ThreadGroup group, Runnable target, String name)
Thread(ThreadGroup group, Runnable target, String name, long stackSize)
4.1实现Runnable接口
package com.test.java.thread; /** * 学习线程 * Created by mrf on 2016/2/25. */ public class NewThread implements Runnable{ Thread thread; NewThread(){ //创建第二个新线程 thread = new Thread(this,"Demo Thread"); System.out.println("我是实现Runnable接口的类,我被创建了。而且我开始创建另一个线程(name,priority,groupname):"+thread); thread.start(); } //第二个线程入口 @Override public void run() { System.out.println("------------------我是实现Runnable接口的运行入口,我要开始运行了。-----------------------"); try { for (int i = 0; i < 5; i++) { System.out.println("Child Thread:"+i); //暂停线程 Thread.sleep(50); } } catch (InterruptedException e) { System.out.println("Child interrupted"); e.printStackTrace(); } System.out.println("---------------我这个线程就要运行结束了.------------------------"); } } class ThreadDemo{ public static void main(String[] args) { System.out.println("===========main线程开始运行。=============="); System.out.println("当前运行的是main线程:"+Thread.currentThread()); new NewThread();//创建一个新线程 try { for (int i =0; i<5; i++){ System.out.println("main thread:"+i); Thread.sleep(100); } } catch (InterruptedException e) { System.out.println("main thread interruped."); e.printStackTrace(); } System.out.println("==============main线程运行结束.================"); } }
运行结果:
===========main线程开始运行。==============
当前运行的是main线程:Thread[main,5,main]
我是实现Runnable接口的类,我被创建了。而且我开始创建另一个线程(name,priority,groupname):Thread[Demo Thread,5,main]
main thread:0
------------------我是实现Runnable接口的运行入口,我要开始运行了。-----------------------
Child Thread:0
Child Thread:1
Child Thread:2
main thread:1
Child Thread:3
Child Thread:4
main thread:2
---------------我这个线程就要运行结束了.------------------------
main thread:3
main thread:4
Disconnected from the target VM, address: '127.0.0.1:4625', transport: 'socket'
==============main线程运行结束.================
分析:
java程序从main线程开始,这是主线程。然后new NewThread(),创建了这个叫做NewThread的类,这个类的构造方法里面又调用了另一个线程,即从这里开始调用新线程了。main线程和新线程的优先级都为5,因此轮换使用cpu,所以才会出现交替打印的现象。
问题:上面的结果显示自线程运行结束后main线程才结束,那么子线程的生命周期和main有关吗?
答案是否定的,下面我将子线程的运行时间加长就会看到结果。
package com.test.java.thread; /** * 学习线程 * Created by mrf on 2016/2/25. */ public class NewThread implements Runnable{ Thread thread; NewThread(){ //创建第二个新线程 thread = new Thread(this,"Demo Thread"); System.out.println("我是实现Runnable接口的类,我被创建了。而且我开始创建另一个线程(name,priority,groupname):"+thread); thread.start(); } //第二个线程入口 @Override public void run() { System.out.println("------------------我是实现Runnable接口的运行入口,我要开始运行了。-----------------------"); try { for (int i = 0; i < 5; i++) { System.out.println("Child Thread:"+i); //暂停线程 Thread.sleep(500); } } catch (InterruptedException e) { System.out.println("Child interrupted"); e.printStackTrace(); } System.out.println("---------------我这个线程就要运行结束了.------------------------"); } } class ThreadDemo{ public static void main(String[] args) { System.out.println("===========main线程开始运行。=============="); System.out.println("当前运行的是main线程:"+Thread.currentThread()); new NewThread();//创建一个新线程 try { for (int i =0; i<5; i++){ System.out.println("main thread:"+i); Thread.sleep(100); } } catch (InterruptedException e) { System.out.println("main thread interruped."); e.printStackTrace(); } System.out.println("==============main线程运行结束.================"); } }
结果:
===========main线程开始运行。============== 当前运行的是main线程:Thread[main,5,main] 我是实现Runnable接口的类,我被创建了。而且我开始创建另一个线程(name,priority,groupname):Thread[Demo Thread,5,main] main thread:0 ------------------我是实现Runnable接口的运行入口,我要开始运行了。----------------------- Child Thread:0 main thread:1 main thread:2 main thread:3 main thread:4 ==============main线程运行结束.================ Child Thread:1 Child Thread:2 Child Thread:3 Child Thread:4 ---------------我这个线程就要运行结束了.------------------------
注意我将子线程的睡眠时间改成了500.这样,main线程执行完毕后子线程仍然继续执行。这个有点可以使我们在做业务代码时将不要紧的代码多线程异步执行,优化运行体验和效率。
4.2守护线程演示
还是上面的代码,我将子线程设置为守护线程,并且设置子线程运行时间>main线程,看是否main线程运行结束后守护线程直接死掉。这里注意,setDaemon一定要放在start前,因为会判断isAlive,如果活着就不能设置为守护线程了,因为已经在运行了。
package com.test.java.thread; /** * 学习线程 * Created by mrf on 2016/2/25. */ public class NewThread implements Runnable{ Thread thread; NewThread(){ //创建第二个新线程 thread = new Thread(this,"Demo Thread"); System.out.println("我是实现Runnable接口的类,我被创建了。而且我开始创建另一个线程(name,priority,groupname):"+thread); thread.setDaemon(true); thread.start(); } //第二个线程入口 @Override public void run() { System.out.println("------------------我是实现Runnable接口的运行入口,我要开始运行了。-----------------------"); try { for (int i = 0; i < 5; i++) { System.out.println("Child Thread:"+i); //暂停线程 Thread.sleep(500); } } catch (InterruptedException e) { System.out.println("Child interrupted"); e.printStackTrace(); } System.out.println("---------------我这个线程就要运行结束了.------------------------"); } } class ThreadDemo{ public static void main(String[] args) { System.out.println("===========main线程开始运行。=============="); System.out.println("当前运行的是main线程:"+Thread.currentThread()); new NewThread();//创建一个新线程 try { for (int i =0; i<5; i++){ System.out.println("main thread:"+i); Thread.sleep(100); } } catch (InterruptedException e) { System.out.println("main thread interruped."); e.printStackTrace(); } System.out.println("==============main线程运行结束.================"); } }
运行结果:
===========main线程开始运行。============== 当前运行的是main线程:Thread[main,5,main] 我是实现Runnable接口的类,我被创建了。而且我开始创建另一个线程(name,priority,groupname):Thread[Demo Thread,5,main] main thread:0 ------------------我是实现Runnable接口的运行入口,我要开始运行了。----------------------- Child Thread:0 main thread:1 main thread:2 main thread:3 main thread:4 Child Thread:1 Disconnected from the target VM, address: '127.0.0.1:6928', transport: 'socket' ==============main线程运行结束.================
和上一个测试对比相当明显,child thread仅仅打印了一次就结束了,说明它死掉了,说明创建他的main线程死掉后,她跟着死掉。
4.3继承Thread
通过继承Thread,重写run方法来实现多线程。下面是实例,另外上面的代码写的不太清楚,start方法应该放外面来调用比较好。
package com.test.java.thread; /** * 多线程学习---继承Thread * Created by mrf on 2016/2/25. */ public class NewThreadExtend extends Thread{ NewThreadExtend(){ //创建第二个线程 super("Demo Thread."); System.out.println("子线程:"+this); } //通过重写run方法来实现线程业务逻辑 public void run(){ System.out.println("-------我是子线程,我开始运行----------------"); try { for (int i = 0; i < 5; i++) { System.out.println("子线程:"+i); Thread.sleep(50); } } catch (InterruptedException e) { System.out.println("子线程interrupted"); e.printStackTrace(); } System.out.println("-------我是子线程,我结束运行----------------"); } } class ExtendThread{ public static void main(String[] args) { System.out.println("========我是main线程,我开始运行:"+Thread.currentThread()); NewThreadExtend newThreadExtend = new NewThreadExtend(); newThreadExtend.start(); try { for (int i = 0; i < 5; i++) { System.out.println("main线程:"+i); Thread.sleep(100); } } catch (InterruptedException e) { System.out.println("main线程interrupted."); e.printStackTrace(); } System.out.println("========main线程运行结束====================="); } }
运行结果:
========我是main线程,我开始运行:Thread[main,5,main] 子线程:Thread[Demo Thread.,5,main] main线程:0 -------我是子线程,我开始运行---------------- 子线程:0 子线程:1 main线程:1 子线程:2 子线程:3 子线程:4 main线程:2 -------我是子线程,我结束运行---------------- main线程:3 main线程:4 Disconnected from the target VM, address: '127.0.0.1:11051', transport: 'socket' ========main线程运行结束=====================
5.Thread的方法
方法就不在这里赘述了,普通方法必须由thread对象来调用,比如start,静态方法可以由Thread来调用。这里再说下start,start执行的是run方法,初学的时候会很纳闷,因为没有深入源码。好吧,我也没深入,但大概看到start是加入group,然后调用start0,start0是个native method。啥是native method?看到介绍说是调用其他语言实现的代码,到这里就是jvm干的事情了,我们不知道做了什么。不过可以猜测是和操作系统交互,获取进程资源之类的吧,然后应该回调run方法。总之,要理解start就是在执行run方法,不过start看起来像是代理而已。
public void start()
使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
6.线程的几个概念
在多线程编程时,你需要了解以下几个概念:
- 线程同步
- 线程间通信
- 线程死锁
- 线程控制:挂起、停止和恢复
7.线程同步和锁下次学习