1)线程和进程的概念
进程 | 线程 | |
---|---|---|
一段正在运行的程序 | 程序运行中可以执行的多个任务叫线程 | |
进程是程序执行过程中资源分配和管理的基本单位 | 线程是cpu执行的最小单位 | |
有独立的内存空间,进程中的数据存放空间(堆空间和栈空间)是独立的 | 堆空间是共享的,栈空间是独立的 | |
线程是轻量级的进程 |
多线程作为一种多任务、并发的工作方式,其存在优势:
①进程之前不能共享内存,而线程之间共享内存(堆内存)则很简单。
②系统创建进程时需要为该进程重新分配系统资源,创建线程则代价小很多,因此实现多任务并发时,多线程效率更高。
③Java语言本身内置多线程功能的支持,而不是单纯第作为底层系统的调度方式从而简化了多线程编程
2)线程的创建
创建和启动线程,传统有两种方式
方式1):继承Thread类
方式2):实现Runnable接口
方式3):使用匿名内部类的方式来创建线程
线程类(java.lang.Thread): Thread类和Thread的子类才能被称为线程类。
方式1
1):定义一个类A继承于java.lang.Thread类.
2):在A类中覆盖Thread类中的run方法
3):我们在run方法中编写需要执行的操作—>run方法里是线程执行体.
4):在main方法(主线程)中,创建线程对象并启动线程.
class MusicThread extends java.lang.Thread{
@Override
public void run() {
for(int i=0;i<20;i++){
System.out.println("播放音乐"+i);
}
}
}
//方法一:继承Thread类
public class ExtendsThreadDemo {
public static void main(String[] args) {
for(int i=0;i<20;i++){
System.out.println("打游戏"+i);
if(i == 10){
//创建线程对象,并启动线程
MusicThread musicThread=new MusicThread();
musicThread.start();//启动线程,不能调用run方法
}
}
}
}
结果可以边打游戏边听歌
方式2
1):定义一个类A实现于java.lang.Runnable接口,注意A类不是线程类。
2):在A类中覆盖Runnable接口中的run方法。
3):我们在run方法中编写需要执行的操作—>run方法里的,线程执行体。
4):在main方法(线程)中,创建线程对象,并启动线程.
//播放音乐类
class MuslicRunnableImplements implements java.lang.Runnable{
@Override
public void run() {
for(int i=0;i<50;i++){
System.out.println("播放音乐"+i);
}
}
}
//方式二:实现Runnable接口
//打游戏主类
public class ImplementsRunnableDemo {
public static void main(String[] args) {
for(int i=0;i<50;i++){
System.out.println("打游戏"+i);
if(i == 10){
//创建线程对象,并启动线程
Runnable target= new MuslicRunnableImplements();
Thread t=new Thread(target);
t.start();
}
}
}
}
结果也可以边打游戏边听歌
***方式三***匿名内部类的方式
注意:只适用于一个类只使用一次的情况
public class AnonymousInnerClassDemo {
public static void main(String[] args){
//主进程:运行游戏
for(int i=0;i<50;i++){
System.out.println("打游戏"+i);
if(i == 10){
/**
* 使用类的形式的匿名内部类
*/
new Thread(){
@Override
public void run() {
for(int i=0;i<50;i++){
System.out.println("播放音乐"+i);
}
};
}.start();
}
}
}
}
结果也可以边打游戏边听歌
3)线程的生命周期及常用方法解析
线程生命周期的6种状态
1:新建状态(new):
使用new创建一 个线程对象,仅仅在堆中分配内存空间,在调用start方法之前.新建状态下,线程压根就没有启动,仅仅只是存在一个线程对象而已.
Thread t = new Thread();//此时t就属于新建状态
当新建状态下的线程对象调用了start方法此时从新建状态进入可运行状态.
线程对象的start方法只能调用一次,否则报错。
2:可运行状态/就绪状态(runnable):可运行线程状态正在Java虚拟机中执行,但它可能等待来自操作系统的其他资源
分成两种状态, ready和running。 分别表示就绪状态和运行状态。
就绪状态:线程对象调用start方法之后,等待JVM的调度(此时该线程并没有运行).
运行状态:线程对象获得JVM调度如果存在多个CPU,那么允许多个线程并行运行.
3:阻塞状态(blocked):
正在运行的线程因为某些原因放弃CPU,暂时停止运行,就会进入阻塞状态此时JVM不会给线程分配CPU,直到线程重新进入就绪状态,才有机会转到运行状态,阻塞状态只能先进入就绪状态,不能直接进入运行状态。
阻塞状态的两种情况:
1):当A线程处于运行过程时,试图获取同步 锁时,却被B线程获职.此时vM把当前A线程存到对象的锁池中,A线程进入阻塞状态。
2):当线程处于 运行过程时,发出了IO请求时,此时进入阻塞状态。
4:等待状态(waiting)(等待状态,只能被其他线程唤醒)此时使用的无参的wait方法。
1):当线程处于运行过程时,调用了wait()方法,此时JvM把当前线程存在对象等待池中。
2):当前线程执行了sleep()方法。
5:睡眠/计时等待状态(timed waiting)(使用了带参数的wait方法或者sleep方法)
1):当线程处于运行过程时,调用了wait(long time)方法此时JVM把当前线程存在对象等待池中。
2):当前线程执行了sleep(long time)方法。
6:终止状态(terminated):通常称为死亡状态,表示线程终止。
1):正常执行完run方法而退出。
2):遇到异常而退出。(遇到异常,程序就会中断)
有关线程的常用方法
1、start():线程状态会从New状态转换到Runnable状态,使该线程开始执行,然后获取Cpu之后进入Running状态然后Java 虚拟机调用该线程的 run 方法。
2、sleep():使得线程进入睡眠状态,暂停执行
●public static native void sleep(long millis)该线程睡眠时间为 millis 毫秒。
●public static void sleep(long millis, int nanos)该线程睡眠时间millis 毫秒 + nanos 纳秒
jdk1.5之后,jdk引入一个枚举TimeUnit,其对sleep方法做了封装,直接使用从而时间单位换算的步骤
3、join():在线程B中join某个线程A,会使得B线程进入等待,直到线程A结束生命周期,或者达到给定的时间,在这期间线程B会处于等待状态。
4、notify():唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。
5、notifyAll():唤醒在此对象监视器上等待的所有线程。
6、interrupt():线程中断方法。