多线程【进阶版】

news2024/11/19 15:26:20

目录

一. 常见的锁策略

1.1 乐观锁和悲观锁

1.2 轻量级锁和重量级锁

1.3 自旋锁和挂起等待锁

1.4 互斥锁和读写锁

1.5 可重入锁和不可重入锁

1.6 公平锁和非公平锁

1.7 关于锁策略的相关面试题

二. CAS

三. Synchronized 原理

3.1 基本特点

3.2 加锁步骤

3.3 锁消除 

3.4 锁粗化 

3.5 JUC常见组件

 四. 线程安全的集合类

4.1 HashTable 和 ConcurrentHashMap的区别

4.2 多线程相关面试题


一. 常见的锁策略

1.1 乐观锁和悲观锁

说到乐观和悲观这两个概念,大家都不陌生,生活中我们也要常常面对一些乐观和悲观的时候,但是这是站在自身的角度去看待的,有的人看待一件事他认为是乐观的,而有的人认为他是悲观的;这里的 "乐观" 和 "悲观" 和我们说的乐观锁和悲观锁也是很相似的;

乐观锁:每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据;

举个例子:假设有A 和 B 两个线程,他们要去获取数据,因为他们是乐观的,所以双方不会认为他们去修改数据,所以他们就拿到数据后执行各自的事情去了,还有一个特点就是线程A和B在更新共享数据之前,他们要去判断这个共享数据是否被其他线程修改,如果没有修改的话,那么就直接更新内存中共享变量的值,那如果被修改了,就会报错或者去执行其他相关的操作了


悲观锁:每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁;

举个例子:还是有A 和 B两个线程,A 和 B要去拿数据,因为它是悲观的,所以在拿数据时需要进行加锁,假设A拿到了锁,那么B就会进入阻塞等待的状态,知道A释放锁,CPU会唤醒等待的线程B,B才能拿到这个锁,从而对数据进行操作;


总体来说,悲观锁一般要做的工作多一点,效率会更低一些;而乐观锁要做的事少一点,效率更高一点; 


1.2 轻量级锁和重量级锁

轻量级锁:加锁和解锁的过程中更快更高效;

重量级锁:加锁和解锁的过程中更慢更低效;

这里看来,轻量级,重量级虽然和乐观,悲观不是一回事,但是有那么一定的相似,可以认为一个乐观锁可能是一个轻量级锁,但不是绝对的;关于这块后面还是会细说;


1.3 自旋锁和挂起等待锁

自旋锁是轻量级锁的一种代表实现,当一个线程尝试去获取某一把锁的时候,如果这个锁此时已经被别人获取(占用),那么此线程就无法获取到这把锁,该线程将会等待,间隔一段时间后会再次尝试获取。这种采用循环加锁 -> 等待的机制被称为自旋锁(spinlock);

优点:自旋锁一旦被释放,就能第一时间拿到锁,自旋锁是纯用户态操作,所以速度很快;

缺点:要一直等待,会消耗CPU资源

挂起等待锁是重量级锁的一直代表实现,当某个线程没有申请到锁的时候,此时该线程会被挂起,即加入到等待队列等待。当锁被释放的时候,就会被唤醒,重新竞争锁;

优点:不需要盲等,在等待的过程中可以参与别的事情,充分利用了CPU的资源;

缺点:如果锁被释放,不能第一时间拿到锁,挂起等待锁是通过内核的机制来实现,所以时间会更长,效率会更低;


1.4 互斥锁和读写锁

互斥锁:互斥锁是一个非常霸道的存在,比如有线程A,B,当线程 A 加锁成功后,此时互斥锁已经被线程 A 独占了,只要线程 A 没有释放手中的锁,线程 B 加锁就会失败,于是就会释放 CPU 让给其他线程,既然线程 B 释放掉了 CPU,自然线程 B 加锁的代码就会被阻塞,

我们学过的Synchronized就是互斥锁;

对于互斥锁加锁失败而阻塞的现象,是由操作系统内核实现的。当加锁失败时,内核会将线程置为「睡眠」状态,等到锁被释放后,内核会在合适的时机唤醒线程,当这个线程成功获取到锁后,于是就可以继续执行。如下图:

