多线程看这一篇文章就够了

news2024/11/25 4:29:56

第一章 多线程概述

1
2
3
1. 什么是程序?
2. 什么是进程?
3. 什么是线程?
  • 程序
1
是为完成特定任务、用某种语言编写的一组指令的集合(一段静态的代码)
  • 进程
1
是程序的一次执行过程,或是正在运行的一个程序
  • 线程
1
2
3
进程可进一步细化为线程,是一个程序内部的一条执行路径

若一个程序可同一时间执行多个线程,我们称之为多线程.
  • 多线程的使用
1
2
3
4
5
我们在什么情况下使用多线程呢?

1. 程序需要同时执行多个任务的时候(2个或2个以上)
2. 程序需要实现一些需要等待的任务时(例如360杀毒软件中有很多功能,但是这些功能都没有被调用,都在等待被调)
3. 后台运行的程序

第二章 多线程创建和使用

1
2
3
Java线程创建有两种方式
1. 一种是继承Thread类
2. 一种是实现Runnable接口

第1节 继承Thread类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class TestDemo1 {

    public static void main(String[] args) {

        //创建线程
        MyThread mt = new MyThread();
        //启动线程
        mt.start();
    }

}

/**
 * 编写自定义类实现Thread类,重写run方法
 * run方法由jvm虚拟机调用,待抢到CPU资源之后才会执行
 */
class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("我被线程调用了...");
    }
}

第2节 实现Runnable接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class TestDemo2 {
    public static void main(String[] args) {
        //创建MyRunnable实例
        MyRunnable mr = new MyRunnable();
        //创建线程
        Thread t = new Thread(mr);
        //启动
        t.start();
    }
}
class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("我被线程调用了...");
    }
}

第三章 线程的优先级

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* Thread类中自带优先级设置 最小优先级
*/
public final static int MIN_PRIORITY = 1;

/**
* Thread类中自带优先级设置 默认优先级
*/
public final static int NORM_PRIORITY = 5;

/**
* Thread类中自带优先级设置 最大优先级
*/
public final static int MAX_PRIORITY = 10;
1
2
3
4
5
6
Thread类中提供了设置和获取优先级的方法

setPriority(): 设置优先级(取值范围1-10)
getPriority(): 获取优先级方法

在线程中设置线程优先级不会出现明显的变化,优先级高的话只是分配到CPU资源的概率高.

第四章 Thread类常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
start():线程启动方法
run(): 线程被调度时执行的方法
getName(): 返回线程名称
setName(String name): 设置线程名称
currentThread(): 返回当前线程
yield(): 线程让步
	暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程
	若队列中没有同优先级的线程,忽略此方法
	线程让步,不代表暂停执行,就像你在高速开车,让了路,不代表车停下来,让给其他线程,CPU可能还会分配给它资源.
join():当某个程序执行中调用其他线程的join()方法时,调用线程将被阻塞,直到join()方法加入的join线程执行完为止.低优先级的线程也可以获得执行
sleep(long millis): 令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后重排队
stop(): 强制线程生命期结束(已过时)
isAlive(): 返回boolean,判断线程是否还活着
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//join测试

public class TestDemo01 {
    public static void main(String[] args) throws InterruptedException {
        TaskRnnable t = new TaskRnnable();
        Thread t1 = new Thread(t, "线程1");
        Thread t2 = new Thread(t, "线程2");
        t1.start();
        t1.join();//join之后必须t1结束之后才能执行t2
        t2.start();
    }
}

class TaskRnnable implements Runnable{
    @Override
    public void run() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"执行了....");
    }
}

第五章 线程的生命周期

1
线程的生命周期状态: 创建 -- 就绪 -- 运行 -- 停止 -- 阻塞

 

第六章 线程同步

1
2
3
4
5
6
7
8
9
因为多线程执行的不确定性引起执行结果的不稳定性,可能造成数据出现问题.

