一、线程的基本概念
在操作系统中两个比较容易混淆的概念是进程(process)和线程(thread)。操作系统中的进程是资源的组织单位。进程有一个包含了程序内容和数据的地址空间,以及其它的资源,包括打开的文件、子进程和信号处理器等。不同进程的地址空间是互相隔离的。而线程表示的是程序的执行流程,是CPU调度的基本单位。线程有自己的程序计数器、寄存器、栈和帧等。引入线程的动机在于操作系统中阻塞式I/O的存在。当一个线程所执行的I/O被阻塞的时候,同一进程中的其它线程可以使用CPU来进行计算。这样的话,就提高了应用的执行效率。线程的概念在主流的操作系统和编程语言中都得到了支持。
一部分的Java程序是单线程的。程序的机器指令按照程序中给定的顺序依次执行。Java语言提供了java.lang.Thread类来为线程提供抽象。有两种方式创建一个新的线程:一种是继承java.lang.Thread类并覆写其中的run()方法,另外一种则是在创建java.lang.Thread类的对象的时候,在构造函数中提供一个实现了java.lang.Runnable接口的类的对象。在得到了java.lang.Thread类的对象之后,通过调用其start()方法就可以启动这个线程的执行。
一个线程被创建成功并启动之后,可以处在不同的状态中。这个线程可能正在占用CPU时间运行;也可能处在就绪状态,等待被调度执行;还可能阻塞在某个资源或是事件上。多个就绪状态的线程会竞争CPU时间以获得被执行的机会,而CPU则采用某种算法来调度线程的执行。不同线程的运行顺序是不确定的,多线程程序中的逻辑不能依赖于CPU的调度算法。
Windows操作系统是支持多线程的,它可以同时执行很多个线程,也支持多进程,因此Windows操作系统是支持多线程多进程的操作系统。Linux和Uinux也是支持多线程和多进程的操作系统。DOS就不是支持多线程和多进程了,它只支持单进程,在同一个时间点只能有一个进程在执行,这就叫单线程。
CPU难道真的很神通广大,能够同时执行那么多程序吗?不是的,CPU的执行是这样的:CPU的速度很快,一秒钟可以算好几亿次,因此CPU把自己的时间分成一个个小时间片,我这个时间片执行你一会,下一个时间片执行他一会,再下一个时间片又执行其他人一会,虽然有几十个线程,但一样可以在很短的时间内把他们通通都执行一遍,但对我们人来说,CPU的执行速度太快了,因此看起来就像是在同时执行一样,但实际上在一个时间点上。
那什么才是真正的多线程?如果你的机器是双CPU,或者是双核,这确确实实是多线程。
二、线程的创建和启动
在JAVA里面,JAVA的线程是通过java.lang.Thread类来实现的,每一个Thread对象代表一个新的线程。创建一个新线程出来有两种方法:第一个是从Thread类继承,另一个是实现接口runnable。VM启动时会有一个由主方法(public static void main())所定义的线程,这个线程叫主线程。可以通过创建Thread的实例来创建新的线程。你只要new一个Thread对象,一个新的线程也就出现了。每个线程都是通过某个特定的Thread对象所对应的方法run()来完成其操作的,方法run()称为线程体。
Demo1:实现Runnable接口创建和启动新线程package com.liangdianshui;
public class ThreadRunable {
public static void main(String args[]) {
MyRunable mRunable = new MyRunable();
// r1.run();//这个称为方法调用,方法调用的执行是等run()方法执行完之后才会继续执行main()方法
new Thread(mRunable).start(); //启动一个线程
for (int i = 0; i < 10; i++) {
System.out.println("main:" + i);
}
}
}
/**
* 自定义一个类,实现Runable接口 打印1-10
*
*/
class MyRunable implements Runnable {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("MyRunable:" + i);
}
}
}
运行结果:
在我的机子上运行的结果是这样,在你们的机子上运行的结果也可能不一样的!他的运行原理是这样的:
package com.liangdianshui;
public class ThreadExtendThread {
public static void main(String[] args) {
MyThread mThread = new MyThread();
mThread.start(); // 开启一个新的线程
for (int i = 0; i < 10; i++) {
System.out.println("main:" + i);
}
}
}
/**
* 继承Thread
*
*/
class MyThread extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 10; i++) {
System.out.println("MyThread: " + i);
}
}
}
使用实现Runnable接口和继承Thread类这两种开辟新线程的方法,在选择上:应该优先选择实现Runnable接口这种方式去开辟一个新的线程。因为接口的实现可以实现多个,而类的继承只能是单继承。因此在开辟新线程时能够使用Runnable接口就尽量不要使用从Thread类继承的方式来开辟新的线程。
通过上面的例子,我们可以这样理解线程,主线程就是一个部门的主管,当遇到比较耗时的工作的时候,他可以安排部门的其他员工帮忙做,然后他自己继续做下面的事情,最后只要知道员工完成的结果就行了~!
三、线程状态转换Demo:
package com.liangdianshui;
import java.util.Date;
public class ThreadRunable {
public static void main(String args[]) {
MyRunable mRunable = new MyRunable();
// r1.run();//这个称为方法调用,方法调用的执行是等run()方法执行完之后才会继续执行main()方法
new Thread(mRunable).start(); // 启动一个线程
for (int i = 0; i < 10; i++) {
System.out.println("main:" + i);
if (i == 5) {
try {
System.out.println(new Date().toLocaleString()); //打印当前时间
Thread.sleep(5000); // 让主线程休眠5秒
System.out.println(new Date().toLocaleString()); //打印当前时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
/**
* 自定义一个类,实现Runable接口 打印1-10
*
*/
class MyRunable implements Runnable {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("MyRunable:" + i);
}
}
}
运行结果:
Demo:
package com.liangdianshui;
import java.util.Date;
public class ThreadRunable {
public static void main(String args[]) {
MyRunable mRunable = new MyRunable();
// r1.run();//这个称为方法调用,方法调用的执行是等run()方法执行完之后才会继续执行main()方法
Thread mThread = new Thread(mRunable);
mThread.start(); // 启动一个线程
for (int i = 0; i < 10; i++) {
System.out.println("main:" + i);
}
try {
mThread.join(); //把子线程合并到主线程,等于是调用子线程的方法
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 自定义一个类,实现Runable接口 打印1-10
*
*/
class MyRunable implements Runnable {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("MyRunable:" + i);
if (i == 2) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
运行结果:
join方法你可以理解为是直接停止了那个子线程,然后在主线程中继续运行!
Demo:
package com.liangdianshui;
import java.util.Date;
public class ThreadRunable {
public static void main(String args[]) {
MyThread1 t1 = new MyThread1("t1");
MyThread1 t2 = new MyThread1("t2");
t1.start();
t2.start();
}
}
class MyThread1 extends Thread {
private String strName;
public MyThread1(String str) {
strName = str;
}
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(strName + ":" + i);
try {
Thread.sleep(1000); //为了更好的显示效果,增加延迟
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (i % 2 == 0) {
yield();
}
}
}
}
四、线程的优先级别
五、线程同步
六、总结
这篇博文首先简单的介绍了什么是进程和线程,知道单个CPU不是真正意义上的多进程,CPU把自己的时间分成一个个小时间片,我这个时间片执行你一会,下一个时间片执行他一会,加上CPU的运行速度很快,因此像同时运行多个程序一样,然后了解实现Thread的两种方法,一种是实现Runable接口,一种是继承Thread类,最后讲了线程的一些方法!