Java最新Java笔记 —— 多线程,这操作真香

Spring全套教学资料

Spring是Java程序员的《葵花宝典》,其中提供的各种大招,能简化我们的开发,大大提升开发效率!目前99%的公司使用了Spring,大家可以去各大招聘网站看一下,Spring算是必备技能,所以一定要掌握。

目录:

部分内容:

Spring源码

  • 第一部分 Spring 概述
  • 第二部分 核心思想
  • 第三部分 手写实现 IoC 和 AOP(自定义Spring框架)
  • 第四部分 Spring IOC 高级应用
    基础特性
    高级特性
  • 第五部分 Spring IOC源码深度剖析
    设计优雅
    设计模式
    注意:原则、方法和技巧
  • 第六部分 Spring AOP 应用
    声明事务控制
  • 第七部分 Spring AOP源码深度剖析
    必要的笔记、必要的图、通俗易懂的语言化解知识难点

脚手框架:SpringBoot技术

它的目标是简化Spring应用和服务的创建、开发与部署,简化了配置文件,使用嵌入式web服务器,含有诸多开箱即用的微服务功能,可以和spring cloud联合部署。

Spring Boot的核心思想是约定大于配置,应用只需要很少的配置即可,简化了应用开发模式。

  • SpringBoot入门
  • 配置文件
  • 日志
  • Web开发
  • Docker
  • SpringBoot与数据访问
  • 启动配置原理
  • 自定义starter

微服务架构:Spring Cloud Alibaba

同 Spring Cloud 一样,Spring Cloud Alibaba 也是一套微服务解决方案,包含开发分布式应用微服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。

  • 微服务架构介绍
  • Spring Cloud Alibaba介绍
  • 微服务环境搭建
  • 服务治理
  • 服务容错
  • 服务网关
  • 链路追踪
  • ZipKin集成及数据持久化
  • 消息驱动
  • 短信服务
  • Nacos Confifig—服务配置
  • Seata—分布式事务
  • Dubbo—rpc通信

Spring MVC

目录:

部分内容:

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

  • Java程序的运行原理:

  • 线程调度

  • 多线程的实现

    • 实现方式一、继承Thread类,并重写run()方法
    • 获取和设置线程名字
  • 从结果分析线程抢占cpu的过程

  • 实现方式二、实现Runnable接口

    • 两种创建方式的对比
  • 线程的优先级

  • 加入线程

  • 线程休眠

      • 线程生命周期图
  • 中断线程

      • 正常执行的代码
  • 中断执行的代码

  • 礼让线程

  • 后台线程(守护线程)

进程与线程
  1. 进程:

正在运行的程序,是系统进行资源分配和调用的独立单位。

每一个进程都有它自己的内存空间和系统资源

  1. 线程:

是进程中的单个顺序控制流,或者说就是一个单独执行路径

一个进程如果只有一条执行路径,称之为单线程程序。一个进程如果有多条执行路径,称之为多线程程序。

线程是包含在进程中的。

并行与并发

并行应该是物理上可以同时发生,两个或多个事件在同一时刻发生。比喻是电话来了,放下筷子打电话,打完电话再吃饭。因为只有一个人只有一张嘴,无法同时打电话和吃饭,所以需要两张嘴才能做到同时打电话和吃饭。强调是同一时刻互不干扰的同时执行。

并发是逻辑上同时发生,两个或多个事件在同一时间间隔内内发生。比喻是一边打电话一边吃饭,用很小的时间间隔吃一口饭,说一句话,两者交替发生,宏观上好像是同时发生,但实际不是。强调的是利用小的时间段交替的完成。

Java程序的运行原理:

java 命令会启动 JVM,等于启动了一个应用程序,也就是启动了一个进程。

该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法。所以 main方法运行在主线程中。也就是说,像前面学习java的过程中,写的程序其实都是单线程的。

而JVM本身启动的时候是多线程,因为JVM至少有主线程和垃圾回收线程两个线程。

线程调度

假设计算机只有一个 CPU, CPU 在某一个时刻只能执行一条指令,线程只有得到 CPU时间片,也就是使用权,才可以执行指令。那么具体应该是哪个线程得到CPU呢,这就牵扯到了线程调度问题。

线程有两种调度模型:

  1. 分时调度模型 :所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片

  2. 抢占式调度模型 :优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。 (这里的优先并不保证一定能抢到CPU,只是可能性更大一些)

多线程的实现
实现方式一、继承Thread类,并重写run()方法

package test.MyThread;

public class MyThreadDemo1 extends Thread {

@Override

public void run(){

for(int i=0;i<10;i++){

System.out.println(i);

}

}

}

package test.MyThread;

