一 、进程、线程与任务
1、进程与程序之间的关系
- 程序是一个视频文件夹,进程是视频文件夹中视频当中的其中之一
- 运行一个java程序的实质是启动一个Java虚拟机进程,也就是说一个运行的Java程序就是一个Java虚拟机进程
- 进程是程序向操作系统申请资源的基本单位
- 线程是进程中可独立执行的最小单位
- 一个进程可以包含多个线程
- 同一个进程中的所有线程共享该进程中的资源
- 线程索要完成的计算就被称为任务,特定的线程总是在执行者特定的任务【Java Web应用例外。一个Java Web服务器是一个进程,它可以同时运行多个Java Web应用】。任务代表线程所要完成的工作
2、什么是多线程编程
多线程编程类似于“和尚挑水:的故事,将打睡比作这些线程要完成的任务,那么我们不难发现线程可能会增加单位时间内完成的任务量,即提高程序的计算效率;但它也有可能降低程序的计算效率(类似三个和尚抬水没水喝)。
3、为什么要使用多线程
类似于读取日志有专门读取日志的线程,记录日志有专门记录日志的线程一样,特定的线程专门处理特定的业务,有利于提高效率。
二、Java线程API间介
Java标准库类java.lang.Thread 就是Java平台对线程的实现。Thread类或其子类的一个实例就是一个线程。
1、线程的创建、启动与运行
- 线程的任务处理逻辑可以在Thread类的run实例方法中直接实现或者通过该方法进行调用,因此run方法相当于线程的任务逻辑的入口方法,它由java虚拟机在运行相应线程时直接调用,而不是由应用代码进行调用。
- 启动线程start( )方法
- 启动一个线程的实质是请求Java虚拟机运行相应线程,而这个线程具体何时能够运行时由线程调度器决定的。因此,start方法调用结束并不意味着相应线程已经开始运行,可能稍后运行,可能永远不会被运行。
2、Thread类常用的构造器
Thread( )和Thread(Runnable target)
3、创建线程
- 一种:使用Thread()构造器,定义Thread类的子类,在该子类中覆盖run方法并在该方法中实现线程任务处理逻辑
public class WelcomeApp {
public static void main(String[] args) {
//创建线程
Thread welcomeThread=new WelcomeThread();
//开启线程
welcomeThread.start();
}
}
class WelcomeThread extends Thread{
@Override
public void run() {
//输出:当前线程的名字Thread-0
System.out.println("当前线程的名字"+Thread.currentThread().getName());
}
}
- 另一种:使用Thread(Runnable target)
public class WelcomeApp {
public static void main(String[] args) {
//创建线程
Thread welcomeThread=new Thread(new WelcomeTask());
//开启线程
welcomeThread.start();
}
}
class WelcomeTask implements Runnable{
//在该方法中实现线程的任务处理逻辑
@Override
public void run() {
//输出当前线程的线程名称---输出为:Thread-0
System.out.println(Thread.currentThread().getName());
}
}
- 不论是何种方式创建线程,一旦线程的run方法执行结束,相应的线程的运行也就结束了(所谓的结束是:不论是正常结束还代码中抛出异常而导致的中止)
- Java虚拟机会为每个线程分配调用栈所需的内存空间
- Java平台中的每个线程可能还有一个内核线程(由操作系统内核直接就进行管理和调度的线程,它与 具体操作系统平台有关)
- 线程在Java平台也是一个对象
4、两种创建线程的区别
第一种创建方式:基于继承的技术
第二种创建方式:基于组合的技术。由于组合相对于继承来说,其类和类之间的耦合性更低,因此它也更加灵活。
5、在线程的属性当中,只有线程的ID是只读属性(属性有:编号ID,名称Name(String),类别Daemon(boolean,true为守护线程),优先级Priority(int))
6、线程分为守护线程和用户线程(又称为非守护线程)。用户线程会阻止Java虚拟机的正常停止,即一个Java虚拟机只有在其所有用户线程都运行结束(即Thread.run()调用未结束)的情况下才能正常停止。守护线程不会影响Java虚拟机的正常停止。因此,守护线程通常用于执行一些重要性不是很高的任务,例如监听其他线程的运行情况。
7、Thread类的常用方法
返回当前线程:static Thread currentThread()
用于实现线程的任务处理逻辑:void run()
启动线程:void start()
等待相应线程运行结束:void join()【等别的线程结束,自己再干活】
使当前线程主动放弃其对处理器的占用,这可能导致当前线程被暂停:static void yield()【别的线程要用资源就先用,但是别人不用,不介意自己继续占用】
使当前线程休眠指定的时间:static void sleep(long millis )
//倒计时案例
/**
* Created by 战战家的小可爱 on 2021/7/13 16:14
*/
public class SimpleTimer {
private static int count;
public static void main(String[] args) {
count=args.length>=1?Integer.valueOf(args[0]):60;
System.out.println(count);//输出为60
int remaining;
while (true){
remaining=countDown();
if(0==remaining){
break;
}else {
System.out.println("Remaining "+count+"s(秒)");
}
try{
Thread.sleep(1000);
}catch (InterruptedException e){
}
}
System.out.println("Done ....");
}
private static int countDown(){
return count--;
}
}
8、普通java类,所有的方法均由main线程负责执行
Web应用中的Servlet类的doGet、doPost方法也是由确定的线程执行的(具体与所使用的Web容器有关)
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.UUID;
/**
* Created by 战战家的小可爱 on 2021/7/13 16:27
*/
@WebServlet("/echo")
public class Echoservlet extends HttpServlet {
private String serialVersionUID= UUID.randomUUID().toString();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取当前线程的名称
Thread currentTread=Thread.currentThread();
String currentTreadName=currentTread.getName();
resp.setContentType("text/plain");
try(PrintWriter printWriter=resp.getWriter()){
//输出处理当前请求线程的名称
printWriter.println("当前线程的名称 :"+currentTreadName);
printWriter.flush();
}
}
}
9、子线程与父线程之间的生命周期没有必然的联系。父线程运行结束后,子线程可以继续运行,反之亦然