当前位置: 首页 > web >正文

Java基础(多线程1)

一、程序同一时刻只能做一件事情,要让它做多件事情

二、基本概念

2.1、 程序、进程、线程

程序(program):是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。

进程(process):是程序的一次执行过程,或是正在运行的一个程序。动态过程:有它自身的产生、存在和消亡的过程。(任务管理器可找到)

~运行中的QQ,运行中的MP3播放器;

~程序是静态的,进程是动态的;

~进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域。

线程(thread):进程可进一步细化为线程,是一个程序内部的一条执行路径。

~若一个进程同一时间并行执行多个线程,就是支持多线程的;

~线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小;

~一个进程中的多个线程共享相同的内存单元/内存地址单元/内存地址空间,它们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便高效。但可能安全性低。

~每个Java程序都有一个隐含的主线程:main方法

2.2、并发和并行

单核CPU,其实是一种假的多线程,因为一个核在一个时间单元内,只能执行一个线程的任务,实际还是串行执行的。操作系统中有一个组件叫做任务调度器,将CPU的时间片分给不同的程序使用,只是由于CPU在线程间的切换非常快,人类感觉是同时运行的。

总结:微观串行,宏观并行。

如果是多核CPU的话,才能更好的发挥线程的效率,现在电脑多核CPU每个核都可以调度运行线程,这时候线程可以并行的。

并发concurrent:同一时间应对多件事情的能力。一个线程轮流交替做多件事情

并行parallel:同一时间动手做多件事情的能力。几个线程做不同事互不干扰

2.3 多线程应用场合

程序需要同时执行两个或多个任务;

需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等;

需要一些后台运行的程序时

三、创建线程

线程通过java.lang.Thread类来体现。

Thread类的特性:

每个线程都是通过某个特定Thread对象的run()方法来完成操作的,经常把run()方法的主体称为线程体;

通过该Thread对象的start()方法来启动这个线程,而非直接调用run().

创建线程的四种方式:

继承Thread类的方式;

实现Runnable接口的方式;

实现Callable接口的方式;

线程池创建线程。

3.1、继承Thread类的

步骤:

1、自定义线程子类,继承Thread类;

2、子类重写Thread类中的run方法 --- run方法中的内容就是线程体

3、创建自定义线程类的对象

4、调用start方法启动线程

run() 和 start() 的区别?

~start():用来启动线程,通过该线程调用run方法执行run方法中所定义的逻辑代码。start方法只能被调用一次。

~run():封装了要被线程执行的代码,可以被调用多次。
注意:启动线程要使用start方法,不要使用run方法。

线程不能重复启动