public class Demo1 {

public static void main(String[] args) {

//创建两个线程m1和m2

MyThreadDemo1 m1 = new MyThreadDemo1();

MyThreadDemo1 m2 = new MyThreadDemo1();

//start()方法的调用,首先单独启动了一个线程,然后再由JVM去调用该线程的run()方法

m1.start();

m2.start();

}

}

结果是从0打印到10 ,而且连续打印两次

这里需要注意的是:

  1. 不能直接用线程对象调用run方法,直接调用run方法,就相当于普通的调用方法,与多线程无关。要想看到多线程的效果,就必须用start()来启动

  2. 调用run()与调用start()的区别

run()的调用仅仅是封装了被线程执行的代码,但是直接调用的话是普通的方法调用

start()方法的调用,首先单独启动了一个线程,然后再由JVM去调用该线程的run()方法

  1. 线程启动调用的是 start() 方法,但实际上调用的却是 run() 方法定义的主体

  2. 同一个线程不能被start()启动两次,如果这样做会报错java.lang.IllegalThreadStateException

获取和设置线程名字
  1. 通过构造方法给线程起名字:

Thread(String name) 分配一个新的 Thread对象。

  1. 通过方法给线程起名字:

void setName(String name) 将此线程的名称更改为等于参数 name

package test.MyThread;

public class MyThreadDemo2 extends Thread{

//通过构造方法给线程起名字

public MyThreadDemo2(){

super();

}

public MyThreadDemo2(String name){

super(name);

}

@Override

public void run(){

for(int i=0;i<10;i++){

System.out.println(getName()+“—”+i);

}

}

}

package test.MyThread;

public class Demo2 {

public static void main(String[] args) {

//方式一、通过构造方法给线程起名字

MyThreadDemo2 m1 = new MyThreadDemo2(“张”);

MyThreadDemo2 m2 = new MyThreadDemo2(“陈”);

m1.start();

m2.start();

//方式二、通过方法给线程起名字

MyThreadDemo2 m3 = new MyThreadDemo2();

MyThreadDemo2 m4 = new MyThreadDemo2();

MyThreadDemo2 m5 = new MyThreadDemo2();

m3.setName(“杜”);

m4.setName(“周”);

m3.start();

m4.start();

m5.start();

//public static Thread currentThread()返回对当前正在执行的线程对象的引用

//通过currentThread()获取当前的线程对象,再通过getName()获取线程名字

//拿这个程序的main方法的主线程举例

System.out.println(Thread.currentThread().getName());

}

}

结果太长,只截取部分图片

在这里插入图片描述

从结果分析线程抢占cpu的过程

通过这个图片就可以看出来,线程之间是以抢夺的方式来占据cpu的,只有占据了cpu的线程才可以运行。因此当线程m1占用cpu成功后,运行run()方法的方法体,打印了输出语句“张—0”。而所有线程是交错运行的,每个线程只能占据cpu的一小段时间,这样看起来是一起运行,实际上不是。因此线程m1运行结束后,线程m3抢占了cpu,打印了语句“杜—0”。

另外这里面还夹着一个字符串 “main”,这是 System.out.println(Thread.currentThread().getName());语句的输出结果

图片的下面是Thread-2,这个是线程对象m5的名字,我故意没有给m5赋名,这里就可以看出来,如果自己不命名,则系统会自动给一个名字。

实现方式二、实现Runnable接口

1、创建自定义类实现Runnable接口

2、重写run()方法

3、创建自定义类的对象

4、创建Thread类的对象,将第三步创建的自定义类对象作为参数传递到构造方法中

package test.MyThread;

public class MyRunnable implements Runnable {

@Override

public void run(){

for(int i=0;i<100;i++){

System.out.println(Thread.currentThread().getName()+“—”+i);

}

}

}

关于Thread.currentThread().getName()这条语句的解释:

由于实现的Runnable接口中并没有getName()方法,所以这里无法使用Thread类中的getName()方法

此时如果我们获取当前线程的名字,就需要间接调用,即通过currentThread()获取当前正在执行run()方法的线程对象,再通过getName()获取线程名字

currentThread()获得的线程对象是Thread类型,因此可以用getName()方法

package test.MyThread;

public class Demo3 {

public static void main(String[] args) {

MyRunnable mr = new MyRunnable();

Thread t1 = new Thread(mr);

Thread t2 = new Thread(mr);

t1.setName(“张”);

t2.setName(“陈”);

t1.start();

t2.start();

}

}

在这里插入图片描述

两种创建方式的对比
  1. 通过Runnable接口实现的类可以继承其他类,继承Thread的类不行。可以避免由于Java单继承带来的局限性。

  2. Runnable适合多个相同程序的代码去处理同一个资源的情况,比如

package test.MyThread;

public class RunnableThread implements Runnable{

int num = 100;

@Override

public void run(){

while(true){

if(num>0){

System.out.println(Thread.currentThread().getName()+“—”+(num–));

}

}

}

}

