☀️苏州程序大白一文解析Java多线程☀️《❤️记得收藏❤️》
目录
🏳️🌈开讲啦!!!!🏳️🌈苏州程序大白🏳️🌈 |
🌟博主介绍
💂 个人主页:苏州程序大白
🤟作者介绍:中国DBA联盟(ACDU)成员,CSDN全国各地程序猿(媛)聚集地管理员。目前从事工业自动化软件开发工作。擅长C#、Java、机器视觉、底层算法等语言。2019年成立柒月软件工作室。
💬如果文章对你有帮助,欢迎关注、点赞、收藏(一键三连)和C#、Halcon、python+opencv、VUE、各大公司面试等一些订阅专栏哦
🎗️ 承接各种软件开发项目
💅 有任何问题欢迎私信,看到会及时回复
👤 微信号:stbsl6,微信公众号:苏州程序大白
🎯 想加入技术交流群的可以加我好友,群里会分享学习资料
基本概念
程序:是为完成特定任务,用某种语言编写的一组指令的集合,即指一段静态的代码,静态对象。
进程:是程序的一次执行过程,或是正在运行的一个程序,是一个动态的过程,有它自身的产生,存在和消亡的过程。-------生命周期。
线程:进程中的一个任务,是一个程序内部的一条执行路径
即:线程《 进程(一个进程可以有多个线程)
程序:静态的代码
-
进程:动态执行的程序。
-
线程:进程中要同时干几件事时,每一件事的执行路径成为线程。
-
并行:多个CPU同时执行多个任务,比如 : 多个线程一起执行。
-
并发:一个CPU(采用时间片)同时执行多个任务,比如:多个线程间隔执行。
线程的相关Api
Thread thread = new Thread();
thread.start(); //启动当前线程,调用当前线程中的run方法,注意并不是立即执行,只是进入就绪状态等待资源的调用
thread.sleep(5000); //休眠5s
thread.getName(); //获取当前线程的名字,也可以Thread.currentThread().getName()
thread.setName(""); //设置当前线程的名字,也可以Thread.currentThread().setName()
Thread.currentThread(); //返回当前线程
Thread.yield(); //主动释放当前线程的执行权
thread.join(); //执行过程中插入该线程,只有插入线程执行完毕,主线程才会继续执行
thread.isAlive(); //判断线程是否存活
thread.setPriority(1); //设置线程的优先级,有三个等级: MAX_PRIORITY:10 MIN_PRIORITY:1 NORM_PRIORITY:5
thread.getPriority(); //获取线程的优先级
thread.setDaemon(false); //设置守护线程,主线程结束守护线程也结束 false,true
thread.isDaemon(); //判断是否是守护线程
thread.wait(); //强行停止当前线程,进入阻塞状态
thread.notify(); //唤醒一个被wait停止线程的线程
thread.notifyAll(); //唤醒所有被wait停止的停止线程
多线程和单线程
单线程示例:
public class SingleThread {
public void method1(){
System.out.println("SingleThread.method1");
}
public void method2(){
method1();
}
public static void main(String[] args) {
//一条线执行
SingleThread single = new SingleThread();
single.method2();
}
}
多线程示例:
public class ManyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
public static void main(String[] args) {
ManyThread manyThread = new ManyThread();
new Thread(manyThread,"线程A").start();
new Thread(manyThread,"线程B").start();
}
}
线程的创建
继承Thread类
1、创建一个继承Thread类的子类 (通过ctrl+o 快速找到run方法)。
2、重写Thread类的run()方法。
3、创建Thread子类的对象。
4、通过此对象调用start()方法。
public class ManyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
public static void main(String[] args) {
ManyThread manyThread = new ManyThread();
manyThread.start();
}
}
实现Runnable接口
1、创建一个实现了Runnable接口的类。
2、实现类去实现Runnable中的抽象方法:run()。
3、创建实现类的对象。
4、将此对象作为参数传递到Thread类中的构造器中,创建Thread类的对象。
5、通过Thread类的对象调用start()。
public class TestRunable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(i);
}
}
public static void main(String[] args) {
TestRunable runable = new TestRunable();
new Thread(runable).start();
}
}
比较创建线程的两种方式:
开发中,优先选择实现Runable接口的方式。
原因:
1、适合多个相同的程序代码的线程去处理同一个资源。
2、可以避免java中的单继承的限制。
3、增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。
联系:Thread也是实现Runnable接口,两种方式都需要重写run()方法,将线程要执行的逻辑声明在run中。
实现callable接口
1、创建一个实现了callable接口的类。
2、实现call方法,将此线程需要执行的操作声明在call()中。
3、创建实现类的对象。
4、将callable接口实现类的对象作为传递到FutureTask的构造器中,创建FutureTask的对象。
5、将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start方法启动(调用FutureTask对象的get方法获取返回值)。
public class TestCallable implements Callable {
@Override
public Object call() throws Exception {
for (int i = 0; i < 5; i++) {
System.out.println(i);
}
return "线程执行完毕";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallable callable = new TestCallable();
FutureTask<String> task = new FutureTask<String>(callable);
new Thread(task).start();
System.out.println(task.get());
}
}
线程的生命周期
1、新建状态(New)
:新创建了一个线程对象。
2、就绪状态(Runnable)
:线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
3、运行状态(Running)
:就绪状态的线程获取了CPU,执行程序代码。
4、阻塞状态(Blocked)
:阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)、等待阻塞
:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
(二)、同步阻塞
:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞
:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5、死亡状态(Dead
):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
线程同步
线程安全问题:
线程安全问题是指,多个线程对同一个共享数据进行操作时,线程没来得及更新共享数据,从而导致另外线程没得到最新的数据,从而产生线程安全问题。
解决方法:使用同步监视器(锁)synchronized 、lock(ReentrantLock)
同步代码块:
public class ManyThread extends Thread{
private static int count = 100; //一百张票,共同的资源
@Override
public void run() {
//同步代码块
synchronized (this){
while (count > 0) {
try {
Thread.sleep(1000); //休眠一秒
System.out.println(Thread.currentThread().getName()+"卖出了一张票,余票"+(--count));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
ManyThread manyThread = new ManyThread();
new Thread(manyThread,"窗口1").start();
new Thread(manyThread,"窗口2").start();
new Thread(manyThread,"窗口3").start();
new Thread(manyThread,"窗口4").start();
}
}
同步方法:
public class ManyThread extends Thread{
private static int count = 100; //一百张票,共同的资源
//创建ReentrantLock锁对象
private Lock lock = new ReentrantLock();
//使用synchronized同步方法
@Override
public synchronized void run() {
while (count > 0) {
try {
Thread.sleep(1000); //休眠一秒
System.out.println(Thread.currentThread().getName()+"卖出了一张票,余票"+(--count));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
ManyThread manyThread = new ManyThread();
new Thread(manyThread,"窗口1").start();
new Thread(manyThread,"窗口2").start();
new Thread(manyThread,"窗口3").start();
new Thread(manyThread,"窗口4").start();
}
}
lock锁的使用:
public class ManyThread extends Thread{
private static int count = 100; //一百张票,共同的资源
//创建ReentrantLock锁对象
private Lock lock = new ReentrantLock();
@Override
public void run() {
lock.lock();
while (count > 0) {
try {
Thread.sleep(1000); //休眠一秒
System.out.println(Thread.currentThread().getName()+"卖出了一张票,余票"+(--count));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lock.unlock();
}
public static void main(String[] args) {
ManyThread manyThread = new ManyThread();
new Thread(manyThread,"窗口1").start();
new Thread(manyThread,"窗口2").start();
new Thread(manyThread,"窗口3").start();
new Thread(manyThread,"窗口4").start();
}
}
Synchronized与lock的区别
相同:二者都可以解决线程安全问题。
不同:synchronized机制在执行完相应的代码逻辑以后,自动的释放同步监视器lock需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unlock())(lock的方式更为灵活)。
线程死锁
理解:两个人过独木桥,走到桥中央谁都不让谁。
public class Deadlock {
public static void main(String[] args) {
//准备两把锁
Object o1 = new Object();
Object o2 = new Object();
new Thread(new Runnable() {
@Override
public void run() {
//先拿第一把锁
synchronized (o1){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("o1过了一半");
synchronized (o2){
System.out.println("o1过完桥了");
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
//先拿第一把锁
synchronized (o2){
System.out.println("o2过了一半");
synchronized (o1){
System.out.println("o2过完桥了");
}
}
}
}).start();
}
}
解决死锁的办法:
1、减少同步共享变量。
2、采用专门的算法,多个线程之间规定先后执行的顺序,规避死锁问题。
3、减少锁的嵌套。
线程通信
线程间通信可以通过共享变量+wait()
+notify()
来实现。
wait()
将线程进入阻塞状态,notify()
将线程唤醒,notifyAll()
唤醒所有的线程。
使用前提:这三个方法均只能使用在同步代码块或者同步方法中:
wait 和 sleep的区别?
二者都会让线程进入阻塞状态,有以下区别:
wait是Object的方法 sleep是Thread的方法。
wait会立即释放锁 sleep不会释放锁。
wait后线程的状态是Watting sleep后线程的状态为 Time_Waiting。
生产者消费者模型:指的是有生产者来生产数据,消费者来消费数据,生产者生产满了就不生产了,通知消费者取,等消费了再进行生产。
public class Test {
public static void main(String[] args) {
Factory msg = new Factory();
Produce produce = new Produce(msg);
Consumer consumer = new Consumer(msg);
new Thread(produce,"工厂A").start();
new Thread(produce,"工厂B").start();
new Thread(produce,"工厂C").start();
new Thread(consumer,"店铺D").start();
new Thread(consumer,"店铺E").start();
}
}
/**
* 消息
*/
class Factory{
int count = 0; //库存
//生产一台电脑
public synchronized void add() throws InterruptedException {
while (count <= 10){
//设置最大库存数
if (count >= 10){
wait();
//防止溢出
continue;
}
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"生产了一台电脑,库存:"+(++count));
notifyAll();
}
}
//消费一台电脑
public synchronized void pull() throws InterruptedException {
while (count >= 0){
if (count <= 0){
wait();
continue;
}
Thread.sleep(500);
System.out.println(Thread.currentThread().getName()+"消费了一台电脑,库存:"+(--count));
notifyAll();
}
}
}
/**
* 生产者
*/
class Produce implements Runnable{
private Factory factory;
public Produce(Factory factory) {
this.factory = factory;
}
@Override
public void run() {
try {
factory.add();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 消费者
*/
class Consumer implements Runnable{
private Factory factory;
public Consumer(Factory factory) {
this.factory = factory;
}
@Override
public void run() {
try {
factory.pull();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
🌟作者相关的文章、资源分享🌟
🌟让天下没有学不会的技术🌟
学习C#不再是难问题
🌳《C#入门到高级教程》🌳
有关C#实战项目
👉C#RS232C通讯源码👈
👉C#委托数据传输👈
👉C# Modbus TCP 源代码👈
👉C# 仓库管理系统源码👈
👉C# 欧姆龙通讯Demo👈
👉C#+WPF+SQL目前在某市上线的车管所摄像系统👈
👉2021C#与Halcon视觉通用的框架👈
👉2021年视觉项目中利用C#完成三菱PLC与上位机的通讯👈
👉VP联合开源深度学习编程(WPF)👈
✨有关C#项目欢迎各位查看个人主页✨
🌟机器视觉、深度学习🌟
学习机器视觉、深度学习不再是难问题
🌌《Halcon入门到精通》🌌
🌌《深度学习资料与教程》🌌
有关机器视觉、深度学习实战
👉2021年C#+HALCON视觉软件👈
👉2021年C#+HALCON实现模板匹配👈
👉C#集成Halcon的深度学习软件👈
👉C#集成Halcon的深度学习软件,带[MNIST例子]数据集👈
👉C#支持等比例缩放拖动的halcon WPF开源窗体控件👈
👉2021年Labview联合HALCON👈
👉2021年Labview联合Visionpro👈
👉基于Halcon及VS的动车组制动闸片厚度自动识别模块👈
✨有关机器视觉、深度学习实战欢迎各位查看个人主页✨
🌟Java、数据库教程与项目🌟
学习Java、数据库教程不再是难问题
🍏《JAVA入门到高级教程》🍏
🍏《数据库入门到高级教程》🍏
有关Java、数据库项目实战
👉Java经典怀旧小霸王网页游戏机源码增强版👈
👉js+css类似网页版网易音乐源码👈
👉Java物业管理系统+小程序源码👈
👉JavaWeb家居电子商城👈
👉JAVA酒店客房预定管理系统的设计与实现SQLserver👈
👉JAVA图书管理系统的研究与开发MYSQL👈
✨有关Java、数据库教程与项目实战欢迎各位查看个人主页✨
🌟分享Python知识讲解、分享🌟
学习Python不再是难问题
🥝《Python知识、项目专栏》🥝
🥝《Python 检测抖音关注账号是否封号程》🥝
🥝《手把手教你Python+Qt5安装与使用》🥝
🥝《用一万字给小白全面讲解python编程基础问答》🥝
🥝《Python 绘制Android CPU和内存增长曲线》🥝
有关Python项目实战
👉Python基于Django图书管理系统👈
👉Python管理系统👈
👉2021年9个常用的python爬虫源码👈
👉python二维码生成器👈
✨有关Python教程与项目实战欢迎各位查看个人主页✨
🌟分享各大公司面试题、面试流程🌟
面试成功不是难事
🍏《2021年金九银十最新的VUE面试题☀️《❤️记得收藏❤️》》🍏
🍏《只要你认真看完一万字☀️Linux操作系统基础知识☀️分分钟钟都吊打面试官《❤️记得收藏❤️》》🍏
🍏《❤️用一万字给小白全面讲解python编程基础问答❤️《😀记得收藏不然看着看着就不见了😀》》🍏
✨有关各大公司面试题、面试流程欢迎各位查看个人主页✨