注:互斥锁和自旋锁最大的区别:

  • 互斥锁加锁失败后,线程会释放 CPU ,给其他线程;
  • 自旋锁加锁失败后,线程会忙等待,直到它拿到锁;

读写锁:它由 读锁 和 写锁 两部分构成,如果只读取共享资源用 读锁 加锁,如果要修改共享资源则用 写锁 加锁。

读写锁一般有 3 种情况:

1.给读加锁

2.给写加锁

3.解锁

读写锁中的约定:

  • 读锁和读锁之间,不会有锁竞争,不会产生阻塞等待
  • 写锁和写锁之间,有锁竞争
  • 读锁和写锁之间,有锁竞争

1.5 可重入锁和不可重入锁

针对一个线程,针对一把锁,连续加锁两次,如果出现死锁了,那就是不可重入锁,如果不死锁,那就是可重入锁;

Object locker = new Object();

synchronized(locker){
     synchronized(locker){
   }
}

像上述这样的代码就是加锁两次的情况,第二次加锁需要等待第一个锁释放,第一个锁释放,需要等待第二个锁加锁成功,所以这种情况就矛盾了,但是并不会真正的死锁,因为synchronized是可重入锁,加锁的时候会先判定一下,看当前尝试申请锁的线程是不是已经拥有锁了,如果是的话,就不会矛盾;

synchronized 和   ReentrantLock 都是可重入锁,可重入锁最大的意思就是为了防止死锁;


1.6 公平锁和非公平锁

 首先从字面意思理解,先到的线程会优先获取资源,后到的会进行排队等待,这种是公平的,而非公平锁是不遵循这个原则的,其实也很好理解,看下图:

这种情况就是公平的,遵循先到先得的规矩;

 而像这种情况,就是非公平的,存在 "插队" 的现象;


1.7 关于锁策略的相关面试题

1. 你是怎么理解乐观锁和悲观锁的,具体怎么实现呢?

悲观锁认为多个线程访问同一个共享变量冲突的概率较大, 会在每次访问共享变量之前都去真正加锁;

乐观锁认为多个线程访问同一个共享变量冲突的概率不大,并不会真的加锁, 而是直接尝试访问数据. 在访问的同时识别当前的数据是否出现访问冲突;

悲观锁的实现就是先加锁(比如借助操作系统提供的 mutex), 获取到锁再操作数据. 获取不到锁就等待;

2. 介绍下读写锁?

读写锁就是把读操作和写操作分别进行加锁;

读锁和读锁之间不互斥;

写锁和写锁之间互斥;

写锁和读锁之间互斥;

读写锁最主要用在 "频繁读,不频繁写" 的场景中;

3. 什么是自旋锁,为什么要使用自旋锁策略呢,缺点是什么?

如果获取锁失败, 立即再尝试获取锁, 无限循环, 直到获取到锁为止. 第一次获取锁失败, 第二次的尝试会在极短的时间内到来. 一旦锁被其他线程释放, 就能第一时间获取到锁.

优点: 没有放弃 CPU 资源, 一旦锁被释放就能第一时间获取到锁, 更高效. 在锁持有时间比较短的场景下非常有用.
缺点: 如果锁的持有时间较长, 就会浪费 CPU 资源


4. synchronized 是可重入锁么?
是可重入锁.
可重入锁指的就是连续两次加锁不会导致死锁

5. synchronized的特点:

  1. 既是乐观锁,也是悲观锁
  2. 既是轻量级锁,也是重量级锁
  3. 轻量级锁是基于自选锁实现的,重量级锁是基于挂起等待锁实现的
  4. 不是读写锁
  5. 是可重入锁
  6. 是非公平锁


二. CAS

1. 概念

全称Compare and swap,比较 内存A 和 寄存器R 中的数据值,如果数值相同,就把 内存B和 寄存器R 的数值进行交换;

2. 特点

CAS最大的特点就是一个CAS操作是单条CPU指令,它是原子的,所以既是CAS不加锁,也能保证线程的安全;

在JDK1.8中针对于CAS提供了一个类:


CAS中的ABA问题:

ABA问题是CAS中的面试的高频问题,我们都知道,CAS是对比内存和寄存器的值,看看是否相同,就是通过这个对比,来检测内存是不是改变过,要么相同,要么不同,不同都好区别,但是有一种相同不是真正意义上的相同,而是不确定这个值中间是否发生过改变,改变了原来的东西,但是变回到原来的状态,假设原来是a,然后变成b,后来又变成a,这就是 a->b->a 问题了;


