写点什么

Java- 进阶:多线程 1,Java 程序员必须掌握的技术

发布于: 1 小时前
  • Java 命令会启动 JVM,即启动了一个进程,该进程会启动一个主线程,然后主线程调用某个类的?main 方法,所以 main 方法 都是运行在主线程里

  • jvm 启动后,必然有一个执行路径(线程)从 main 方法开始的,一直执行到 main 方法结束,这个线程在 Java 中称之为主线程

  • 当程序的主线程执行时,如果遇到了循环而导致程序在指定位置停留时间过长,则无法马上执行下面的程序,需要等待循环结束后能够执行

  • 方法在哪个线程中被调用,它就运行在哪个线程中

  • JVM 是一个多线程程序,每个 Java 进程都分配一个 JVM 实例


public class ThreadDemo {    public static void main(String[] args) {        //利用垃圾回收器来证明        while(true) {            //这里虽然一直在堆空间中,创建数组对象,            // 但是始终没有耗尽堆空间,就是因为垃圾回收器,            // 在另外一个线程中,帮我们回收垃圾,所以才不会耗尽heap内存            // 从而证明,jvm是线程的            int[] ints = new int[1024];            ints = null;        }    }}
复制代码



二、Thread 类

1. 概述

  • Thread 是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程

  • 不是抽象类

2. 构造方法

  • Thread(): 分配新的 Thread 对象

  • Thread(String name):分配新的 Thread 对象,将指定的 name 作为其线程名称

3. 常用方法

  • void start():使该线程开始执行,Java 虚拟机调用线程的 run 方法

  • void run():该线程要执行的操作,

  • static void sleep(long millis):在指定毫秒内让当前正在执行的线程休眠,暂停执行

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

4. 创建新执行线程的两种方法

  • 将类声明为?Thread?的子类。该子类应重写 Thread 类的?run?方法。创建对象,开启线程。run 方法相当于其他线程的 main 方法。

  • 声明一个实现?Runnable?接口的类。该类然后实现?run?方法。然后创建 Runnable 的子类对象,传入到某个线程的构造方法中,开启线程。


虽然实现线程有两种方式,其实从客观来讲,线程本身只代表独立的执行路径, 执行的具体内容其实是 Task 本身,和执行路径的实现本身没有联系;只是我们开发者,想将一个 task 放在某条独立的执行路径(Thread 类对象,也就是一个线程中)来运行



三、创建线程:继承 Thread 类

  • 创建线程的步骤

  • 定义一个类继承 Thread

  • 重写 run 方法

  • 创建子类对象,就是创建线程对象

  • 调用 start 方法,开启线程并让线程执行,同时还会告诉 jvm 去调用 run 方法


线程对象调用?run 方法?不开启线程。仅是对象调用方法。?线程对象调用?start?开启线程,并让 jvm 调用 run 方法在开启的线程中执行


//测试类public class Test {    public static void main(String[] args) {        //创建自定义线程对象        MyThread mt = new MyThread("新的线程!");
//错误启动线程,这只是普通的方法调用 //firstThread.run();
//开启新线程 mt.start();
//再次启动一个线程,会抛异常IllegalThreadStateException //因为一个线程对象只能启动一次, // 如果同一个线程对象,启动多次,就会抛出异常 //firstThread.start(); //只能创建一个新的对象 new MyThread("第二个线程!").start(); //获取主线程的名字 System.out.println(Thread.currentThread().getName()+":主线程!"); }}
//自定义线程class MyThread extends Thread { //定义指定线程名称的构造方法 public MyThread(String name) { //调用父类的String参数的构造方法,指定线程的名称 super(name); }
//重写run方法,完成该线程执行的逻辑 @Override public void run() { //获取线程的名字getName() System.out.println(getName() + ":正在执行!"); }}
复制代码



四、创建线程:实现 Runnable 接口

1. Runnable 接口的构造方法

  • Thread(Runnable target): 分配新的 Thread 对象,以便将 target 作为其运行对象

  • Thread(Runnable target,String name)?: 分配新的 Thread 对象,以便将 target 作为其运行对象;并将指定的 name 作为其名称

2. 创建线程的步骤

  • 定义类实现?Runnable?接口。

  • 覆盖接口中的?run 方法

  • 创建 Thread 类的?对象

  • 将 Runnable 接口 的子类对象作为参数传递给?Thread 类?的构造方法

