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

做任务赚话费的网站/seo站长助手

做任务赚话费的网站,seo站长助手,dw cs4怎么做网站,网站要放备案号吗前言 JUC并发编程是Java程序猿必备的知识技能,只有深入理解并发过程中的一些原则、概念以及相应源码原理才能更好的理解软件开发的流程。在这篇文章中荔枝会梳理并发编程的基础,整理有关Java线程以及线程死锁的知识,希望能够帮助到有需要的小…

前言

        JUC并发编程是Java程序猿必备的知识技能,只有深入理解并发过程中的一些原则、概念以及相应源码原理才能更好的理解软件开发的流程。在这篇文章中荔枝会梳理并发编程的基础,整理有关Java线程以及线程死锁的知识,希望能够帮助到有需要的小伙伴~~~


文章目录

前言

一、基本概念

1.1 什么是线程

1.2 常见的三种创建线程的方式

1.3 共享变量的wait、notify、notifyAll

1.4 线程的join()、sleep()和yeild()

1.5 线程中断

二、线程死锁

2.1 线程死锁产生的四种条件

2.2 避免死锁的有效措施

2.3 守护线程和用户线程

三、ThreadLocal

3.1 threadLocals

3.2 ThreadLocal不可继承问题的解决 —— InheritableThreadLocal类

总结


一、基本概念

1.1 什么是线程

        线程是运行在进程上的单元,我们知道操作系统是将资源分配到进程上的,而线程是CPU资源分配的基本单位,或者说是占用CPU执行的基本单位更为贴切,线程栈中存放的是该线程的局部变量。进程中除了线程之外还提供了一块共享区域:堆和方法区。堆是进程中最大的一块内存,堆中存放的是所有的被new操作创建的对象示例,而方法区中则存放所有被JVM加载的类、常量和静态变量。

1.2 常见的三种创建线程的方式

常见的开启线程的方式主要有三种:继承Thread类并重写run方法、实现Runnable接口并实现run方法、实现Callable接口并实现call()方法

继承Thread类并重写run方法

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

实现Runnable接口

    public static class MyThread implements Runnable{@Overridepublic void run() {System.out.println("overwrite Runnable");}}public static void main(String[] args) {MyThread myThread = new MyThread();new Thread(myThread).start();}