如何解决ABA问题呢???

ABA问题实质上就是一个反复横跳的问题,我们只要约定数据只能单方面变化,要么数据只能增加,要么数据只能减小,那么问题就迎刃而解了;

如果我们要求数据既能增大又能减小,我们可以约定一个版本号变量,约定版本号只能增加,并且每次修改,都会增加一个版本号,这样我们每次对比的时候就是拿版本号去对比,而不是对比数值本身,这样也能很好的解决问题了;


三. Synchronized 原理

3.1 基本特点

  • 1. 开始时是乐观锁, 如果锁冲突频繁, 就转换为悲观锁.
  • 2. 开始是轻量级锁实现, 如果锁被持有的时间较长, 就转换成重量级锁.
  • 3. 实现轻量级锁的时候大概率用到的自旋锁策略
  • 4. 是一种不公平锁
  • 5. 是一种可重入锁
  • 6. 不是读写锁

3.2 加锁步骤

过程:

  1. 最开始是没有锁的;
  2. 刚开始加锁,是一个偏向锁状态;
  3. 遇到锁竞争,就转化为轻量级锁状态(自旋锁);
  4. 竞争激烈时,就会变成重量级锁,这一过程交给内核阻塞等待;

关于偏向锁:

偏向锁并非是真的加锁,只是简单的标记一下,想占有这把锁,如果没有锁竞争,那就不加锁,如果有别的线程来竞争这把锁,那么就升级成轻量级锁,这样做既保证了效率,又保证了线程安全;


3.3 锁消除 

锁消除是编译阶段做的一种优化手段,用来检测当前代码是否多线程任务 或者 是否有必要加锁,如果没有必要,又把锁给写了,就会在编译过程中自动把锁去掉;

假如在不涉及线程安全的问题时,我们用 synchronized 关键字对一个操作进行加锁了,那么在编译阶段,就会自动把锁进行一个消除,锁消除这个机制是一个智能化的操作,它会根据不同的代码,去判断当前的操作需不需要进行加锁,如果不需要,就会自动消除;


3.4 锁粗化 

提到锁的粗化,就要先提到一个概念叫锁的粒度,锁的粒度就是 synchronized 代码块里包含代码的多少,一般认为代码越多,粒度越粗,代码越少,粒度越细;

通常写代码的情况下,我们是希望锁的粒度更小一点,因为这样串行的代码少,并发执行的代码就越多;

举个例子:

假设我们给领导打电话汇报3份工作,分两种情况:

1. 先打个电话,汇报 A 的进展,再挂电话

    再打个电话,汇报 B 的进展,再挂电话

    再打个电话,汇报 C 的进展,再挂电话

每次领导接电话就是一个加锁的过程,别人(其他线程)想要给领导打电话就是处于一个阻塞等待的状态,挂电话就是释放锁;当你挂断电话后,再想去汇报工作B的进展,你不一定能打进去,领导可能和别人正在通话,这样一来,你再想打进去就要阻塞等待一会,这个过程就相当于把锁的粒度拆分的更细了,但是每次都可能会阻塞等待,这样效率并不高,还可能并发其他的问题;

2. 打通一次电话,直接把A,B,C的工作进展一次性想领导汇报;

这样就避免了阻塞等待的消耗了,也大大的提升了效率;


3.5 JUC常见组件

JUC是 Java.util.concurrent 的缩写,这里是多线程并发的一个类;

1. Callable接口

这里写一个程序去实现一下这个接口:

public class Demo16 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 创建任务,计算从 1 加到 100 的和
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for (int i = 0; i < 100; i++) {
                    sum += i;
                }
                return sum;
            }
        };
        // 创建一个线程来完成这个任务
        // Thread 不能直接传 callable, 外面需要再包装一层
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        Thread thread = new Thread();
        thread.start();
        System.out.println(futureTask.get());

    }
}

上述代码也是线程创建的一种方法;


2. ReentrantLock类

可重入互斥锁 和 synchronized 定位类似,,都是用来实现互斥效果,,保证线程安全;

