【JavaEE】浅谈线程(一)

线程

  • 前言
  • 线程的由来
  • 线程是什么
    • 线程的属性
    • 线程更高效的原因
    • 举个例子(线程便利性的体现)
  • 多线程代码
    • 线程并发执行的代码
    • jconsole(观测多线程)
  • 线程的调度问题
  • 创建线程的几种方法
    • 1)通过继承Thread 重写run
    • 2)使用Runnable接口 重写run 有利于解耦合
    • 3)匿名内部类
    • 4)Runnable匿名内部类
    • 5)Lambda表达式
  • 总结


前言

线程的由来

现如今的CPU存在多个核心,因此能够同时处理多个进程,我们也称为并发执行(并行+并发)。通过这样的“多进程模式”,能够很好的利用多核CPU,极大的提升了效率。
但是,多进程的编程虽然好用,但也会带来一些问题。
在服务器中,需要给多个客户端提供服务,如果同时来了多个客户端,如此一来,只能用多个进程处理这件事情。这样会导致服务器频繁进行创建+销毁进程的操作,这是极其影响效率的。
于是引入了"轻量级进程"——线程。线程的创建和开销会小的多。

线程是什么

线程,可以理解为“线程的一部分”;一个进程中可以包含多个线程。
在进程中存在PCB结构体,一个PCB代表着一个线程,而一个进程由多个PCB构成,若干个PCB联合在一起,描述了一个进程。举个例子来说:进程像是一个小区,而线程就是一个个住在小区里的人。

线程的属性

因为线程存在与进程中,因此他们共用一个PCB,为了区分线程之间的区别,设置了一个新的概念词tgid(tgd)。同一个进程的tgid是相同的而pid是不同的;线程们共用同一份内存指针和文件描述符表。
因为线程们共用内存资源和文件资源的原因,线程之间可以访问到彼此的数据。而每个线程都是在独立的CPU上调度执行。因此我们可以认为:线程是系统调度执行的基本单位,进程是系统资源分配的基本单位。

线程更高效的原因

进程在创建和销毁过程中需要进行资源分配和资源释放的过程,而线程存在于进程中,线程之间共用资源,在线程创建和销毁的过程中可以省略资源分配/释放的步骤,省去了申请资源的过程,因此线程是更高效,更轻量级的一种进程。

举个例子(线程便利性的体现)

现在有一项任务需要完成,我们可以选择创建一个进程来解决,如果这个任务太繁重了,我们可以选择创建多个进程来分担压力。这种方法好是好,但是进程的创建和销毁会占用到资源,造成资源的浪费。
但是现在,我们学过线程了!我们可以选择在进程中创建多个线程,同时通过多核CPU的特点让线程可以达到并发执行的效果,这样既不会有申请资源的烦恼,又能高速高效的执行任务,可谓是一举多得!(鼓掌)
当然说归说,我们不能想到这么方便,直接在进程中创建多个线程叠罗汉不就好了,这肯定是不对的。俗话说的好:物极必反。如果我们一味的引入,自然会引发其他的问题(且听下回分解)

多线程代码

在JVM中已经对线程封装好了,即Thread类。我们可以通过Thread类实现线程的创建。接下来我写一份简单的代码进行演示

class MyThread extends Thread {@Overridepublic void run() {System.out.println("hello thread");}
}
public class ThreadDemo1{public static void main(String[] args) {Thread t = new MyThread();t.start();}
}

如何理解这段代码呢?首先创建了MyThread类并继承Thread方法,实现动态绑定。接着重写Thread的run方法;run()方法中包含的是线程t要执行的方法;而t.start()方法是启动线程t的方法。这段代码中有两个线程:t线程和main线程。由此我们可以知道,在学习线程之前的代码中,只存在着一个线程:main线程。同时我们也称main线程为主线程。
当然,run()是start()创建出来的线程,在线程里被调用的,run()方法已经创建好而不去手动调用,将这个方法交给系统/其他的库调用,即“回调函数”。

线程并发执行的代码

或许在上面的代码中我们不能很好的看出来t线程和main线程是怎么体现并发执行的,因此这边我分别在两个线程中加入两个循环以求更好的体现出线程并发执行的特点。