实现Callable接口

    public static class MyThread implements Callable {@Overridepublic String call() throws Exception {System.out.println("overwrite callable");return "thread";}}public static void main(String[] args) throws InterruptedException{//创建异步任务FutureTask<String> futureTask = new FutureTask<>(new MyThread());//启动线程new Thread(futureTask).start();try{String result = futureTask.get();} catch (ExecutionException e) {e.printStackTrace();}}

三种线程创建方式的区别:
通过继承的方式优点:方便传参,在需要获取当前线程时使用this,而无需使用Thread.currentThread()。

通过实现接口方式创建线程优点:由于Java语言不支持多继承,所以直接通过继承的方式还存在一定的局限性; 

        三种线程创建方式唯一一个有返回值的就是实现Callable接口并使用FutureTask创建异步任务,Callable接口中的call方法是一个有返回值的方法,可以通过FutureTask对象的get方法来获得线程返回结果。

1.3 共享变量的wait、notify、notifyAll

wait()

        当一个获得共享变量的监视器锁的线程调用变量的wait(),该线程会被阻塞挂起,同时会释放当前共享变量的监视器锁,想要结束阻塞挂起状态一般可以通过其他线程在调用wait()的线程挂起后调用该共享变量地notify() | notifyAll()方法来完成线程唤醒,也可以直接中断该线程异常返回。当然了,wait()方法中还可以提供了一个超时参数,没有在指定时间被唤醒就会返回。

    public final void wait(long timeout, int nanos) throws InterruptedException {if (timeout < 0) {throw new IllegalArgumentException("timeout value is negative");}if (nanos < 0 || nanos > 999999) {throw new IllegalArgumentException("nanosecond timeout value out of range");}if (nanos > 0) {timeout++;}wait(timeout);}

 wait(long timeout, int nanos)方法的底层也是调用一个wait(long timeout),只有nanos>0才能自增timeout。

虚假唤醒

        线程的虚假唤醒指的是一个线程在没有被其它线程notify() | notifyAll() | 被中断 | 等待超时,就直接从挂起状态变为可运行状态(被唤醒)。避免虚假唤醒的方式仅需要使用一个循环不断地去获取是否达到了可唤醒线程地条件。

notify()

        前面已经提到了,线程调用共享变量的notify()方法就可以将调用变量wait()方法阻塞的线程唤醒,需要注意的是即使唤醒该线程也不一定能直接进入执行状态,而是要与其它的线程一起竞争该共享变量的锁资源。

notifyAll()

 区别于notify()只能唤醒一个线程,notifyAll可以直接唤醒该共享变量上由于调用wait方法挂起的所有线程。

1.4 线程的join()、sleep()和yeild()

sleep

阻塞挂起指定时长,结束后会退化成一个线程就绪状态,等待下一次的CPU调度。

package Thread;import jdk.nashorn.internal.runtime.regexp.joni.exception.InternalException;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class ThreadTest {//创建一个资源独占锁private static final Lock lock = new ReentrantLock();//线程竞争锁的测试public static void main(String[] args) throws InterruptedException{Thread threadA = new Thread(new Runnable() {@Overridepublic void run() {//给线程的资源上锁lock.lock();try{System.out.println("线程被激活:"+Thread.currentThread());System.out.println("threadA is sleep");Thread.sleep(3000);System.out.println("threadA is awake");}catch (InterruptedException e){e.printStackTrace();}finally {lock.unlock();}}});Thread threadB = new Thread(new Runnable() {@Overridepublic void run() {//给线程的资源上锁lock.lock();try{System.out.println("线程被激活:"+Thread.currentThread());System.out.println("threadB is sleep");Thread.sleep(3000);System.out.println("threadB is awake");}catch (InterruptedException e){e.printStackTrace();}finally {lock.unlock();}}});//启动线程threadA.start();threadB.start();}
}

运行结果

 这里我们测试一下,如果给B不加锁资源的话是否B还需要等待A释放锁,结果自然是否定的。因为此时二者竞争的就不是一个锁资源辽!

需要注意的是:线程调用sleep后是不会让出锁资源的,而仅仅是在指定时间不参与线程的调度!

join()

join()是由Thread类提供的一个无参返回方法,主要功能是阻塞主线程,等待调用线程执行完成一起将结果返回。

yeild()

        正常情况下,一个线程只有将分配给自己的时间片使用完后,线程调度器才会进行下一轮的线程调度。但是如果一个线程调用yeild()方法,就相当于该线程告诉线程调度器他将让出CPU的执行权,即使按照时间片轮转机制还剩余一部分时间片。此时该线程就会由执行状态变为就绪状态,线程调度器会重新从线程就绪队列里面获取一个线程优先级最高的线程。

调用yeild方法的时候,线程并不会被阻塞挂起,而是让出时间片后处于一种就绪状态

1.5 线程中断

Java中的线程中断是一种线程间的协作模式,通过设置线程中断的方式并不能直接终止线程,而是被中端的线程根据中断状态自行决定。

interrupt()

中断线程,这里需要注意的是调用该方法并不是实际意义上的中断线程,而是通过修改中断标志来告诉线程我现在尝试中断,但具体的决定权属于被中断的线程。

isInterrupted()

判断当前调用方法实例对象的线程是否被中断,不清除线程中断的标志

Interrupted()

判断当前执行线程是否被中断,清除中断标志。需要注意的是,interrupted()内部调用的是获取当前线程和的中断标志,而不是调用interrupted()的实例对象的中断标志。

线程中断的应用场景

        在某些场景下,线程为了等待一些特定条件到来时,一般会调用sleep、wait系例函数、或者join来阻塞挂起当前线程。但如果此时线程在挂起的时候已经满足执行条件,此时我们可以直接中断线程强制抛出一个InterruptedException异常并使其处于激活状态,线程恢复并继续在新一轮CPU调度中继续向下执行。

线程的上下文切换

        线程的上下文切换指的是:CPU的执行从一个线程调度到另一个线程的过程。一个典型的场景就是:当前线程使用完时间片后,就会处于就绪状态并让出CPU资源给其它的线程占用。


二、线程死锁

2.1 线程死锁产生的四种条件

资源互斥条件

线程堆已经获取到的资源进行排他性使用,即该资源同时只有一个线程占用。只有等到占用资源的线程释放出资源后,其它的资源请求者才能获取。

请求并持有条件

线程A已经持有至少一个资源,但A又提出了新的资源请求,那么A就会一直保持着原来持有资源的占用权。如果请求的资源已被其它的资源所占用,A就会一直阻塞着,但是不会释放当前持有的资源。

不可剥夺条件

线程获取到的资源在自己使用完主动释放前,其它的线程不能抢占。

环路等待原则

形成死锁的线程必定存在着一个线程-资源的环形链。

2.2 避免死锁的有效措施

        在操作系统的学习中我们清除,形成死锁的四个条件中只有请求并持有和环路等待条件是可以被破坏的。形成死锁其实与不同线程间资源申请的顺序有关系,要避免死锁的出现我们必须保证资源申请的有序性原则。

什么是资源申请的有序性呢?

比如A和B线程同时申请诸如1,2,3,4的资源,那么我们要保证A,B线程在申请资源的时候是按照同一个顺序来申请资源的。不管A还是B先开始申请资源,其余的线程都会阻塞直至开始申请资源的线程执行完成释放出自己所拥有的资源。 

2.3 守护线程和用户线程

守护线程和用户线程的概念区分需要基于JVM,具体的区别就是用户线程如果全部结束后JVM会退出,而守护线程是否结束并不影响JVM的退出。

设置线程为守护线程

thread.setDaemon(true);

而在Tomcat中,NIO实现的NioEndpoint中会开启一组接受线程来接受用户的连接请求、一组处理线程来负责具体处理用户请求,而这些线程其实都是守护线程! 


三、ThreadLocal

        ThreadLocal是一种在线程上维护本地变量的技术,它提供了线程的本地变量,可以将共享变量拷贝一份到线程分配到的内存空间中,虽然它的提出其实并不是用来解决多线程同时访问共享变量的线程安全问题,线程安全问题一般可以通过Java中的锁机制来解决,但是这里通过ThreadLocal提供了另外一种思路。

static ThreadLocal<String> localVariable = new ThreadLocal<>();

        通过ThreadLocal创建一个本地变量localVariable ,当线程设置localVariable中的值,其实设置的是该线程的本地内存中的一个副本。值得注意的是,每个线程的本地变量并不是放在ThreadLocal实例中的,而是放在调用线程的threadLocals变量中。

3.1 threadLocals

每个线程内部都有的成员变量,类型为HashMap,其中key是我们定义的ThreadLocal变量的this引用,value通过set方法设置的值。每个线程的本地变量都存放在自己的内存变量threadLocals中!

本地变量的生命周期

只要调用线程不终止,该本地变量就会一直存放在threadLocals变量中,这就会带来内存溢出的隐患

调用线程清除本地变量

可以通过调用ThreadLocal变量里面的remove()方法。

3.2 ThreadLocal不可继承问题的解决 —— InheritableThreadLocal类

        要想父线程本地变量可以被子线程继承使用,我们就需要借助InheritableThreadLocal类。InheritableThreadLocal继承自ThreadLocal类并重写了createMap()、getMap()、childValue()三个方法。具体的使用声明倒没有什么区别:

static InheritableThreadLocal<String> localVariable= new InheritableThreadLocal<>();

        通过查看Thread源码,我们知道它的底层逻辑实现通过重写ThreadLocal类的creatMap和getMap方法让本地变量保存到了具体线程的inheritableThreadLocals成员变量中,线程在通过InheritableThreadLocal实例的set和get的时候就会创建当前线程的inheritableThreadLocals,并在创建子线程的构造函数中将父线程的inheritableThreadLocals成员变量复制一份到子线程中。下面是该类重写的三个方法:

public class InheritableThreadLocal<T> extends ThreadLocal<T> {/*** Computes the child's initial value for this inheritable thread-local* variable as a function of the parent's value at the time the child* thread is created.  This method is called from within the parent* thread before the child is started.* <p>* This method merely returns its input argument, and should be overridden* if a different behavior is desired.** @param parentValue the parent thread's value* @return the child thread's initial value*/protected T childValue(T parentValue) {return parentValue;}/*** Get the map associated with a ThreadLocal.** @param t the current thread*/ThreadLocalMap getMap(Thread t) {return t.inheritableThreadLocals;}/*** Create the map associated with a ThreadLocal.** @param t the current thread* @param firstValue value for the initial entry of the table.*/void createMap(Thread t, T firstValue) {t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);}
}

        从Thread类中的源码中我们可以看到,Thread类在构造方法的时候就会调用init方法,而在init方法中,我们不仅获取当前父线程,通过判断inheritableThreadLocals非空,在createInheritedMap中传入父线程的inheritableThreadLocals,复制到一个ThreadLocalMap对象中