但是 synchronzied 是基于代码块实现来控制加锁和解锁,而 ReentrantLock 是提供了 lock 和unlock  的方法来进行加锁和解锁;

虽然 synchronized 已经在绝大多数情况下满足使用了,但是 ReentrantLock 也有自己特殊的方法:

  1. 使用 synchronized 加锁的时候如果发现锁被占有,会进入阻塞状态,而 ReentrantLock 提供了一个 tryLock 方法,如果发现锁被占用,不会阻塞,直接返回 false;
  2.  synchronized 是一个非公平锁(不遵循规则),而 ReentrantLock 提供了公平和非公平两种工作模式;
  3.  synchronized 搭配 wait 和 notify 进行唤醒等待,如果多个线程 wait 同一个对象,notify 的时候随机唤醒一个,而 ReentrantLock 搭配 Condition 这个类进行唤醒等待,并且它能指定一个线程唤醒;

 四. 线程安全的集合类

4.1 HashTable 和 ConcurrentHashMap的区别

我们常用的ArrayList,LinkedList,HashMap等等这样的类都是线程不安全的,那如果我们在多线程环境下要使用,可能就会出问题;

针对这种情况,标准库里提供了线程安全版本的集合类,据我了解,从早期的线程安全的集合说起,它们是 Vector 和 HashTable:

Vector:

Vector 和 ArrayList 类似,是长度可变的数组,与 ArrayList 不同的是,Vector 是线程安全的,它给几乎所有的 public 方法都加上了 synchronized 关键字。由于加锁导致性能降低,在不需要并发访问同一对象时,这种强制性的同步机制就显得多余,所以现在 Vector 已被弃用;

CopyOnWriteArrayList 和 CopyOnWriteArraySet:

它们是加了写锁的 ArrayList 和 ArraySet,锁住的是整个对象,但读操作可以并发执行;

HashTable:

HashTable 和 HashMap类似,不同点是 HashTable 是线程安全的,它给几乎所有 public 方法都加上了 synchronized 关键字,还有一个不同点是 HashTable 的 K,V 都不能是 null ,但 HashMap 可以,它现在也因为性能原因被弃用了;

HashTable 和 ConcurrentHashMap的区别:

HashTable 是针对整个哈希表加锁,任何的 CURD 操作都可能会触发加锁,也可能有锁竞争;而 ConcurrentHashMap 是针对每个链表进行加锁,每次进行操作,都是针对对应链表进行加锁,操作不同链表就是针对不同的锁对象加锁,此时不会有锁冲突,没有锁冲突,就没有阻塞等待,这样也提升了效率;


4.2 多线程相关面试题

(1)谈谈 volatile关键字的用法?

volatile 能够保证内存可见性, 强制从主内存中读取数据,此时如果有其他线程修改被 volatile 修饰的变量,可以第一时间读取到最新的值;
 

(2)Java多线程是如何实现数据共享的?
JVM 把内存分成了这几个区域:方法区,堆区,栈区,程序计数器,其中堆区这个内存区域是多个线程之间共享的,只要把某个数据放到堆内存中, 就可以让多个线程都能访问到;


(3)Java线程共有几种状态?状态之间怎么切换的?

  • NEW: 安排了工作, 还未开始行动. 新创建的线程, 还没有调用 start 方法时处在这个状态;
  • RUNNABLE: 可工作的. 又可以分成正在工作中和即将开始工作. 调用 start 方法之后, 并正在CPU 上运行/在即将准备运行 的状态;
  • BLOCKED: 使用 synchronized 的时候, 如果锁被其他线程占用, 就会阻塞等待, 从而进入该状态;
  • WAITING: 调用 wait 方法会进入该状态;
  • TIMED_WAITING: 调用 sleep 方法或者 wait(超时时间) 会进入该状态;
  • TERMINATED: 工作完成了. 当线程 run 方法执行完毕后, 会处于这个状态;

(4)Thread和Runnable的区别和联系?

      Thread 类描述了一个线程,Runnable 描述了一个任务,在创建线程的时候需要指定线        程完成的任务, 可以直接重写 Thread 的 run 方法, 也可以使用Runnable 来描述这个任          务;


(5)进程和线程的区别?

  • 进程是包含线程的. 每个进程至少有一个线程存在,即主线程。
  • 进程和进程之间不共享内存空间. 同一个进程的线程之间共享同一个内存空间.
  • 进程是系统分配资源的最小单位,线程是系统调度的最小单位

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

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