  • 调用 Thread 类的?start()?开启线程。


Thread 类的构造函数:1.?Thread(): 分配新的 Thread 对象 2.?Thread(String name):分配新的 Thread 对象,将指定的 name 作为其线程名称


//测试类public class Test {    public static void main(String[] args) {        //创建实现 Runnable 接口的子类对象        MyRunnable myrunnable = new MyRunnable();        //创建Thread实例,在Thread的构造方法中传递Runnable实例        //Runnable就代表在 Thread 上运行的任务        Thread thread = new Thread(myrunnable);        //开启线程        thread.start();        for (int i = 0; i < 10; i++) {            System.out.println("main线程:正在执行!"+i);        }    }}
//自定义线程执行任务类class MyRunnable implements Runnable{ //定义线程要执行的run方法逻辑 //run方法,不能抛出编译时异常 //run方法,没有参数没有返回值 @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println("我的线程:正在执行!"+i); } }}
复制代码

3. 实现 Runnable 接口的原理

为什么需要定一个类去实现 Runnable 接口呢?继承 Thread 类和实现 Runnable 接口有啥区别呢??只有创建 Thread 类的对象才可以创建线程。线程任务已被封装到 Runnable 接口的 run 方法中,而这个 run 方法所属于 Runnable 接口的子类对象,所以将这个子类对象作为参数传递给 Thread 的构造函数,这样,线程对象创建时就可以明确要运行的线程的任务

4. 两种方式的比较

  • 继承 Thread 类方式

  • 如果某个类已经有父类,则无法再继承 Thread 类

  • 实现 Runnable 接口方式

  • 解决了方式一的单继承的局限性

  • 还有一个优点,便于多线程共享数据


第二种方式实现 Runnable 接口避免了单继承的局限性,所以较为常用。实现 Runnable 接口的方式,更加的符合面向对象;线程分为两部分,一部分线程对象,一部分线程任务。


  • 继承 Thread 类,线程对象和线程任务耦合在一起。一旦创建 Thread 类的子类对象,既是线程对象,有又有线程任务

  • 实现 runnable 接口,将线程任务单独分离出来封装成对象类型就是 Runnable 接口类型。Runnable 接口对线程对象和线程任务进行解耦



五、线程优先级

1. 概述

  • 我们可以通过?Thread?类中:

  • getPriority 方法?:获取?线程的优先级

  • setPriority 方法?:设置?线程的优先级

2. 线程优先级的范围

  • 如果设置线程优先级的范围,超出了规定范围,会抛出异常;


MAX_PRIORITY 10 //最大优先级?MIN_PRIORITY 1 //最小优先级?NORM_PRIORITY 5 //默认优先级


//使用thread1.setPriority(Thread.MAX_PRIORITY); //设置thread2.setPriority(Thread.MIN_PRIORITY);System.out.println(thread1.getPriority());//获取
复制代码

3. 注意!

  • 对于 TThread 类中的优先级,对于 jvm 而言,不起决定性作用


也就是说 jvm 在实际调度线程的时候,它使用的优先级可能不仅仅包含我们给每个线程所设置的静态优先级,可能还考虑了其他的很多因素(各个线程的运行状态),所以我们所设置的优先级对于 jvm 只起一个参考作用



六、线程的生命周期

1. 新建状态

  • 创建线程对象

2. 就绪状态

  • ?start() 之后

  • sleep() 睡醒之后

  • yield() 之后

  • 有执行资格,等待 cpu 调度

3. 运行状态

  • 取得执行权,开始执行

4. 阻塞状态

  • 无执行资格,无执行权

5. 死亡状态

  • 执行完毕,等待垃圾回收




七、同步代码块

1. 问题引入——线程安全问题

  • 发生线程安全问题的三个条件


1. 多线程运行环境 2. 多线程访问线程共享数据(存在共享数据)3. 访问共享数据的操作不是原子操作。?注:原子操作:不可分割的操作?,相当于一次性完成的操作


  • 当这三个条件同时满足的时候,才会发生多线程的数据安全问题

  • 解决多线程的线程安全问题:如何实现线程对共享资源的排他性访问(只有我访问完了你们才能修改)?

  • 使用?同步代码块

2. 同步代码块

  • 同步代码块实现?线程同步

  • 线程同步:就是利用锁对象,完成多线程运行环境中,对共享资源的排他性访问(我走你不能走, 你走我不能走)

  • 优点:解决了多线程的安全问题

  • 缺点:消耗资源(当线程很多时,每个线程运行的时候都需要去判断同步锁,这个是很耗费系统资源的)


线程异步:线程之间,互不干扰,各自独立运行(我走我的,你走你的)