总结

        在这篇文章中,荔枝主要梳理了有关Java线程的相关知识,包括线程创建、几种线程的状态、线程死锁产生和避免、线程中断等等。也从源码角度深入地探究了ThreadLocal的原理及其应用场景,在后续的文章中荔枝也会持续输出,深入学习JUC并发编程并持续输出高质量的知识博文,希望大家能喜欢哈哈哈哈哈。

今朝已然成为过去,明日依然向往未来!我是荔枝,在技术成长之路上与您相伴~~~

如果博文对您有帮助的话,可以给荔枝一键三连嘿,您的支持和鼓励是荔枝最大的动力!

如果博文内容有误,也欢迎各位大佬在下方评论区批评指正!!!

相关文章:

JUC并发编程系列(一):Java线程

前言 JUC并发编程是Java程序猿必备的知识技能&#xff0c;只有深入理解并发过程中的一些原则、概念以及相应源码原理才能更好的理解软件开发的流程。在这篇文章中荔枝会梳理并发编程的基础&#xff0c;整理有关Java线程以及线程死锁的知识&#xff0c;希望能够帮助到有需要的小…...

双向链表相关代码

DLinkList.h // // DLinkList.hpp // FirstP // // Created by 赫赫 on 2023/10/31. // 双向链表相关代码&#xff1a;双向链表、循环双向链表#ifndef DLinkList_hpp #define DLinkList_hpp #include <stdio.h> #include <stdlib.h> #include <iostream>…...