相关文章

利用宏简化Q_PROPERTY动态属性的定义

目录 写在前面实现历程传统定义方式预想的方式(事实上有一点点区别) 例程mainwindow.hmainwindow.cppmain.cpp 执行结果如上事实的使用方法 写在前面 上一篇写了pyqt如何更加便利地定义动态属性&#xff0c;关于C版的其实在我刚接触Qt不久就想过并做了一些尝试&#xff0c;但死…

C. Vus the Cossack and Strings(异或判断二进制位匹配数奇偶)

Problem - C - Codeforces 题目描述 Vus the Cossack has two binary strings, that is, strings that consist only of "0" and "1". We call these strings aa and bb . It is known that |b| \leq |a|∣b∣≤∣a∣ , that is, the length of bb is at m…

手把手教你学习IEC104协议和编程实现 十二-读取定值

不废话,直接接上一章开始。忘记的复习一下上一章。 读取多个/全部定值 结构 设计思路 先说一下,读取多个定值的思路,首先。。。我靠,好长时间不看这一部分的程序,忘记了,待我先好好捋一捋。。。 言归正传,首先我们要选择我们要调用的定值。如下图: 大概就这样举个例…

2023年数据治理工程师证书有哪些值得考?DAMA-CDGA/CDGP

DAMA认证为数据管理专业人士提供职业目标晋升规划&#xff0c;彰显了职业发展里程碑及发展阶梯定义&#xff0c;帮助数据管理从业人士获得企业数字化转型战略下的必备职业能力&#xff0c;促进开展工作实践应用及实际问题解决&#xff0c;形成企业所需的新数字经济下的核心职业…

CDR2023新版本安装下载教程

CDR2023是最新发布的一款强大且专业的图像设计软件,适用于平面设计、装饰设计、产品包装设计等领域。coco玛奇朵其主要功能如下: 1. 矢量图形设计:提供强大的绘图与编辑工具,支持创建和编辑各种矢量图形,如线条、曲线、矩形、椭圆、星形等。 2. 文字编辑:提供文字输入、编辑与…

Docker资源控制和镜像

目录 第一章Docker的资源控制 1.2.对内存使用的限制 1.3.对磁盘IO配额控制&#xff08;blkio&#xff09;的限制 第二章Docker 的数据管理 2.1.数据卷 2.2.数据卷容器 2.3.端口映射 2.4.容器互联&#xff08;使用centos镜像&#xff09; 第三章.Docker 镜像的创建 3.…

java 实现对称加密的方法

对称加密是一种加密算法&#xff0c;它和非对称加密的区别在于&#xff1a;加密和解密使用不同的密钥&#xff0c;对称加密使用同一个密钥对数据进行加密和解密。 今天我们来学习下对称加密的实现方法&#xff0c;不需要使用到私钥&#xff0c;只需要用到公钥。 首先我们来看下…

气传导蓝牙耳机优缺点有哪些?气传导耳机科普及推荐

气传导蓝牙耳机&#xff0c;可以说是目前使用场景最全的种类&#xff0c;个人认为&#xff0c;这种耳机也同时是目前最好的耳机体验形态&#xff0c;将会是主流的长时间佩戴的耳机形态&#xff0c;甚至是未来智能耳机终极形态。 不过目前大多数人都还没有接触过这类蓝牙耳机&a…

污水处理厂的工艺流程有哪些?

对于普通人来说&#xff0c;一个完整的污水处理过程是相当神秘的&#xff0c;那么&#xff0c;今天我们来聊一下&#xff0c;一个完整的污水处理厂的工艺流程&#xff0c;都包括了哪方面的内容&#xff0c;一起来了解一下吧&#xff01; 一、污水处理厂的工艺流程 1、先进行污…

项目管理中,管理者如何打造团队执行力?

作为一名优秀的团队管理者&#xff0c;首先应该以身作则&#xff0c;成为团队的榜样&#xff0c;并将自己的良好工作作风带入团队中&#xff0c;影响团队成员&#xff0c;以真诚的态度打动每一位成员&#xff0c;促进团队之间的默契合作。 团队执行力的最重要原因通常与团队的…