package test.MyThread;

public class Test1 {

public static void main(String[] args) {

RunnableThread rt = new RunnableThread();

Thread t1 = new Thread(rt,“窗口一”);

Thread t2 = new Thread(rt,“窗口二”);

Thread t3 = new Thread(rt,“窗口三”);

t1.start();

t2.start();

t3.start();

}

}

在这里插入图片描述

三个窗口,也就是三个线程是共用一个变量num的,即三个窗口卖票的总和是100(这里的num是票数),三个线程共用一个RunnableThread类的变量num

package test.MyThread;

public class ExtendsThread extends Thread {

int num = 100;

@Override

public void run(){

while(true){

if(num>0){

System.out.println(getName()+“—”+(num–));

}

}

}

}

package test.MyThread;

public class Test1 {

public static void main(String[] args) {

ExtendsThread e1 = new ExtendsThread();

ExtendsThread e2 = new ExtendsThread();

ExtendsThread e3 = new ExtendsThread();

e1.setName(“窗口一”);

e2.setName(“窗口二”);

e3.setName(“窗口三”);

e1.start();

e2.start();

e3.start();

}

}

在这里插入图片描述

这里可以看出来,三个线程,也就是三个窗口各有一百张票,总共三百张票。每个线程都有一个自己独自的ExtendsThread类的实例变量num

这里如果想让extends继承产生的线程也共用一个变量,就需要将int num = 100改为

public static int num = 100;

因此总结一下是:Runnable可以实现多个相同的程序代码的线程去共享同一个资源,而Thread虽然可以做到但不推荐

线程的优先级

获取线程的优先级

public final int getPriority()返回此线程的优先级。

设置线程的优先级

public final void setPriority(int newPriority)更改此线程的优先级, 参数newPriority的范围为1-10之间

注意:

  1. 线程的默认优先级为5

  2. 线程优先级的范围是1-10

  3. 线程优先级高仅仅表示的是获取CPU时间片的几率会高一些,但不代表优先级高的线程一定能抢到CPU

package test.MyThread;

public class MyPriority extends Thread {

@Override

public void run(){

for(int i=0;i<10;i++){

System.out.println(getName()+“—”+i);

}

}

}

package test.MyThread;

public class Demo4 {

public static void main(String[] args) {

MyPriority m1 = new MyPriority();

MyPriority m2 = new MyPriority();

//获取m1和m2的优先级

System.out.println(m1.getPriority());

System.out.println(m2.getPriority());

//设置m1和m2的优先级

m1.setPriority(1);

m2.setPriority(10);

//启动线程

m1.start();

m2.start();

}

}

在这里插入图片描述

可以看出来线程m2抢到CPU的概率高了很多

加入线程

加入线程:

public final void join():其他线程等待这个线程死亡

注意事项:

在线程设置为加入线程之前,先将该线程变为就绪状态,也就是调用start()方法

package test.MyThread;

public class JoinThread extends Thread{

@Override

public void run(){

for(int i=0;i<10;i++){

System.out.println(getName()+“—”+i);

}

}

}

最后

本人也收藏了一份Java面试核心知识点来应付面试,借着这次机会可以送给我的读者朋友们

目录:

全靠这套面试题,才让我有惊无险美团二面拿offer  (面经解析)

Java面试核心知识点

一共有30个专题,足够读者朋友们应付面试啦,也节省朋友们去到处搜刮资料自己整理的时间!

全靠这套面试题,才让我有惊无险美团二面拿offer  (面经解析)

Java面试核心知识点

已经有读者朋友靠着这一份Java面试知识点指导拿到不错的offer了

全靠这套面试题,才让我有惊无险美团二面拿offer  (面经解析)

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

以看出来线程m2抢到CPU的概率高了很多

加入线程

加入线程:

public final void join():其他线程等待这个线程死亡

注意事项:

在线程设置为加入线程之前,先将该线程变为就绪状态,也就是调用start()方法

package test.MyThread;

public class JoinThread extends Thread{

@Override

public void run(){

for(int i=0;i<10;i++){

System.out.println(getName()+“—”+i);

}

}

}

最后

本人也收藏了一份Java面试核心知识点来应付面试,借着这次机会可以送给我的读者朋友们

目录:

[外链图片转存中…(img-QPyWjU9n-1715403887320)]

Java面试核心知识点

一共有30个专题,足够读者朋友们应付面试啦,也节省朋友们去到处搜刮资料自己整理的时间!

[外链图片转存中…(img-vkugEDVG-1715403887320)]

Java面试核心知识点

已经有读者朋友靠着这一份Java面试知识点指导拿到不错的offer了

[外链图片转存中…(img-tYbkoG5a-1715403887321)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值