[每周一更]-(第70期):常用的GIT操作命令

1、增删文件 # 添加当前目录的所有文件到暂存区 $ git add .# 添加指定文件到暂存区 $ git add <file1> <file2> ...# 添加指定目录到暂存区&#xff0c;包括其子目录 $ git add <dir># 删除工作区文件&#xff0c;并且将这次删除放入暂存区 $ git rm [file…...

Leetcode-283 移动零

count记录0的个数&#xff0c;不为0的数取代0位置&#xff0c;最后把剩余位置置零 class Solution {public void moveZeroes(int[] nums) {int count 0;for(int i0;i<nums.length;i){if(nums[i]0){count;}else{nums[i-count]nums[i];}}for(int inums.length-count;i<nu…...

爱上C语言:函数递归,青蛙跳台阶图文详解

&#x1f680; 作者&#xff1a;阿辉不一般 &#x1f680; 你说呢&#xff1a;生活本来沉闷&#xff0c;但跑起来就有风 &#x1f680; 专栏&#xff1a;爱上C语言 &#x1f680;作图工具&#xff1a;draw.io(免费开源的作图网站) 如果觉得文章对你有帮助的话&#xff0c;还请…...

Pycharm 对容器中的 Python 程序断点远程调试

pycharm如何连接远程服务器的docker容器有两种方法&#xff1a; 第一种&#xff1a;pycharm通过ssh连接已在运行中的docker容器 第二种&#xff1a;pycharm连接docker镜像&#xff0c;pycharm运行代码再自动创建容器 本文是第一种方法的教程&#xff0c;第二种请点击以上的链接…...

自动驾驶行业观察之2023上海车展-----车企发展趋势(3)

合资\外资发展 宝马&#xff1a;i7、iX1新车亮相&#xff0c;未来将持续发力电动化、数字化&#xff08;座舱&#xff09; 宝马在本次车展重点展示了电动化产品&#xff0c;新发车型为i7 M70L、iX1、及i vision Dee概念车等车型。 • 展示重点&#xff1a;电动化数字化&#…...

day55【动态规划子序列】392.判断子序列 115.不同的子序列

文章目录 392.判断子序列115.不同的子序列 392.判断子序列 题目链接&#xff1a;力扣链接 讲解链接&#xff1a;代码随想录讲解链接 题意&#xff1a;给定字符串 s 和 t &#xff0c;判断 s 是否为 t 的子序列。 字符串的一个子序列是原始字符串删除一些&#xff08;也可以不…...

c语言中磁盘文件的分类