Java中引入互斥锁的概念,来保证共享数据操作的完整性.每个对象都对应于一个可称为"互斥锁" 的标记;这个标记用来保证在任一时刻,只能有一个线程访问该对象.

在Java中使用synchronized关键字来实现互斥锁
1. 同步代码块
	锁对象: 1.1 实例对象(锁实例数据)  1.2 静态对象(锁静态数据)
2. 同步方法
	2.1 实例方法(锁为this)  2.2 静态方法(锁为当前类本身)

模拟火车站售票程序,开启三个窗口售票

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//通过这个案例,多次执行会发现他的数据执行顺序是有问题的.
public class TestDemo3 {
    public static void main(String[] args) {
        //创建车票
        Ticket ticket = new Ticket();
        //创建线程并启动
        new Thread(ticket,"win1").start();
        new Thread(ticket,"win2").start();
        new Thread(ticket,"win3").start();
    }
}
class Ticket implements Runnable{
    private int ticket=100;
    @Override
    public void run() {
        while(true){
            if(ticket>0){
                System.out.println("窗口-> "+ Thread.currentThread().getName() +" <- 买票"+ticket+"剩余票数为:"+ --ticket);
            }else {
                break;
            }
        }
    }
}

解决问题

  • 同步代码块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
synchronized (锁对象){
	//需要锁定的资源
}

public class TestDemo3 {
    public static void main(String[] args) {
        //创建车票
        Ticket ticket = new Ticket();
        //创建线程并启动
        new Thread(ticket,"win1").start();
        new Thread(ticket,"win2").start();
        new Thread(ticket,"win3").start();
    }
}
class Ticket implements Runnable{

    private int ticket=100;

    @Override
    public void run() {
        while(true){
          	//锁定共享资源
            synchronized (this){
                if(ticket>0){
                    System.out.println("窗口-> "+ Thread.currentThread().getName() +" <- 买票"+ticket+"剩余票数为:"+ --ticket);
                }else {
                    break;
                }
            }
        }
    }
}
  • 同步方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//使用synchronized修饰操作共享资源的方法

public class TestDemo4 {
    public static void main(String[] args) {
        //创建车票
        Ticket1 ticket = new Ticket1();
        //创建线程并启动
        new Thread(ticket,"win1").start();
        new Thread(ticket,"win2").start();
        new Thread(ticket,"win3").start();
    }
}
class Ticket1 implements Runnable{
    private int ticket=100;
    @Override
    public void run() {
        while(true){
            sale();
        }
    }
  	//给操作共享资源的方法添加synchronized
    public synchronized void sale(){
        if(ticket>0){
            System.out.println("窗口-> "+ Thread.currentThread().getName() +" <- 买票"+ticket+"剩余票数为:"+ --ticket);
        }else{
            return;
        }
    }
}

第七章 死锁

1
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
public class DeadLock implements Runnable {
	private static Object obj1 = new Object();
	private static Object obj2 = new Object();
	private boolean flag;
	public DeadLock(boolean flag) {
		this.flag = flag;
	}
	@Override
	public void run() {	
		if(flag) {
			synchronized (obj1) {
				System.out.println(Thread.currentThread().getName()+"已经锁定obj1");
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				//如果出现死锁当前执行不到
				synchronized (obj2) {
					System.out.println("一秒钟后"+Thread.currentThread().getName()+"已经锁定obj2");
				}
			}
		}else {
			synchronized (obj2) {
				System.out.println(Thread.currentThread().getName()+"已经锁定obj2");
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				//如果死锁访问不到
				synchronized (obj1) {
					System.out.println("一秒钟后"+Thread.currentThread().getName()+"已经锁定obj1");
				}
			}
		}		
	}
}

//main方法
/**
 * 死锁
 *
 * obj1
 * obj2
 * 
 * A线程
 * run(){
 * 	synchronized(obj1){
 * 		//代码
 * 		synchronized(obj2){
 * 			//
 * 		}	
 * 	}
 * }
 * B线程
 * run(){
 * 	synchronized(obj2){
 * 		//代码
 * 		synchronized(obj1){
 * 			//
 * 		}	
 * 	}
 * }
 */
