目录
一.进程:
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位
线程:
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源
线程状态
新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();
就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;
运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就 绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;
阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:
1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
2.同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
3.其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
二.多线程创建方式
1.继承java.lang.Thread类
package Thread;
//1.创建一个继承于Thread的子类
class SubThread extends Thread {
//2.重写Thread类的run()方法.方法内实现此子线程要完成的功能
public void run() {
for(int i=0;i<100;i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
public class TestThread {
public static void main(String[] args) {
//3.创建子类的对象
SubThread st1=new SubThread();
SubThread st2=new SubThread();
//4.调用线程的start():启动此线程;调用相应的run()方法
st1.start();
st2.start();
}
}
** 程序启动运行main时候,java虚拟机启动一个进程,主线程main在main()调用时候被创建。随着调用SubThread的两个对象start方法,另外两个线程也启动了,这样,整个应用就在多线程下运行。
注:start()方法的调用后并不是立即执行多线程代码,而是使得该线程变为可运行态(Runnable),什么时候运行是由操作系决定的。
多线程程序是乱序执行。因此,只有乱序执行的代码才有必要设计为多线程。所有的多线程代码行顺序都是不确定的,每次执行的结果都是随机的。
一个线程start方法只能调用一次,重复调用的话,会出现java.lang.IllegalThreadStateException异常。
要想启动一个多线程,必须调用start()
2.实现java.lang.Runnable接口
package Thread;
//1.创建一个实现了Runnable接口的类
class PrintNum implements Runnable{
//2.实现接口的抽象方法
public void run() {
for(int i=0;i<100;i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
public class TestThread2 {
public static void main(String[] args) {
//3.创建一个Runnable接口实现类的对象
PrintNum p=new PrintNum();
//4.将此对象作为形参传递给Thread类的构造器中,创建Thread类的对象,此对象即为一个线程
Thread t1=new Thread(p);
//5.调用start()方法:启动线程并执行run()
t1.start();//启动线程;执行Thread对象生成时构造器形参的对象的run()方法。
//再创建一个线程
Thread t2=new Thread(p);
t2.start();
}
}
** PrintNum类通过实现Runnable接口,使得该类有了多线程类的特征。run()方法是多线程程序的一个约定。所有的多线程代码都在run方法里面。Thread类实际上也是实现了Runnable接口的类。
实际上所有的多线程代码都是通过运行Thread的start()方法来运行的。因此,不管是扩展Thread类还是实现Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的,熟悉Thread类的API是进行多线程编程的基础。
3.Thread和Runnable区别
实现Runnable接口比继承Thread类所具有的优势:
1):适合多个相同的程序代码的线程去处理同一个资源
2):可以避免java中的单继承的限制
3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
4):线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类
!----main方法也是一个线程,java中线程都是同时启动,什么时候执行、哪个执行,取决于哪个获取CPU资源
!----java程序启动,最少启动两个线程,一个main,一个垃圾回收线程。
三.线程调度
1.线程优先级:java线程有优先级,优先级高的有较大几率获取CPU执行权。
线程优先级取值为1~10整数,数值越大优先级越高,有三个静态常量
static int MAX_PRIORITY 线程可以具有的最高优先级,取值为10。
static int MIN_PRIORITY 线程可以具有的最低优先级,取值为1。
static int NORM_PRIORITY 分配给线程的默认优先级,取值为5。
Thread类的setPriority()、getPriority()可以设置、获取线程的优先级值。
线程优先级具有继承关系,比如A线程中创建了B线程,那么B将和A具有相同的优先级。
2.线程睡眠:Thread.sleep(long millis)方法使线程转到阻塞状态。millis设置时间,单位为毫秒。睡眠结束后转为就绪(Runnable)状态。
3.线程等待:Object类的wait()方法导致线程等待阻塞,直到其他线程调用次对象的notify()方法或notifyall()方法唤醒,这两个唤醒方法也是Object类中的方法,行为等同于wait(0)。
4.线程让步:Thread.yield()方法使当前线程从运行状态变为就绪状态,让其他或者自己的线程执行(也就是谁先抢到谁执行)。
5.线程加入:在当前线程中调用另一个线程的join()方法,则当前线程转入阻塞状态,直到另一个进程运行结束,当前线程再由阻塞转为就绪状态。
6.线程唤醒:Object类的notify()、notifyall()方法。notify()唤醒任意一个等待线程,notifyall()唤醒所有等待线程。