#include <stdio.h> /*磁盘文件的分类&#xff1a; * 一个文件通常是磁盘上一段命名的存储区计算机的存储在物理上是二进制的&#xff0c; * 所以物理上所有的磁盘文件本质上都是一样的&#xff1a;以字节为单位进行顺序存储 * 从用户或者操作系统使用的角度&#xff08…...

Unity适配微信

使用的是微信开发的插件 GitHub - wechat-miniprogram/minigame-unity-webgl-transform 路径相关&#xff1a; Unity&#xff1a;Application.streamingAssetsPath --> 配置的cdn路径StreamingAssets...

虚拟机本地磁盘在线扩容

背景 虚拟机本地盘对于host物理机来说就是一个LVM卷,虚拟化(libvirt+kvm_qemu)已经支持虚拟机磁盘在线调整,配合物理机lvm管理工具可实现云场景下虚拟机磁盘在线扩容功能。环境检查 (1)虚拟机本地盘信息 <disk type=block device=disk><driver...

ACTIVE_MQ学习

ActiveMq学习①___入门概述https://blog.csdn.net/qq_45905724/article/details/131796502 ActiveMq学习②__安装与控制台https://blog.csdn.net/qq_45905724/article/details/133893214 ActiveMq学习③___Java编码实现ActiveMQ通讯https://blog.csdn.net/qq_45905724/articl…...

【C++初阶】类和对象(上)

【C初阶】类和对象&#xff08;上&#xff09; 1.面向对象与面向过程的初步认识2.类的引入3. 类的定义4.类的访问限定符及封装4.1访问限定符4.2封装 5.类的作用域6.类的实例化6.类的对象的大小计算7.类的this指针7.1this指针的引入7.2this指针的一些特性 &#x1f4c3;博客主页…...

新版onenet平台安全鉴权的确定与使用

根据onenet官方更新的文档&#xff1a;平台提供开放的API接口&#xff0c;用户可以通过HTTP/HTTPS调用&#xff0c;进行设备管理&#xff0c;数据查询&#xff0c;设备命令交互等操作&#xff0c;在API的基础上&#xff0c;根据自己的个性化需求搭建上层应用。 为提高API访问安…...

容器核心技术-Namespace

一、容器 基于Linux 内核的 Cgroup&#xff0c; Namespace&#xff0c;以及Union FS等技术&#xff0c;对进程进行封装隔离&#xff0c;属于操作系统层面的虚拟化技术&#xff0c;由于隔离的进程独立于宿主和其它的隔离的进程&#xff0c;因此也称其为容器。 1.1 容器主要特性…...

linux写文件如何保证落盘?

3.1.1. sync sync函数只是将所有修改过的块缓冲区排入写队列&#xff0c;然后就返回&#xff0c;它并不等待实际写磁盘操作结束。通常称为 update的系统守护进程会周期性地&#xff08;一般每隔30秒&#xff09;调用sync函数。这就保证了定期冲洗内核的块缓冲区。命令 sync也…...

2023 electron最新最简版打包、自动升级详解

这里我将讲解一下从0搭建一个electron最简版架子&#xff0c;以及如何实现打包自动化更新 之前我有写过两篇文章关于electron框架概述以及 常用api的使用&#xff0c;感兴趣的同学可以看看 Electron桌面应用开发 Electron桌面应用开发2 搭建electron 官方文档&#xff1a;ht…...

ConcurrentHashMap是如何实现线程安全的

目录 原理&#xff1a; 初始化数据结构时的线程安全 put 操作时的线程安全 原理&#xff1a; 多段锁cassynchronize 初始化数据结构时的线程安全 在 JDK 1.8 中&#xff0c;初始化 ConcurrentHashMap 的时候这个 Node[] 数组是还未初始化的&#xff0c;会等到第一次 put() 方…...

MYSQL:索引与锁表范围简述

一、聚簇索引原则 当有主键索引时&#xff0c;选择主键索引&#xff1b;如果没有主键索引&#xff0c;选择第一个的unique索引&#xff1b;如果都没有就选择隐藏生成的ROW_ID。 二、加锁原则 来自知乎MySQL探秘(七):InnoDB行锁算法 - 知乎 (zhihu.com) 在不通过索引条件查询时…...

15 款 PDF 编辑器帮助轻松编辑、合并PDF文档

