.进程&线程(&java.lang.Thread)详解
Posted ikun
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了.进程&线程(&java.lang.Thread)详解相关的知识,希望对你有一定的参考价值。
一.进程与线程
进程
我们在进行操作电脑的时候,通常会打开浏览器,通讯工具等应用程序,这个时候CPU通过作业调度在内存中就会分配一些空间让它们处于宏观上的运行状态(处于可以被CPU执行的状态),而这部分空间资源就可以说是相应的进程占据的,很显然运行不同的程序需要不同的进程,在内存中也为它们分配独立,无共享的区域。静态描述进程的是PCB快(数据结构集),它是系统感知进程存在的唯一实体,通常包含进程名(或者标识符),用户名(或者用户标识号),家族关系。
进程的状态
就绪态:进程获得了除cpu以外的其他资源,进入抢占cpu的队列中
执行态:cpu正在执行该进程
阻塞态:进程继续执行的某个事件条件(如I/O设备)尚未具备
挂起态:处理机调度将进程调离内存进入外存
终止态:进程结束。(当我们撤销一个进程的时候,会通过PCB结构递归删除所有它的子进程,否者子进程可能处于无法控制的状态。)
(撤销进程的流程)
进程阻塞与挂起
阻塞详细流程图:
阻塞与挂起的区别:
1.首先阻塞是被动行为,当资源完备后,任然受cpu调度进入就绪状态,而挂起是主动行为,进程外存后,不受cpu作业调度,通过resume()函数进入就绪态。
2.发生原因不同:阻塞原因是针对这单个进程等待发生的条件不具备,而挂起原因内存资源有限暂时调离内存的过程。
线程
线程也称为轻型进程。线程的作用域在单个特定的线程内。一个进程内支持多个线程。这些线程的特点是:共享一定的内存区和数据。它们是彼此相互独立的,可以并发进行,宏观上看来是同时运行的,但实际上CPU在一个时刻只能处理一个子任务,分时操作系统采用了“时间片的策略”,即CPU将时间切割为时间片,然后将这些时间片分配给这些程序,只有获得时间片的程序才被执行。还有一个概念叫做主线程,它是进程初始化后自动启动的线程,如java里面的main。
进程与线程的区别
资源方面:一个进程包含多个线程,同类的多个线程共享一块内存和数据资源,而进程之间(包括子进程与父进程之间)是完全独立的,不存在资源的共享。
功能方面:进程是并发程序执行过程中资源分配的基本单元,线程是程序运行与调度的基本单元。
处理机的调度
(图片来自:http://www.cnblogs.com/dyllove98/p/3249243.html)
cpu调度发生的情况总结:
(1)创建进程:将进程读入内存中;
(2)进程终止:每当一个进程终止时,必须进行调度;从就绪队列中选择要执行的下一个进程;
(3)等待事件:运行进程由于等待I/O、信号量或其他原因不能继续,这时cpu对I/O设备发出启动指令,并将当前进程调入等待区;
(4)中断发生:当I/O设备完成时后会发出I/O中断,CPU完成中断响应,之后进行现场保护,中断服务,中断返回一系列调度;
(5)运到到时:当进程进程分配的时间片用完,此时选择新的进程投入运行。
(很明显挂起的进程不需要cpu进行作业调度)
二.进程的优先级
对于所有线程都具有它自己的优先级,优先级高的首先被CPU执行,当某个线程处于运行状态时,那些尚未拥有优先级的处于可运行状态的线程会自动获得优先级。还存在另一种情况,当某个线程在运行时,一个更高优先级的线程处于可运行状态,这是这个线程被立即执行,这称为先占式调度。
三.线程的开发方法
1.实现Runnable接口实现它的run()方法
2.继承Thread类,覆盖它的run()方法----Thread已经实现了Runnable接口实现了run方法,因此继承Thread的类是覆盖run()方法
(Runnable实现线程解决了java单一继承的局限性)
Thread/Runnable原理请看本人另一博客http://www.cnblogs.com/kundeg/p/6576094.html
四.线程池
背景:例如web服务器,数据库服务器,文件服务器等服务器端应用程序,都面临这来着客户端的请求,这些请求的整体特点是:单个任务处理的时间很短,但是请求的数目巨大
瓶颈:对于在进程内划分线程已经是个很大的改进,但仍然存在以下问题,对于每个到达服务器端的请求,单独为它们创建一个线程,将请求处理完后,再将线程销毁,然后线程创建与销毁的时间比处理请求的时间要多得多。同时线程过多,也会消耗内存。
改进:初始的时候便创建一个线程池(拥有i个线程),当请求到达时便拥有线程池中的某个线程(免去了创建时间),当线程池中的所有线程都被占据的时候,其他请求需要等待,占据线程的请求处理完后,线程又空余出来,新的请求可以进入。此处会有一定的等待时间,但和原来繁琐的线程创建和销毁相比,性能改进不少,同时线程池的构建也需要不断优化。
五.线程的状态以及运行控制
线程的运行状态
上述表明了线程在运行过程中的5种状态:
初始化状态:通过new实例化了一个线程对象,但该对象是空的并没有分配资源
可运行状态:通过start()方法,为该线程分配了除了CPU外的所需资源(进入队列,排队等待CPU),处于Runnable状态
运行状态:线程调度控制CPU处理一个Runnable线程,这个时候才实际上执行run()函数的代码
阻塞状态:正在运行的线程由于某些事件尚未具备(例如:输入输出)从而进入进入阻塞状态,来到阻塞池等待
挂起状态: A,通过调用sleep()方法使线程进入休眠状态,线程在指定时间内不会运行。
B,通过调用join()方法使线程挂起,如果某个线程在另一个线程t上调用t.join(),这个线程将被挂起,直到线程t执行完毕为止。
C,通过调用wait()方法使线程挂起,直到线程得到了notify()和notifyAll()消息,线程才会进入“可执行”状态。
(区别进程的挂起,线程不拥有资源,也不存在将资源放入外存中)
死亡状态:线程结束了(Dead),可由这些原因导致:1.该线程运行完毕。2.意外退出。3.父类线程控制其消亡。
线程的运行控制
1.线程的启动start()
public class test extends Thread{
public void run(){
while(true){
System.out.println("子线程运行中---");
}
}
}
public static void main(String args[]){
test t =new test();
t.start();
try{
Thread.sleep(2000);//父线程进入等待池等待
}
catch(Exception e ){
}
t.stop();
}//在上述实例中,通过main主线程创建了一个子线程,他们是相互独立,并发执行,但是父线程可以控制子线程的状态,例如stop()
2.休眠(sleep)与挂起(yield),join
相同:休眠与挂起都会使线程暂停处于等待状态
区别:休眠可以设定时间,并且期间任何优先级的线程都可以由可运行状态变为可执行状态,而挂起不可以设定时间,只有同等优先级的线程可以变成可执行状态
join立即执行方法
class test1 extends Thread{
static int count = 0;
public void run(){
for(int i=0;i<5;i++){
count++;
}
}
}
public class join {
public static void main(String ats[]){
test1 t = new test1() ;
try {
t.start();
t.join();//如果没有这条语句输出为0,当前输出为5
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(test1.count);
}
}
3.synchronized()
1.当两个并发线程访问同一个对象中的这个synchronized(this)同步代码块时,首先访问的线程获得对象锁,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块
public class synchronized1 implements Runnable {
public void run() {
synchronized(this) {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " 执行" );
}
}
}
public static void main(String[] args) {
synchronized1 t1 = new synchronized1();
Thread ta = new Thread(t1, "A");
Thread tb = new Thread(t1, "B");
ta.start();
tb.start();
}
}
//结果
B执行
B执行
B执行
B执行
B执行
A执行
A执行
A执行
A执行
A执行
2.当一个线程访问一个类对象的一个synchronized(this)同步代码块时,获得对象锁,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
public class synchoronized2 {
public void syn() {
synchronized(this) {
for(int i=0;i<5;i++){{
System.out.println(Thread.currentThread().getName() + "执行ing");
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
}
}
}
public void notSyn() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() +"执行ing");
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
}
public static void main(String[] args) {
synchoronized2 s2 = new synchoronized2();
Thread t1 = new Thread(new Runnable() {
public void run() {
s2.syn();
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
public void run() {
s2.notSyn();
}
}, "t2");
t1.start();
t2.start();
}
}
//结果
t2执行ing
t1执行ing
t2执行ing
t1执行ing
t1执行ing
t2执行ing
t2执行ing
t1执行ing
t1执行ing
t2执行ing
3.当一个线程访问一个对象的一个synchronized(this)同步代码块时,获得对象锁,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞
public class synchronized3 {
public void syn1() {
synchronized (this) {
for(int i=0;i<5;i++) {
System.out.println(Thread.currentThread().getName() + "执行ing");
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
}
}
public void syn2() {
//synchronized修饰方法,结果一样
synchronized (this) {
for(int i=0;i<5;i++) {
System.out.println(Thread.currentThread().getName() + "执行ing");
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
}
}
public static void main(String[] args) {
synchronized3 s = new synchronized3();
Thread t1 = new Thread(new Runnable() {
public void run() {
s.syn1();
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
public void run() {
s.syn2();
}
}, "t2");
t1.start();
t2.start();
}
}
//结果
t1执行ing
t1执行ing
t1执行ing
t1执行ing
t1执行ing
t2执行ing
t2执行ing
t2执行ing
t2执行ing
t2执行ing
4.区别对象锁和类锁:对象锁和类锁互不干扰,一个线程可以同时具有对象锁和类锁
public class synchronized4 {
public static instancet i = new instancet();
public static instancet getI() {
return i;
}
public static void main(String args[]) {
Thread t1 = new Thread(new tt(), "t1");
Thread t2 = new Thread(new tt(), "t2");
Thread t3 = new Thread(new tt(), "t3");
Thread t4 = new Thread(new tt(), "t4");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class tt implements Runnable {
public void run() {
if (Thread.currentThread().getName().equals("t1")) {
synchronized4.getI().getClassLock(Thread.currentThread().getName());
} else if (Thread.currentThread().getName().equals("t2")) {
synchronized4.getI().getObjectLock(Thread.currentThread().getName());
} else if (Thread.currentThread().getName().equals("t3")) {
synchronized4.getI().getClassLock(Thread.currentThread().getName());
} else {
synchronized4.getI().getObjectLock(Thread.currentThread().getName());
}
}
}
class instancet {
public synchronized void getObjectLock(String s) {
System.out.println(s + "获得对象锁");
try {
Thread.sleep(2000);//线程进入休眠,但仍然具有对象锁
} catch (Exception e) {
}
}
public synchronized static void getClassLock(String s) {
System.out.println(s + "获得类锁");
try {
Thread.sleep(4000);
} catch (Exception e) {
}
}
}
//结果
t2获得对象锁
t1获得类锁
t4获得对象锁
t3获得类锁
4.sleep(time),经过一段时间后自动唤醒,sleep后线程进入不可执行状态,但仍然具有锁,对其他线程有阻塞作用
上述代码已经说明sleep的作用
5.wait()与notify(),wait()线程进入休眠且放弃锁
public class synchronized7 {
public static void main(String[] args) {
new Thread(new Thread1()).start();
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
new Thread(new Thread2()).start();
}
private static class Thread1 implements Runnable{
@Override
public void run(){
synchronized (synchronized7.class) {
System.out.println("线程1正在执行ing");
try {System.out.println("线程1进入休眠");
synchronized7.class.wait();//休眠且放弃锁
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("线程1继续执行");
System.out.println("线程1结束"); }
}
}
private static class Thread2 implements Runnable{
@Override
public void run(){
synchronized (synchronized7.class) {
System.out.println("线程2正在执行ing");
synchronized7.class.notify();
try {
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("线程2继续执行");
System.out.println("线程2结束");
}
}
}
}
//结果
线程1正在执行ing
线程1进入休眠
线程2正在执行ing
线程2继续执行
线程2结束
线程1继续执行
线程1结束
6.wait()与sleep()区别
public class waitandsleep extends Thread{
static int number = 10;
public void firstMethod() throws Exception {
synchronized (this) {
number += 100;
System.out.println(number);
}
}
//下面是否加上static,又是一种同步锁,结果不一样
public synchronized void secondMethod() throws Exception {
/**用于区别wait和sleep
*1.sleep(在Thread的静态方法)的时候仍然占有机锁,释放cpu
*2.wait(Object的方法)的时候释放机锁,其他线程块可以进入,释放cpu
**/
//Thread.sleep(2000);//输出2100
this.wait(2000);//输出110
number *= 200;
}
public void run() {
try {
firstMethod();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
waitandsleep ws=new waitandsleep();
ws.start();
ws.secondMethod();
ps:纯手打,图片是拍的书上的,勉强看的清楚,感觉自己写博文一直有个弊端,试图在一篇文章里面囊括一个大的知识工厂,但现在发现能将一个点讲清楚就非常非常困难,所以这篇失败的博文,还将继续改善,fighting!!
更新+1+1+1
以上是关于.进程&线程(&java.lang.Thread)详解的主要内容,如果未能解决你的问题,请参考以下文章
JAVA多线程 & 同步关键词synchronized & ReadWriteLock读写文件
Java 线程--继承java.lang.Thread类实现线程