每日一记–多线程(续)
今天看了看书 更了解了多线程。
Java中的线程
程序:程序是一段静态的代码,他是应用软件执行的蓝本。
进程:进程是程序的一次动态执行过程,它对应了从代码加载、执行至完毕的一个完整过程,这个过程也是进程本身从产生、发展至消亡的过程。
线程:线程是比进程更小的执行单位。线程之间可以共享相同的内存单元(即内存中的堆区 包括代码和数据)并利用这些共享单元来实现数据交换、实时通信与必要的同步操作。
进程与线程的关系:一个进程在其执行的过程中可以产生多个线程,形成多条执行线索,每条线索,即没有线程都有它自身的产生、存在与消亡的过程,也是一个动态的概念。
多线程原理:我们知道,操作系统分时管理各个进程,按时间片轮流执行每个进程。Java的多线程就是在操作系统每次分时给Java程序一个时间片的cpu时间内,在若干个独立的可控制的线程之间进行切换。如果机器能有多个cpu处理器,那么JVM就能充分利用这些cpu,使得Java程序在同一时刻能获得多个时间片,Java程序就可以获得真实的线程并发执行效果。
主线程:每个Java程序都有一个默认的主线程,这个线程就是当执行主类main方法开始执行时,当JVM加载代码发现main方法之后,就会启动一个线程,这个线程就被称为**“主线程”,该线程负责执行main方法,如果在main方法的执行过程中再创建的线程 就称为其他线程**。如果main方法中没有创建其他线程,那么当main方法执行完最后一个语句,JVM就会结束Java应用程序。如果main方法中有创建了其他线程,那么JVM就要在主线程和其他线程之间轮流切换,以保证每个线程都能有机会使用cpu资源。
注:main方法即使执行完最后的语句(主线程结束),JVM也不会结束程序,JVM会等待程序中其他线程都结束才会结束Java应用程序。
线程的状态与生命周期
新建状态:当一个Thread类或其子类的对象被声明并创建时,新生的线程处于NEW状态。此时它已经有了相应的内存空间和其他资源,即此时线程已准备但尚未启动(未调用**start()**方法),此时JVM管理的线程中还没有这个线程。
可运行状态:当处于NEW状态的线程调用了Thread类提供的**start()**方法,此时线程就会进入RUNNABLE状态,称为可运行状态,此时JVM就会知道有一个新的线程在等待切换了。
中断状态:BOLOCKED、WAITING、TIMED_WAITING状态都属于中断状态,当中断的线程重新进入RUNNABLE状态后,一旦JVM将cpu使用权切换给该线程,run()方法将从中断出继续执行。
- JVM将从cpu资源从当前RUNNABLE线程切换给其他线程,是本线程让出cpu的使用权进入BLOCKED状态,进入BOLOCKED状态的线程必须等待JVM解除它的BLOCKED状态,再次进入RUNNABLE状态,并从中断处继续执行。
- 线程使用cpu资源期间,执行了sleep(int millsecond),使得当前线程进入休眠状态。sleep(int millsecond)方法是Thread类的类方法,一个线程一旦执行了这个方法,就立刻让出cpu使用权,使当前线程处于TIMED_WAITING状态。经过至多参数millsecond指定的毫秒数之后,该线程再次进入RUNNABLE状态。
- 线程使用cpu资源期间,执行了wait()方法,使得当前线程进入WAITING状态。该状态的线程不会主动进入RUNNABLE状态,必须由其他线程调用**notify()**方法通知它,使得它进入RUNNABLE状态。
死亡状态:当一个线程执行完run()方法,该线程就完成了它的全部工作进入TERMINATED状态。
注:只有处于NEW状态的线程才可以调用start()方法,处于其他状态的线程都不可以调用该方法,否则将会触发IllegalThreadStateException异常
例:用继承Thread的子类创建两个线程,这两个线程在控制台分别输出五句“老虎”和“小猫”;主线程控制台输出6句主人。
代码:
package DEVIL.多线程;
public class E_06 {
public static void main(String[] args) {
Cat t1 = new Cat();
Tiger t2 = new Tiger();
System.out.println("Tiger线程的状态"+ t1.getState());
System.out.print("Cat线程的状态"+ t2.getState());
t1.start();
t2.start();
for (int i = 1; i <= 6; i++) {
System.out.printf("\n%s","Tiger线程的状态"+ t1.getState());
System.out.printf("\n%s","Cat线程的状态"+t2.getState());
System.out.printf("\n%s","主人"+i);
}
System.out.println("\nTiger线程的状态"+ t1.getState());
System.out.println("Cat线程的状态"+ t2.getState());
}
}
class Cat extends Thread{
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.print("|小猫"+i);
}
System.out.println();
}
}
class Tiger extends Thread{
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.print("|老虎"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println();
}
}
控制台输出
Tiger线程的状态NEW
Cat线程的状态NEW
Tiger线程的状态RUNNABLE|老虎1|小猫1|小猫2|小猫3|小猫4|小猫5
Cat线程的状态BLOCKED
主人1
Tiger线程的状态TERMINATED
Cat线程的状态TIMED_WAITING
主人2
Tiger线程的状态TERMINATED
Cat线程的状态TIMED_WAITING
主人3
Tiger线程的状态TERMINATED
Cat线程的状态TIMED_WAITING
主人4
Tiger线程的状态TERMINATED
Cat线程的状态TIMED_WAITING
主人5
Tiger线程的状态TERMINATED
Cat线程的状态TIMED_WAITING
主人6
Tiger线程的状态TERMINATED
Cat线程的状态TIMED_WAITING
|老虎2|老虎3|老虎4|老虎5
注:程序在不同的计算机运行或在同一台计算机上反复运行的结果不尽相同,输出结果依赖于当前cpu使用情况。当其他线程的run()方法都结束了,线程进入死亡状态,只留下一个线程时,JVM不再将cpu资源切给主线程,等到最后一个线程也结束了,JVM就将java程序退出虚拟机,即java程序的结束。
线程的调度与优先级
处于就绪状态的线程首先进入就绪队列排队等候cpu资源,同一时刻再就绪队列中的线程可能由多个。Java虚拟机(JVM)中的线程调度器负责管理线程,调度器把线程的的优先级分为了10个级别,分别用Thread类中的类常量表示。每个Java线程的优先级都为常数1~10,即Thread.MIN_PRIORITY和Thread.MAX_PRIORITY之间。如果没有明确第设置线程的优先级别,每个线程的优先级都默认为常数5,即Thread.NORM_PRIORITY。
线程的优先级可以通过setPriority(int grade)方法调整,该方法需要一个int参数代表设置线程的等级,如果该参数不在1~10的范围内,那么setPriority便会产生一个IllegalArgumentException异常。如果参数正常该方法会返回所设置的线程的优先级。( 注:有些操作系统只能识别3个级别,即1、5和10。)
java线程调度器的任务就是使高优先级的线程能始终运行,一旦时间片有空闲,则使具有同等优先级的线程以轮流D这四个线程,且A和B的级别高于C和D,那么,Java调度器首先以轮流的方式执行A和B,一直等到A、B都执行完毕进入死亡状态,才会再C、D之间轮流切换。)
注:在实际编程时,不提倡使用线程的优先级来保证算法的正确执行。如果要编写正确、跨平台的多线程代码,必须假设线程在任何时刻都有可能被剥夺cpu资源的使用权
Java程序内存的简单分析
- 栈:一般来说,基本数据类型直接在栈中分配空间,局部变量(在方法代码段中定义的变量)也在栈中直接分配空间,当局部变量所在方法执行完成之后该空间便立刻被JVM回收,还有一种是引用数据类型,即我们通常所说的需要用关键字new创建出来的对象所对应的引用也是在栈空间中,此时,JVM在栈空间中给对象引用分配了一个地址空间(相当于一个门牌号,通过这个门牌号就可以找到你家),在堆空间中给该引用的对象分配一个空间,栈空间中的地址引用指向了堆空间中的对象区(通过门牌号找住址);
- 堆:一般用来存放用关键字new出来的数据。
–end–
今天还是摸索学习方法的一天呢。
|