PDF 编辑器在当今的数字环境中至关重要&#xff0c;因为 PDF 已成为共享和存储信息的首选格式。只需几分钟&#xff0c;可靠的 PDF 编辑器即可让用户能够根据其特定需求修改、定制和定制文档。在本文中&#xff0c;我们全面汇编了 15 款最佳免费 PDF 编辑器&#xff0c;让您可以…...

PS Raw中文增效工具Camera Raw 16

Camera Raw 16 for mac&#xff08;PS Raw增效工具&#xff09;的功能特色包括强大的图像调整工具。例如&#xff0c;它提供白平衡、曝光、对比度、饱和度等调整选项&#xff0c;帮助用户优化图像的色彩和细节。此外&#xff0c;Camera Raw 16的界面简洁易用&#xff0c;用户可…...

Flink源码解析二之执行计划⽣成

JobManager Leader 选举 首先flink会依据配置获取RecoveryMode,RecoveryMode一共两两种:STANDALONE和ZOOKEEPER。 如果用户配置的是STANDALONE,会直接去配置中获取JobManager的地址如果用户配置的是ZOOKEEPER,flink会首先尝试连接zookeeper,利用zookeeper的leadder选举服务发现…...

MySQL遍历所有表所有字段查找字符数据

MySQL遍历所有表所有字段查找字符数据 工作中有一些数据查找&#xff0c;但是在那个库那个表那个字段中并不明确&#xff0c;特别是敏感字符查找&#xff0c;如果数据量并不大&#xff0c;我们可以采用遍历整个库、表中字符来查找相关数据来解决该问题。 我们可以写一个存储过…...

Java中的异常处理机制是怎样的?

Java中的异常处理机制主要包括以下几个部分&#xff1a; 异常类&#xff08;Exception Class&#xff09;&#xff1a;Java中的异常类继承自java.lang.Throwable&#xff0c;主要分为两大类&#xff1a;Error和Exception。Error表示程序无法处理的严重问题&#xff0c;如系统崩…...

高教社杯数模竞赛特辑论文篇-2023年A题:定日镜场的优化设计(附获奖论文及MATLAB代码实现)

目录 摘 要 一、 问题重述 1.1 问题背景 1.2 问题重述 二、 模型假设 三、 符号说明...

c语言实现http下载功能,显示进度条和下载速率

#include <stdio.h>//printf #include <string.h>//字符串处理 #include <sys/socket.h>//套接字 #include <arpa/inet.h>//ip地址处理 #include <fcntl.h>//open系统调用 #include <unistd.h>//write系统调用 #include <netdb.h>//…...

Educational Codeforces Round 157 (Rated for Div. 2) D. XOR Construction (思维题)

题目 给定长为n-1(n<2e5)的整数序列a&#xff0c;第i个数a[i](0<a[i]<2n) 构造一个长为n的整数序列b&#xff0c;满足&#xff1a; 1. 0到n-1在b数组中每个数恰好出现一次 2. 对于&#xff0c; 题目保证一定有解&#xff0c;有多组时可以输出任意一组 思路来源 …...

【unity实战】实现类似英雄联盟的buff系统

文章目录 先来看看最终效果前言开始BUFF系统加几个BUFF测试1. 逐层消失&#xff0c;升级不重置剩余时间的BUFF2. 一次性全部消失&#xff0c;升级重置剩余时间的BUFF3. 永久BUFF&#xff0c;类似被动BUFF4. 负面BUFF&#xff0c;根据当前BUFF等级计算每秒收到伤害值&#xff0c…...

【C语言基础教程】函数指针与指针大小

文章目录 前言一、函数指针1.1 函数指针的概念1.2 三个示例代码示例1: 使用函数指针调用不同的函数示例 2: 使用函数指针实现回调函数示例 3: 使用函数指针数组 二、指针的大小2.1 前述2.2 指针大小如何决定&#xff1f;两方面理解 总结 前言 在C语言中&#xff0c;指针是一项…...

Web前端—网页制作(以“学成在线”为例)

版本说明 当前版本号[20231105]。 版本修改说明20231105初版 目录 文章目录 版本说明目录day07-学成在线01-项目目录02-版心居中03-布局思路04-header区域-整体布局HTML结构CSS样式 05-header区域-logo06-header区域-导航HTML结构CSS样式 07-header区域-搜索布局HTML结构CSS…...