package day26;/*
* 聊天线程
* */
public class ChatThread extends Thread {@Overridepublic void run() {for (int i = 0; i <100 ; i++) {System.out.println("聊天");//睡眠//异常只能try/catch run方法不支持抛出try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}}}}package day26;/*
* 喝酒线程
* */
public class DrinkThread extends Thread {@Overridepublic void run() {for (int i = 0; i <100 ; i++) {System.out.println("喝酒");try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}}}}package day26;/*
* 吃饭线程
* */
public class EatThread extends Thread {@Overridepublic void run() {for (int i = 0; i <100 ; i++) {System.out.println("吃饭");try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}}}}package day26;/*
* 创建多线程的方式:
*  1、继承Thread类
*  2、实现Runnable接口
*  3、实现Callable接口
*  4、线程池实现
* */
public class MyTest1 {public static void main(String[] args) {//创建线程类的对象DrinkThread drinkThread = new DrinkThread();ChatThread chatThread = new ChatThread();EatThread eatThread = new EatThread();//启动线程//这里如果改成.run(),就变成顺序执行的了。drinkThread.start();chatThread.start();eatThread.start();}
}

3.2、实现Runnable接口

步骤:

1、定义子类实现Runnable接口;

2、子类中重写Runnable接口中的run方法;

3、通过Thread类构造方法创建线程对象;

4、将Runnable接口的子类对象作为实际参数传递给Thread类的构造方法中;

5、调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法。

优势:

1、避免了单继承的局限性;

2、多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源。

package day26;public class DrinkThread1 implements Runnable {@Overridepublic void run() {for (int i = 0; i <100 ; i++) {System.out.println("喝酒");try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}package day26;public class ChatThread1 implements Runnable {@Overridepublic void run() {for (int i = 0; i <100 ; i++) {System.out.println("聊天");//睡眠//异常只能try/catch run方法不支持抛出try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}package day26;public class EatThread1 implements Runnable {@Overridepublic void run() {for (int i = 0; i <100 ; i++) {System.out.println("吃饭");try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}package day26;public class MyTest2 {public static void main(String[] args) {DrinkThread1 drinkThread1 = new DrinkThread1();EatThread1 eatThread1 = new EatThread1();ChatThread1 chatThread1 = new ChatThread1();//创建线程对象Thread th1 = new Thread(drinkThread1);Thread th2 = new Thread(eatThread1);Thread th3 = new Thread(chatThread1);//匿名内部类Thread th4 = new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i <100 ; i++) {System.out.println("唱歌");try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}}}});//启动线程th1.start();th2.start();th3.start();}
}

如何在多个线程中分享数据:

package day26;public class MyThread implements Runnable {//10只有一份private Integer count = 10;@Overridepublic void run() {System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" + count);}
}package day26;public class MyThread1 extends Thread {//要想让它像MyThread类一样用接口传递分享一份数据,用staticprivate static Integer count = 10;@Overridepublic void run() {System.out.println("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" + count);}
}package day26;public class MyTest3 {public static void main(String[] args) {MyThread myThread = new MyThread();Thread th1 = new Thread(myThread);Thread th2 = new Thread(myThread);Thread th3 = new Thread(myThread);Thread th4 = new Thread(myThread);th1.start();th2.start();th3.start();th4.start();MyThread1 m1 = new MyThread1();MyThread1 m2 = new MyThread1();MyThread1 m3 = new MyThread1();m1.start();m2.start();m3.start();}
}

3.3、Thread类相关方法

相关方法:

start():启动线程,并执行对象的run()方法;

run():线程在被调用时执行的操作;

getName():返回线程的名称;

setName():设置线程的名称;

currentThread():返回当前线程。

package day26;/*
* 自定义线程类
* getName() --- 获取当前线程的名字
* */
public class MyThread extends Thread {@Overridepublic void run() {for (int i = 1; i <= 100 ; i++) {//System.out.println(getName() + i);System.out.println(Thread.currentThread().getName() + ": " + i);}}
}package day26;/*
* getName() - 获取当前线程的名字
* setName() - 为线程设置名字
* currentThread - 获取当前线程的引用
* */
public class MyTest {public static void main(String[] args) {//创建两个线程的对象MyThread t1 = new MyThread();MyThread t2 = new MyThread();t1.setName("线程1");t2.setName("线程2");//比如main方法,没有获取线程引用时Thread.currentThread().setName("主线程");t1.start();t2.start();for (int i = 1; i <= 100 ; i++) {//System.out.println(getName() + i);System.out.println(Thread.currentThread().getName() + ": " + i);}}
}

四、线程的调度和生命周期

4..1、线程的调度

调度策略:

基于时间片轮转

抢占式:高优先级的线程抢占CPU

调度方法:

同优先级线程组成先进先出队列,使用时间片策略

对高优先级,使用优先调度的抢占式策略

Java中线程优先级的范围是1~10,默认的优先级是5

MAX_PRIORITY(10) MIN_PRIOPITY(1) NORM_PRIORITY(5)

涉及的方法:

setPriority(int newPriority):设置线程的优先级

getPriority():获取线程的优先级

yield():线程让步:暂停当前正在执行的线程。把执行机会让给优先级相同或更高的线程,若队列中没有同优先级的线程,忽略此方法。

join():

当某个程序执行流中调用其他线程的join()方法时,调用线程将被阻塞,直到join()方法加入的join线程执行完为止

低优先级的线程也可以获得执行。

sleep(long millis):令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行时,时间到后重排队

stop():强制线程生命期结束。

isAlive():判断线程是否还活着。

4.2、join()方法:

1)

package day26;/*
* t1
* t2  t1.join()
* t3  t2.join()
*
* 如果有多个线程,如何实现多个线程按照顺序运行 -- join
*
* */
public class MyTest1 {public static void main(String[] args) {Thread th1 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("t1");}});Thread th2 = new Thread(new Runnable() {@Overridepublic void run() {try {th1.join();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("t2");}});Thread th3 = new Thread(new Runnable() {@Overridepublic void run() {try {th2.join();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("t3");}});th1.start();th2.start();th3.start();}
}
package day26;/*
* 自定义线程类
* getName() --- 获取当前线程的名字
* */
public class MyThread extends Thread {@Overridepublic void run() {for (int i = 1; i <= 100 ; i++) {//System.out.println(getName() + i);System.out.println(Thread.currentThread().getName() + ": " + i);}}
}

2)更简便调用join方法

package day26;public class MyThread1 extends Thread {private Thread t;public MyThread1(Thread t){this.t = t;}@Overridepublic void run() {try {if(t != null){t.join();}} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(Thread.currentThread().getName());}
}
package day26;public class MyTest2 {public static void main(String[] args) {MyThread1 t1 = new MyThread1(null);MyThread1 t2 = new MyThread1(t1);MyThread1 t3 = new MyThread1(t2);t1.setName("线程1");t2.setName("线程2");t3.setName("线程3");t1.start();t2.start();t3.start();}
}

4.3、线程生命周期

新建(NEW):

当一个线程对象被创建,但还未调用start方法时处于新建状态

此时未与操作系统底层线程关联

可运行(RUNNABLE):

调用了start方法,就会由新建进入可运行

此时与底层线程关联,由操作系统调度执行

终结(TERMINATED):

线程内代码已经执行完毕,由可运行进入终结

此时会取消与底层线程关联

阻塞(BLOCKED):

当获取锁失败后,由可运行进入Monitor的阻塞队列阻塞,此时不占用CPU时间

当持锁线程释放锁时,会按照一定规则唤醒阻塞队列中的阻塞线程,唤醒后的线程进入可运行

等待(WAITING):

当获取锁成功后,但由于条件不满足,调用了wait()方法,此时从可运行状态释放锁进入Monitor

。。。。。。。

http://www.xdnf.cn/news/6306.html

相关文章:

  • 鸿蒙-5.1.0-release构建编译环境
  • 分割等和子集习题分析
  • HCIP(OSPF的拓展配置及选路规则)
  • 矩阵乘法的优化与复杂度分析
  • 一个日志量突增的问题分析处理经历
  • 普通IT的股票交易成长史--20250514复盘
  • 机器学习任务的常用评估指标
  • JVM内存模型
  • 前端面试题:vue3 为什么不需要时间分片?
  • Linux程序设计--期末复习
  • 企业网络新选择:软件定义架构下的MPLS
  • 【Docker】Windows10环境下安装DockerDesktop
  • TCP 三次握手建立连接详解
  • C2S-Scale:Cell2Sentence v2
  • 在星河社区学习PARL使用强化学习来训练AI
  • C#高级编程:IO和序列化
  • linux内核主要由哪五个模块构成?
  • ultralytics 中的 RT-DETR 之 模型结构解析
  • 【python机器学习】Day 25 异常处理
  • 吴恩达机器学习笔记:多变量梯度下降
  • Permission Denied Error on Port 6277 When Starting MCP
  • 彻底解决QT5 中文编译不过问题
  • HCIP-Datacom Core Technology V1.0_1认识网络设备
  • 【unity游戏开发——编辑器扩展】EditorWindow自定义unity窗口拓展
  • AI-02a5a6.神经网络-与学习相关的技巧-批量归一化
  • Spring Boot拦截器详解:原理、实现与应用场景
  • centos7忘记root密码后使用单用户模式重置
  • 算法备案如何判断自己的产品是否具备舆论属性
  • LeetCode100.5 盛最多水的容器
  • Linux系统之----基础IO