Java多线程笔记

一 、进程、线程与任务

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、子线程与父线程之间的生命周期没有必然的联系。父线程运行结束后,子线程可以继续运行,反之亦然

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值