项目管理工具哪家使用更便捷?

项目管理作为企业管理中的一个重要分支&#xff0c;受到越来越多企业及商业人士的关注。事实上&#xff0c;项目管理是基于有限的技能、人力、物质资源和其他资源&#xff0c;完成一个项目的过程。项目管理软件和工具哪家好&#xff1f;Zoho Projects项目管理工具可以帮助企业提…

【应急响应】拒绝服务钓鱼指南DDOS压力测试邮件反制分析应用日志

文章目录 内网应急-日志分析-爆破&横向&数据库红队APT-钓鱼邮件-内容&发信人&附件拒绝攻击-DDOS&CC-代理&防火墙防御 内网应急-日志分析-爆破&横向&数据库 1、协议口令爆破事件(以SQLserver、RDP为例) 查看SQL server日志文件(可以看到密码爆…

女程序员晒出11月的工资条:工资是高,但是真累,说老十岁一点也不过分

现在网上又好多人晒工资。有服务员、工人、护士、教师还有“程序猿”。程序员在大多数人眼里都是“高智商”&#xff0c;高薪资&#xff0c;那程序员到底一月的工资能开多少呢? 前两天有位女程序员在网上晒出了11月的工资条&#xff0c;说&#xff1a; 工资是高(比起有些人算…

jenkins——参数化配置、插件安装以及Jenkins SSH 插件安装

文章目录 一、Jenkins 参数化配置参数化 Job 的配置 二、插件安装1、插件安装入口2、插件管理代理3、插件新装4、插件更新5、插件移除和禁用 三、Jenkins SSH 插件安装1、下载 SSHAgent 插件2、SSHAgent 相关使用 一、Jenkins 参数化配置 同一个项目需要在不同环境下测试 参数…

智能工厂技术架构、系统架构、数据架构、应用架构及场景应用方案

本资料来源公开网络&#xff0c;仅供个人学习&#xff0c;请勿商用&#xff0c;如有侵权请联系删除。 流程制造-智能工厂规划设计工作总体视图 流程制造-智能工厂总体业务框架 根据业务调研和分析&#xff0c;勾勒出流程制造-智能工厂的总体业务框架&#xff0c;涵盖计划经营…

Netty核心源码分析(一),Netty的Server端启动过程源码分析

文章目录 系列文章目录一、Netty的Server端启动过程源码分析1、NioEventLoopGroup的创建&#xff08;1&#xff09;构造方法 2、ServerBootstrap的创建&#xff08;1&#xff09;构造方法&#xff08;2&#xff09;group方法&#xff08;3&#xff09;channel方法&#xff08;4…

机器学习——结构风险最小化(SRM)

问&#xff1a;基于结构风险最小化 (SRM)原则会倾向于选择更复杂的模型&#xff0c;因此其泛化能力一般较差。 答&#xff1a;错误&#xff0c;基于经验风险最小化&#xff08;ERM&#xff09;会倾向于选择更复杂的模型。基于结构风险最小化原则的模型选择会考虑模型的复杂度和…

水泥工厂 3D 可视化大屏,全工艺流程数字孪生,破局产线低效运转!

近年来&#xff0c;随着我国经济的发展和人民生活水平的提高&#xff0c;我国对于水泥行业的关注程度也越来越高&#xff0c;为了保证水泥行业的健康稳定发展&#xff0c;许多地方都在大力推动水泥生产技术创新工作。当前水泥行业的发展正处于新旧动能更迭的关键阶段&#xff0…

String类的学习笔记(上):介绍String类及其常用方法的使用

本文介绍了Java中用来描述操作字符串的String类,和其一些常用的基本操作方法,字符串的创建输出,字符串对象的比较,字符串查找,字符串的转化,字符串的替换,字符串拆分,字符串截取,和大小写转换,去除左右空格,子字符串包含,学会使用这些方法,能更方便的使用操作字符串~ String类的…

c++积累6-内联函数

1、说明 内联函数是c为提高程序运行速度所做的一项改进。 2、常规函数运行 编译的可执行程序&#xff1a;由一组机器语言指令组成。 程序执行&#xff1a; 1、操作系统将这些指令载入到内存&#xff0c;每条指令都有一个特定的内存地址 2、计算机逐步执行这些指令 3、如果有…