  • 格式


synchronized(对象){    //critical section        对共享资源的访问的代码}
复制代码


1. 同步代码块中所使用的对象,称之为锁对象?——> 锁的角色?锁对象中,有一个标志位:可以表示两种状态,加锁?和?解锁 2. 持有锁的是线程(一个线程给一个锁对象加锁,我们就说这个线程持有了锁对象)3. 同步代码块何时给锁对象加锁?进入同步代码块的同时,就给锁对象加锁 4. 线程何时释放持有的锁??当执行完同步代码块的时候,就会释放同步代码块的锁对象 5. 判断和修改锁对象标志位的操作,这是由 jvm?保证一定是原子操作?6.?锁对象,究竟是什么对象? java 语言中任意一个对象,都可以充当锁对象的角色(因为任意一个对象中都有一个表示加锁,解锁状态的标志位)


  • 注意点


虽然锁对象可以是任意对象,但是针对同一个(同样的多个共享变量)?的所有操作,都必须保证在同步代码块中,使用同一个锁对象,才能避免线程安全问题。

3. 同步方法

  • 当同步代码块的范围扩大到整个方法的方法体的时候,我们可以将整个方法定义成同步方法在方法声明上加上synchronized

  • 普通的同步代码块:?synchronized(锁对象) {}

  • 同步方法:?void synchronized 方法名(){}

  • 同步方法的锁对象就是?this

最后的内容

在开头跟大家分享的时候我就说,面试我是没有做好准备的,全靠平时的积累,确实有点临时抱佛脚了,以至于我自己还是挺懊恼的。(准备好了或许可以拿个 40k,没做准备只有 30k+,你们懂那种感觉吗)


如何准备面试?


1、前期铺垫(技术沉积)


程序员面试其实是对于技术的一次摸底考试,你的技术牛逼,那你就是大爷。大厂对于技术的要求主要体现在:基础,原理,深入研究源码,广度,实战五个方面,也只有将原理理论结合实战才能把技术点吃透。


下面是我会看的一些资料笔记,希望能帮助大家由浅入深,由点到面的学习 Java,应对大厂面试官的灵魂追问,有需要的话就戳这里:蓝色传送门打包带走吧。


这部分内容过多,小编只贴出部分内容展示给大家了,见谅见谅!


  • Java 程序员必看《Java 开发核心笔记(华山版)》



  • Redis 学习笔记



  • Java 并发编程学习笔记


四部分,详细拆分并发编程——并发编程+模式篇+应用篇+原理篇



  • Java 程序员必看书籍《深入理解 ava 虚拟机第 3 版》(pdf 版)



  • 大厂面试必问——数据结构与算法汇集笔记



其他像 Spring,SpringBoot,SpringCloud,SpringCloudAlibaba,Dubbo,Zookeeper,Kafka,RocketMQ,RabbitMQ,Netty,MySQL,Docker,K8s 等等我都整理好,这里就不一一展示了。



2、狂刷面试题


技术主要是体现在平时的积累实用,面试前准备两个月的时间再好好复习一遍,紧接着就可以刷面试题了,下面这些面试题都是小编精心整理的,贴给大家看看。


①大厂高频 45 道笔试题(智商题)



②BAT 大厂面试总结(部分内容截图)




③面试总结




3、结合实际,修改简历


程序员的简历一定要多下一些功夫,尤其是对一些字眼要再三斟酌,如“精通、熟悉、了解”这三者的区别一定要区分清楚,否则就是在给自己挖坑了。当然不会包装,我可以将我的简历给你参考参考,如果还不够,那下面这些简历模板任你挑选:



以上分享,希望大家可以在金三银四跳槽季找到一份好工作,但千万也记住,技术一定是平时工作种累计或者自学(或报班跟着老师学)通过实战累计的,千万不要临时抱佛脚。


另外,面试中遇到不会的问题不妨尝试讲讲自己的思路,因为有些问题不是考察我们的编程能力,而是逻辑思维表达能力;最后平时要进行自我分析与评价,做好职业规划,不断摸索,提高自己的编程能力和抽象思维能力。


以上文章中,提及到的所有的笔记内容、面试题等资料,均可以免费分享给大家学习,有需要的话就戳这里打包带走吧。

用户头像

VX:vip204888 领取资料 2021.07.29 加入

还未添加个人简介

评论

发布
暂无评论
Java-进阶:多线程1,Java程序员必须掌握的技术