class MyThread extends Thread {@Overridepublic void run() {while (true){System.out.println("hello thread");}}
}
public class ThreadDemo1{public static void main(String[] args) {Thread t = new MyThread();t.start();while (true){System.out.println("hello main");}}
}

在这里插入图片描述
在这里我们也看到了 “hello thread” 和 "hello main"线程在控制台上交替打印,这也代表着两个线程正在并发执行。

jconsole(观测多线程)

jconsole是java的一款观测线程的实用工具,我已在其他文章中编写好,需要的可以观看下面的文章。
jconsole的简单使用

线程的调度问题

在上面的线程循环代码中我们已经可以证明多线程是并发执行的。
那么他们执行的顺序是否存在规律呢?
我们可以使用Thread.sleep()方法进行简单的观测。

Thread.sleep()可以设置时间使线程停止接下来的工作,使线程进入休眠状态(阻塞)。在规定的时间过后线程继续运行,执行未完成的任务。

代码如下

class MyThread extends Thread {@Overridepublic void run() {while (true){System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
public class ThreadDemo1{public static void main(String[] args) {Thread t = new MyThread();t.start();while (true){System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

部分运行结果如下:
在这里插入图片描述
从这部分运行结果我们可以看出来,线程的运行使无序的。在操作系统中也称之为**“抢占式执行”。任何一个线程,在执行到任何一个代码的过程中,都可能被其他的线程抢占掉它的cpu资源,于是cpu就给别的线程执行了。
如果是这样的结果,显然这样的抢占式执行充满了随机性,执行效果难以预测甚至
可能引入bug。**这肯定是程序猿们不想看到的,这涉及到了线程的安全问题。

创建线程的几种方法

1)通过继承Thread 重写run

在上面的代码中已经展示,这边不再赘述。

2)使用Runnable接口 重写run 有利于解耦合

在Runnable接口中,我们可以通过查看源码看到,其中只有一个抽象类方法run(),因此Runnable的作用就是描述一个任务,一个行为。**无论是线程还是其他方法执行run()这个方法都是可以的**。

通过Runnable方法实现的多线程有一个好处:解耦合
在第一种写法中,线程重写自己的run()方法,以及线程调用的都是自己本身的方法;而使用Runnable方法之后,在后续进行代码的改动将会降低成本。

在这里插入图片描述
下面给出一个举例

class MyRunnable implements Runnable{@Overridepublic void run() {while (true){System.out.println("hello runnable");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
public class ThreadDemo2{public static void main(String[] args) {Thread t = new Thread(new MyRunnable());t.start();while (true){System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

3)匿名内部类

继承Thread 重写run,创建Thread的子类(不知名),同时也创建了一个不具名子类实例。我们看看下面的代码:

public class ThreadDemo3{public static void main(String[] args) {Thread t1 = new Thread(){@Overridepublic void run() {System.out.println("使用匿名内部类");}};t1.start();}
}

4)Runnable匿名内部类

与Thread匿名内部类的写法类似,在new Runnable之后直接重写Runnable的方法,这也创建了一个实例(不知名)实现了Runnable接口的方法。
演示代码如下:

public class ThreadDemo4 {public static void main(String[] args) {Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("匿名内部类使用Runnable");}});t1.start();System.out.println("hello main");}
}

5)Lambda表达式

通过使用lambda表达式代替需要重写的run方法。

public class ThreadDemo5 {//lambda表达式//lambda本质上也是一个匿名函数,用完就丢public static void main(String[] args) {Thread t = new Thread(() ->{while (true){System.out.println("hello thread");}});t.start();System.out.println("hello main");}
}

总结

线程是一个轻量级进程,能够很大程度上节约成本和资源,在开发中有着十分重要的作用。同样的,引入多线程意味着我们需要解决一些引入所带来的问题。(如线程安全问题)这是随着我们逐渐深入了解所需要做的。
需要源码的可以点击链接:线程代码
有帮助到大家的请三连~感谢!!

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软考117-上午题-【计算机网络】-杂题+小结

一、杂题 真题1: 真题2: 真题3: 真题4: 真题5: 真题6: 真题7: 真题8: 真题9: 真题10: 真题11: 真题12: 真题13: 真题14&a…

sqlmap(五)

一、进行文件读写操作 1.1 前提条件 高权限 目录有读写权限 secure_file_priv " " 1.2 测试目标 第一步:用抓包的方式获取请求测试站点的数据包 可以使用Burpsuite 第二步:将抓到的数据包,保存到sqlmap目录下的a.txt 第三步&am…

租用阿里云4核16G服务器优惠价格多少钱?

阿里云4核16G服务器优惠价格26.52元1个月、79.56元3个月、149.00元半年,配置为阿里云服务器ECS经济型e实例ecs.e-c1m4.xlarge,4核16G、按固定带宽 10Mbs、100GB ESSD Entry系统盘,活动链接 aliyunfuwuqi.com/go/aliyun 活动链接打开如下图&a…

JS 利用 webcam访问摄像头 上传到服务器

webcam JS 较为详细的指南 定义标题 <!doctype html> <html> <head><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>How to capture picture from webcam with Webcam.js</title></…

PTA C 1050 螺旋矩阵(思路与优化)

本题要求将给定的 N 个正整数按非递增的顺序&#xff0c;填入“螺旋矩阵”。所谓“螺旋矩阵”&#xff0c;是指从左上角第 1 个格子开始&#xff0c;按顺时针螺旋方向填充。要求矩阵的规模为 m 行 n 列&#xff0c;满足条件&#xff1a;mn 等于 N&#xff1b;m≥n&#xff1b;且…

【保姆级教程】如何在 Windows 上实现和 Linux 子系统的端口映射

写在前面 上次分享【保姆级教程】Windows上安装Linux子系统&#xff0c;搞台虚拟机玩玩&#xff0c;向大家介绍了什么是虚拟机以及如何在Windows上安装Linux虚拟机。对于开发同学而言&#xff0c;经常遇到的一个问题是&#xff1a;很多情况下代码开发需要依赖 Linux 系统&…

Centos7下docker安装jenkins【使用docker-compose图文教程】

个人记录 前置条件&#xff1a;安装Docker与Docker-compose Centos7安装Docker与Docker-compose【图文教程】 查看jenkins最新的版本 https://www.jenkins.io/download/ 配置docker-compose.yml vim docker-compose.yml按i进行编辑模式&#xff0c;粘贴如下内容。把image里…

常见现代卷积神经网络(Pytorch 09)

本章将介绍现代的 卷积神经网络架构&#xff0c;许多现代卷积神经网络的研究都是建立在这一章的基础上的。在本章中的每一个模型都曾一度占据主导地位&#xff0c;其中许多模型都是 ImageNet竞赛 的优胜者。ImageNet竞赛自2010年以来&#xff0c;一直是计算机视觉中监督学习进展…

.NET8 和 Vue.js 的前后端分离

在.NET 8中实现前后端分离主要涉及到两个部分&#xff1a;后端API的开发和前端应用的开发。后端API通常使用ASP.NET Core来构建&#xff0c;而前端应用则可以使用任何前端框架或技术栈&#xff0c;比如Vue.js、React或Angular等。下面是一个简化的步骤指南&#xff0c;帮助你在…

深入浅出 -- 系统架构之Keepalived搭建双机热备

Keepalived重启脚本双机热备搭建 ①首先创建一个对应的目录并下载keepalived安装包&#xff08;提取码:s6aq&#xff09;到Linux中并解压&#xff1a; [rootlocalhost]# mkdir /soft/keepalived && cd /soft/keepalived [rootlocalhost]# wget https://www.keepalived.…

隐私计算实训营学习九:隐语多方安全计算在安全核对的行业实践

文章目录 一、业务背景&#xff1a;安全核对产生的土壤二、产品方案&#xff1a;从试点到规模化的路三、技术共建&#xff1a;与隐语的共同成长 一、业务背景&#xff1a;安全核对产生的土壤 业务背景&#xff1a;很多粗放使用数据的方式被新出台的法律法规所规范&#xff0c;…

基于vue+node.js导师选择分配管理系统

开发语言 node.js 框架&#xff1a;Express 前端:Vue.js 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat 开发软件&#xff1a;VScode .设计一套导师选择管理系统&#xff0c;帮助学校进行导师选择管理等繁琐又重复的工作&#xff0c;提高工作效率的同时&#xff0c…