### 一、简介
> 多线程是程序中有多个线程流在同时调度资源。线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),一个线程可以创建和撤销另一个线程。
### 二、线程和进程
1.进程是程序运行的实例,每一个进程都有自己的内存空间,包含内容和数据,不同进程间有相互独立的地址空间
2.线程是CPU调度的基本单位,每一个线程都有顺序执行的,线程有共享资源和锁机制。
3.两者区别和联系
(1) 划分尺度:线程更小,所以多线程程序并发性更高;
(2) 资源分配:进程是资源分配的基本单位,同一进程内多个线程共享其资源;
(3) 地址空间:进程拥有独立的地址空间,同一进程内多个线程共享其资源;
(4) 处理器调度:线程是处理器调度的基本单位;
(5) 执行:每个线程都有一个程序运行的入口,顺序执行序列和程序的出口,但线程不能单独执行,必须组成进程,
一个进程至少有一个主线程。简而言之,一个程序至少有一个进程,一个进程至少有一个线程
#### 三、线程的创建和启动
线程创建有2中方式,一是实现Runnable接口,实现run()方法,然后创建一个Thread对象,将
而是继承Thread类,实现run方法。两者都需要调用start()方法来启动线程。下面是两者方法的程序实现:
1.继承Thread方式
~~~
public class Demo1 extends Thread {
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+i);
}
}
}
~~~
2.实现Runnable接口
~~~
public class Demo2 implements Runnable {
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+i);
}
}
}
~~~
### 四、线程的状态
线程运行时也有它的生命周期,线程会要经历开始(等待),运行,挂起(阻塞)和终止四种不同的状态,且四种状态可以由Thread来自由控制,下面给出Thread类控制各个状态的方法
> 1.线程开始 start()/run();
2.线程挂起和唤醒 resume()/suspend()-已过时 sleep();
3.线程终止 stop()不建议使用 interupt();
4.其他与线程状态相关的方法
isAlive():判断线程的状态是否还活着
join():调用某线程的该方法,将当前线程与该线程“合并”,即等待该线程结束,再恢复当前线程的运行;
yield():线程的让步,即让出当前线程的资源给其他线程使用。
状态图如下:
![这里写图片描述](https://box.kancloud.cn/2016-04-13_570df38a5a3dd.jpg "")
从图中可看出线程在建立后并不马上执行run方法中的代码,而是处于等待状态。线程处于等待状态时,可以通过Thread类的方法来设置线程不各种属性,如线程的优先级(setPriority)、线程名(setName)和线程的类型(setDaemon)等。
- 当调用start方法后,线程开始执行run方法中的代码。线程进入运行状态。可以通过Thread类的isAlive方法来判断线程是否处于运行状态。
- 当线程处于运行状态时,isAlive返回true,
- 当isAlive返回false时,可能线程处于等待状态,也可能处于停止状态。
***注意:***一但线程开始执行run方法,就会一直到这个run方法执行完成这个线程才退出。但在线程执行的过程中,可以通过两个方法使线程暂时停止执行。这两个方法是suspend和sleep。
在使用suspend挂起线程后,可以通过resume方法唤醒线程。而使用sleep使线程休眠后,只能在设定的时间后使线程处于就绪状态(在线程休眠结束后,线程不一定会马上执行,只是进入了就绪状态,等待着系统进行调度)。
**在使用sleep方法时有两点需要注意:**
1. sleep方法有两个重载形式,其中一个重载形式不仅可以设毫秒,而且还可以设纳秒(1,000,000纳秒等于1毫秒)。但大多数操作系统平台上的Java虚拟机都无法精确到纳秒,因此,如果对sleep设置了纳秒,Java虚拟机将取最接近这个值的毫秒。
1. 在使用sleep方法时必须使用throws或try{…}catch{…}。因为run方法无法使用throws,所以只能使用try{…}catch{…}。当在线程休眠的过程中,使用interrupt方法中断线程时sleep会抛出一个InterruptedException异常。sleep方法的定义如下:
publicstaticvoid sleep(long millis) throws InterruptedException
publicstaticvoid sleep(long millis, int nanos) throws
InterruptedException
下面的举例为线程的合并的实现:
#### A线程:
~~~
public class DemoA extends Thread {
public DemoA(String name){
super(name);
}
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"-"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
~~~
#### B线程:
~~~
public class DemoB extends Thread {
private Thread a;
public DemoB(String name,Thread a){
super(name);
this.a=a;
}
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"-"+i);
try {
Thread.sleep(1000);
if(i==4){
a.join();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
~~~
### 五、线程的属性
线程的属性包括线程的优先级,守护线程等,明白线程的属性的作用,可以更灵活的设置线程的执行状态。
#### 1.线程的优先级
每一个线程都会对应一个优先级,默认情况下,新创建的线程会继承他父类的优先级。可以利用setPriority方法来修改线程的优先级的高低。修改的范围可以使MIN_PRIORITY和MAX_PRIORITY之间的任意级别
下面为线程优先级设置的实例:
***定义的线程类:***
~~~
public class Demo3 implements Runnable {
@Override
public void run() {
for(int i=0;i<20;i++){
System.out.println(Thread.currentThread().getName()+"-"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
~~~
***线程优先级的设置:***
~~~
//最小级别
Thread t1=new Thread(new Demo3());
t1.setPriority(Thread.MIN_PRIORITY);
t1.start();
//正常
Thread t2 =new Thread(new Demo3());
t2.setPriority(Thread.NORM_PRIORITY);
t2.start();
//最大级别
Thread t3 =new Thread(new Demo3());
t3.setPriority(Thread.NORM_PRIORITY);
t3.start();
}
~~~
注意:在使用线程优先级时,应避免常犯的一个错误,假如高优先级的线程处于非活跃状态,低优先级的线程也不可能会执行,而资源调度线程时会在高优先级的线程中选择,这会是低优先级的线程饿死。
##### 2.守护线程
一般线程要转换为守护线程,可通过setDaemon(true);来设置,守护线程不会去访问固有资源,如文件,数据库等。其作用是为其他线程提供服务,如计时器的例子,守护线程可定时发送
信号给实现计时的线程。
#### 3.未捕获异常的处理器
线程实现run方法时不会抛出可被检测的异常,而抛出的异常不能被检测到会导致线程终止,从而是程序死亡。
java提供了一个未捕获异常的处理器,该处理器为Thread.UncaughTExceptionHandler接口的类。从JSE5.0后,提供了setUncaughTExceptionHandler方法为线程安装处理器.