public class TestDemo01 {
	public static void main(String[] args) {
		//new Thread(new DeadLock(true)).start();
		new Thread(new DeadLock(false)).start();//死锁
		new Thread(new DeadLock(true)).start();
	}
}

第八章 线程通信

第1节 为什么要进行线程通信

多个线程并发执行时,在默认情况下CPU是随机切换线程的,当我们需要多个线程来共同完成一件任务.并且我们希望他们有规律的执行,那么多线程之间需要一些协调通信

第2节 Java语言实现通信的方式

java.lang.Object类中提供了wait()/notify/notifyAll()方法实现线程之间的通信;这三个方法必须在synchronized方法或synchronized代码块中才能使用,否则会报java.lang.IllegalMonitorStateException异常
  • wait()方法
当前线程挂起并放弃CPU,释放对锁的拥有权(在wait时必须先获取锁,所以wait必须在synchronized中),同时在等待的位置加一个标志,以备后面被唤醒时它好能从标志位置获得锁的拥有权,变成就绪状态.
  • notify()方法
1
唤醒一个等待当前对象的锁的线程
  • notifyAll()方法
1
方法会唤醒在此对象监视器上等待的所有线程

第3节 练习(使用两个线程打印 1-100 线程1, 线程2 交替打印)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class PrintNum implements Runnable{
	int num=1;
	@Override
	public void run() {
		while(true) {
			synchronized (this) {
				notify();//唤醒
				try {
					Thread.sleep(100);
				} catch (InterruptedException e1) {
					e1.printStackTrace();
				}
				if(num<=100) {
					System.out.println(Thread.currentThread().getName()+"-->"+ num);
					num++;
					try {
						//等待
						wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}			
				}else {
					break;
				}
			}
		}
	}
}

public static void main(String[] args) {
		/*
		 * 创建两个线程,循环1-100
		 */
		PrintNum p = new PrintNum();
		new Thread(p).start();
		new Thread(p).start();
	}

第九章 生产着消费者模式

  • 容器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class Box {
	//设置盒子容量
	private int c=0; //最大值10
	/**
	 * 向盒子中添加对象
	 */
	public synchronized void add() {
		if(c>=10) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}else {
			System.out.println("生产产品"+c);
			c++;
			notifyAll();
		}
	}
	/**
	 * 获取
	 */
	public synchronized void get() {
		if(c<=0) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}else {
			System.out.println("消费产品"+c);
			c--;
			notifyAll();
		}	
	}	
}
  • 生产者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
 * 生产线程
 */
