Java笔记 —— 多线程

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);

}

}

}

package test.MyThread;

public class Demo5 {

public static void main(String[] args) {

JoinThread j1 = new JoinThread();

JoinThread j2 = new JoinThread();

JoinThread j3 = new JoinThread();

j1.setName(“张”);

j2.setName(“周”);

j3.setName(“陈”);

j1.start();

try {

j1.join();

} catch (InterruptedException e) {

e.printStackTrace();

}

j2.start();

j3.start();

}

}

在这里插入图片描述

可以看出来,等到线程j1全部运行结束后,j2和j3才正常抢占cpu运行程序

线程休眠

package test.MyThread;

public class SleepThread extends Thread{

@Override

public void run(){

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

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

try {

//sleep(long millis)millis是毫秒数,一秒等于1000毫秒

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

package test.MyThread;

public class Demo6 {

public static void main(String[] args) {

SleepThread s1 = new SleepThread();

SleepThread s2 = new SleepThread();

SleepThread s3 = new SleepThread();

s1.setName(“张”);

s2.setName(“周”);

s3.setName(“陈”);

s1.start();

s2.start();

s3.start();

}

}

在这里插入图片描述

实际在控制台打印结果的时候,按照

张—0

周—0

陈—0

这样三个为一组的顺序,每隔一秒打印一次,直至打印完毕,但是内部的顺序可以改变,比如

周—1

张—1

陈—1

因为三个进程每次抢占完CPU打印后都要休眠一秒,因此第一个进程休眠的时间内,足够其余的进程完成一次输出,进程m1打印完就休眠,不再抢夺CPU。接着m2打印完休眠,最后m3打印完休眠。不会出现m1刚刚打印完就立刻开始抢占CPU的情况。所以以三个为一组打印。但这三个进程休眠结束后,会再次抢占CPU,所以组内的顺序不一样。

线程生命周期图

在这里插入图片描述

线程Thread通过start()来启动线程,真正实现多线程运行,此时线程处于就绪状态。只有抢到了CPU后可以进入运行状态,Thread调用run()方法,完成线程的执行语句。

而当语句完成后就回进入死亡状态,之前的代码里面都是while循环,所以输出语句完成后还会继续循环,一直没有完成run方法。因此在运行主函数的时候会发现,程序不会结束,需要手动关闭。原因就在于这个while循环一直是true,线程一直没办法死亡,程序也就不会结束。

中断线程

public final void stop():让正在运行的线程停止。run方法剩下的代码不会执行,此方法已经过时弃用了

public void interrupt():中断正在运行的线程,被中断的线程会将run方法执行完毕,并抛出异常

正常执行的代码

import java.util.Date;

public class MyStopThread extends Thread {

@Override

public void run() {

System.out.println(“开始执行时间:” + new Date());

try {

Thread.sleep(10000);

} catch (InterruptedException e) {

e.printStackTrace();

}

Syste

必看视频!获取2024年最新Java开发全套学习资料 备注Java

m.out.println(“执行结束时间:” + new Date());

System.out.println(“test”);

}

}

package test.MyThread;

public class ThreadStopDemo {

public static void main(String[] args) {

MyStopThread t1 = new MyStopThread();

t1.start();

}

}

总结

面试难免让人焦虑不安。经历过的人都懂的。但是如果你提前预测面试官要问你的问题并想出得体的回答方式,就会容易很多。

此外,都说“面试造火箭,工作拧螺丝”,那对于准备面试的朋友,你只需懂一个字:刷!

给我刷刷刷刷,使劲儿刷刷刷刷刷!今天既是来谈面试的,那就必须得来整点面试真题,这不花了我整28天,做了份“Java一线大厂高岗面试题解析合集:JAVA基础-中级-高级面试+SSM框架+分布式+性能调优+微服务+并发编程+网络+设计模式+数据结构与算法等”

image

且除了单纯的刷题,也得需准备一本【JAVA进阶核心知识手册】:JVM、JAVA集合、JAVA多线程并发、JAVA基础、Spring 原理、微服务、Netty与RPC、网络、日志、Zookeeper、Kafka、RabbitMQ、Hbase、MongoDB、Cassandra、设计模式、负载均衡、数据库、一致性算法、JAVA算法、数据结构、加密算法、分布式缓存、Hadoop、Spark、Storm、YARN、机器学习、云计算,用来查漏补缺最好不过。

image

3a705143646f4f3f6ea879d59b2788.png)(title-必看视频!获取2024年最新Java开发全套学习资料 备注Java)]

m.out.println(“执行结束时间:” + new Date());

System.out.println(“test”);

}

}

package test.MyThread;

public class ThreadStopDemo {

public static void main(String[] args) {

MyStopThread t1 = new MyStopThread();

t1.start();

}

}

总结

面试难免让人焦虑不安。经历过的人都懂的。但是如果你提前预测面试官要问你的问题并想出得体的回答方式,就会容易很多。

此外,都说“面试造火箭,工作拧螺丝”,那对于准备面试的朋友,你只需懂一个字:刷!

给我刷刷刷刷,使劲儿刷刷刷刷刷!今天既是来谈面试的,那就必须得来整点面试真题,这不花了我整28天,做了份“Java一线大厂高岗面试题解析合集:JAVA基础-中级-高级面试+SSM框架+分布式+性能调优+微服务+并发编程+网络+设计模式+数据结构与算法等”

[外链图片转存中…(img-ZT4QW3GD-1716464522515)]

且除了单纯的刷题,也得需准备一本【JAVA进阶核心知识手册】:JVM、JAVA集合、JAVA多线程并发、JAVA基础、Spring 原理、微服务、Netty与RPC、网络、日志、Zookeeper、Kafka、RabbitMQ、Hbase、MongoDB、Cassandra、设计模式、负载均衡、数据库、一致性算法、JAVA算法、数据结构、加密算法、分布式缓存、Hadoop、Spark、Storm、YARN、机器学习、云计算,用来查漏补缺最好不过。

[外链图片转存中…(img-W9ehFEPn-1716464522515)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值