public class Pro implements Runnable{
	Box box;
	public Pro(Box box) {
		this.box = box;
	}
	@Override
	public void run() {
		System.out.println("生产开始....");
		while(true) {
			try {
				Thread.sleep((int)Math.random()*1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}		
			box.add();
		}
	}
}
  • 消费者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
 * 消费线程
 */
public class Cus implements Runnable {
	Box box;
	public Cus(Box box) {
		this.box = box;
	}
	@Override
	public void run() {
		System.out.println("消费开始...");
		while(true) {
			try {
				Thread.sleep((int)Math.random()*1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}		
			box.get();
		}
	}
}
  • 测试
1
2
3
4
5
6
7
8
9
10
11
12
public static void main(String[] args) {
		//创建容器	
		Box box = new Box();
		//创建生产者
		Pro pro = new Pro(box);
  	//创建消费者
		Cus cus = new Cus(box);
		//给生产者分配线程
		new Thread(pro).start();
  	//给消费者分配线程
		new Thread(cus).start();
	}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/141716.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软考网络工程师上午常考点

软考网络工程师上午常考点&#xff1a; **计算机硬件基础&#xff1a;**根据考纲分析&#xff0c;本章主要考查三个模块&#xff1a;计算机体系结构、存储系统、I/O输入输出系统&#xff0c;其中每一模块又分若干知识点。“计算机硬件基础”相当于软考中的“公共基础课”&…

12月更新 | Visual Studio Code Python

我们很高兴地宣布&#xff0c;2022年12月发布的适用于 Visual Studio Code Python 和 Jupyter 扩展现已推出&#xff01;此版本包括以下改进&#xff1a;Pylance 自动缩进 预览&#xff1a;浏览器中运行与调试 Python社区提供新扩展&#xff1a;Ruff如果您有兴趣&#xff0c;可…

C51单片机基础之串口编程实战

目录 一、串口编程寄存器分析 1、PCON : 电源控制寄存器 2、SCON&#xff1a;串行控制寄存器 二、自己实现串口初始化编程 三、发送一串字符串给到PC端编程 四、PC发送指令控制LED编程 五、串口中断实时控制LED编程 1、串口中断的中断号&#xff1a;interrupt4 2、串口…

Apache Doris Join 实现与调优实践|未来源码

推荐语&#xff1a; SQL 的支持力度和粒度&#xff0c;已经作为今天所有大数据计算引擎的重要衡量标准之一&#xff0c;而 SQL 的所有操作&#xff0c;可以分为简单操作&#xff08;如 where、limit等 filter 操作&#xff09;和复杂操作&#xff08;groupby、join 等聚合操作&…

SpringCloud微服务项目实战 - 3.App端文章

经历了新冠疫情始末&#xff0c;之后&#xff0c;多出门走走&#xff0c;看看山&#xff0c;看看海&#xff0c;吃好吃的 系列文章目录 项目搭建App登录及网关App文章 文章目录系列文章目录一、文章列表1. 数据库⑴. 导入文章数据库⑵. 表结构分析①. ap_article 文章基本信息…

MySQL慢SQL探究

文章目录前言1、慢SQL捕获慢查询追踪配置方式2、情况分析为什么查询会慢&#xff1f;2.1 SQL执行计划分析explain执行计划分析PROFILE分析OPTIMIZER_TRACE分析3、引擎参数配置分析I/O性能分析MySQL I/O参数其他原因分析网络抖动单表数据量过大总结前言 我们在日常开发中&#…

GeoServer搭建私有地图服务,Cesium引擎加载。

一、安装JDK 1、安装GeoServer是基于Java的环境&#xff0c;所以需要先装Jdk环境。 2、前往官网下载Java SE 3、下载GeoServer 1、前往官网下载最新稳定版 2、安装GeoServer 二、发布地图服务 1、启动geoserver 找到安装目录&#xff0c;bin/下的startup.bat,双击执行…

PF-Net源码详解

代码及数据见最后 1.参数配置 参数配置使用默认参数即可,但是windows操作系统下,需要将--workers设置为0. 2.数据准备 PF-Net将不完整的点云做输入,并预测其缺失部分。同时,我们可以从整体流程看到,输入有三个尺度,通过最远点采样进行构建,预测的输出也有三个尺度。数…

MySQL:SQL通用语法、SQL分类、DDL、DML、DQL、DCL

一.SQL通用语法 SQL语句可以单行或多行书写&#xff0c;以分号结尾&#xff1b;SQL语句可以使用空格/缩进来增强语句的可读性&#xff1b;MySQL数据库SQL语句不区分大小写&#xff0c;关键字建议大写&#xff1b;注释&#xff1a; 单行&#xff1a;--或#多行&#xff1a;/* …

Spring AOP 面向切面编程

1.AOP是什么我们之前学过 OOP 面向对象编程, 那么 AOP 是否就比 OOP 更牛逼呢? 是否也是面向过程到面向对象这种质的跨越呢? 其实不是, 它和 OOP 思想是一种互补的关系, 是对 OOP 思想的一种补充.AOP (Aspect Oriented Programming) : 面向切面编程, 它是一种思想, 它是对某一…

Arrays数组

1.Arrays.toString()方法:输出数组内容 2.Arrays.sort()方法:给数组排序&#xff0c;默认升序 对其他对象数组进行排序 一个对象数组&#xff0c;排序算法需要重复比较数组中的元素。不同的类比较元素的规则是不同的&#xff0c;但是排序算法只应该调用类提供的比较方法&#…

netty中channelHandler实现原理及最佳实践|极客星球

为持续夯实MobTech袤博科技的数智技术创新能力和技术布道能力&#xff0c;本期极客星球邀请了企业服务研发部工程师梁立从 TCP 的粘包/半包、 Netty 处理粘包/半包及源码分析、 开源项目对 channelHandler最佳实践三方面对《netty 中channelHandler的原理与最佳实践》进行了全面…

【Ctfer训练计划】——(九)

作者名&#xff1a;Demo不是emo 主页面链接&#xff1a;主页传送门创作初心&#xff1a;舞台再大&#xff0c;你不上台&#xff0c;永远是观众&#xff0c;没人会关心你努不努力&#xff0c;摔的痛不痛&#xff0c;他们只会看你最后站在什么位置&#xff0c;然后羡慕或鄙夷座右…

Python+Selenium4元素定位_web自动化(3)

目录 0. 上节回顾 1. 八大定位 2. 定位器 3. CSS选择器 4. XPath选择器 4.1. XPath语法 4.2. XPath 函数 5. 相对定位 5.1 XPath 中的相对定位【重点】 5.1.1 相对路径 5.1.2 轴 5.2 selenium4 中的相对定位 总结 0. 上节回顾 浏览器的一般操作 浏览器的高级操作…

【sciter】:JSX 组件实现数据持久化

# 原理 组件数据持久化指的是:重新加载组件后,能否将重新加载前组件所存在的数据,在重新加载后数据依旧保存在组件中。 组件数据持久化实现原理:将每次更新组件数据同步到 Storage 中。并且监听组件重新加载(刷新),在刷新前将 Storage 关闭(确保数据不丢失)。当加载…

idea中添加git使用时文件不同颜色,标签不同颜色,代码不同颜色代表的含义

文章目录文件的颜色标签的颜色合并代码时不同颜色区块的含义文件的颜色 绿色——已经加入控制暂未提交&#xff1b; 红色——未加入版本控制&#xff1b;自己建立新文件后就是红色的&#xff0c;出现红色的一定要Add到git中&#xff0c;不然不能上传到远程仓库 蓝色——加入&am…

关于markdown相关语法的学习

众所周知&#xff0c;一个好的项目需要搭配一个好的项目说明&#xff0c;就行吃饺子需要蘸醋一样&#xff0c;没有醋的饺子&#xff0c;你仅仅吃掉了他的肉体&#xff0c;而得不到他的灵魂。下面开始吃饺子&#xff0c;不对&#xff0c;是开始学习markdown文件的基础语法&#…

在采购管理过程中使用技术有什么好处?

采购过程不总是简单直接的&#xff0c;人工采购过程非常耗费人力和时间&#xff0c;并且涉及大量文书工作。另一方面&#xff0c;当你在采购过程中使用技术时&#xff0c;比如使用SRM采购管理系统&#xff0c;会节省很多时间&#xff0c;使整个过程变得更加简单和轻松。 在讨…

Homekit智能家居创意DIY之智能吸顶灯

买灯要看什么因素 好灯具的灯光可以说是家居的“魔术师”&#xff0c;除了实用的照明功能外&#xff0c;对细节的把控也非常到位。那么该如何选到一款各方面合适的灯呢&#xff1f; 照度 可以简单理解为清晰度&#xff0c;复杂点套公式来说照度光通量&#xff08;亮度&#…

【达梦8】vm 虚拟机centos 7 安装达梦8 数据库

目录准备下载安装包版本选择安装前准备【登录root用户】创建用户【登录root用户】设置限制资源配置【登录dmdba用户】上传iso文件挂载iso创建安装路径开始安装【登录dmdba用户】安装【登录dmdba用户】初始化实例初始化注意事项开始初始化启动数据库启动方式1 &#xff08;推荐&…