AQS(AbstractQueuedSynchronizer)

一、什么是AQS

AQS的全称为(AbstractQueuedSynchronizer)抽象的队列式的同步器,用来构建锁或者其他同步组件的基础框架类。使⽤AQS能简单且⾼效地构造出应⽤⼴泛的⼤量的同步器,如:基于AQS实现的lock、CountDownLatch、CyclicBarrier、Semaphore需解决的问题。

AQS核⼼思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的⼯作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占⽤,那么就需要⼀套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是⽤CLH(虚拟的双向队列)队列锁实现的,即将暂时获取不到锁的线程加⼊到队列中。

AQS 的源码中 方法很多,但主要做了三件事情:

1、管理 同步状态;

2、维护 同步队列;

3、阻塞和唤醒 线程。

另外,从行为上来区分就是 获取锁 和 释放锁,从模式上来区分就是 独占锁 和 共享锁。

二、AQS的实现原理

1、同步状态(State)

volatile int state; 用于存储同步状态,通过getState()setState(int newState)compareAndSetState(int expect, int update)方法进行访问和原子操作

    /**
     * 同步状态
     */
    private volatile int state;

    /**
     * 返回同步状态的当前值。此操作具有读取的 volatile 内存语义
     * 返回值: 当前状态值
     */
    protected final int getState() {
        return state;
    }

    /**
     * 设置同步状态的值。此操作具有写入的 volatile 内存语义
     * 形参: newState – 新的状态值
     */
    protected final void setState(int newState) {
        state = newState;
    }

    /**
     * 如果当前状态值等于预期值,则以原子方式将同步状态设置为给定的更新值。此操作具有读取和写入的volatile 内存语义。
     * 形参: expect – 期望值 update – 新价值
     * 返回值: true 如果成功。错误返回表示实际值不等于预期值。     
     */
    protected final boolean compareAndSetState(int expect, int update) {
        return U.compareAndSetInt(this, STATE, expect, update);
    }

2、线程排队与阻塞

队列采用CLH变体的FIFO双向链表实现,每个节点仅需持有前驱节点的引用,由尾部节点维护整个队列。

使用内部类Node表示等待队列中的节点,包含线程引用、等待状态等信息。

3、同步模式

独占模式(Exclusive):只有一个线程能持有同步状态,如ReentrantLock

共享模式(Shared):多个线程可以同时持有同步状态,如Semaphore

4、阻塞与唤醒机制

使用java.util.concurrent.locks.LockSupport.park()unpark(Thread thread)方法实现线程阻塞与唤醒。

5、公平与非公平策略

子类通过不同的tryAcquire实现逻辑控制是否遵循FIFO原则。

6、关键方法(acquire)

(1)tryAcquire(int arg) 和 tryRelease(int arg):子类实现,分别用于独占模式下尝试获取和释放同步状态。

 Acquire:
      while (!tryAcquire(arg)) {
         enqueue thread if it is not already queued;
         possibly block current thread;
      }

 Release:
      if (tryRelease(arg))
         unblock the first queued thread;


    /**
     尝试以独占模式获取。此方法应查询对象的状态是否允许在独占模式下获取它,如果允许,则查询获取它。
     此方法始终由执行 acquire 的线程调用。如果此方法报告失败,则 acquire 方法可能会对线程进行排队(如果尚未排队),直到其他线程发出释放信号。
     这可以用于实现方法 Lock.tryLock()。默认实现抛出 UnsupportedOperationException.
     形参: arg – 获取参数。此值始终是传递给 acquire 方法的值,或者是在进入条件等待时保存的值。该值在其他方面是未解释的,可以表示您喜欢的任何内容。
     返回值: true 如果成功。成功后,该对象已被收购。
     抛出: IllegalMonitorStateException – 如果获取会使此同步器处于非法状态。必须以一致的方式引发此异常,同步才能正常工作。UnsupportedOperationException – 如果不支持独占模式
     */
    protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }


    /**
     尝试设置状态以反映独占模式下的发布。
     此方法始终由执行释放的线程调用。
     默认实现抛出 UnsupportedOperationException.
     形参: arg – 释放参数。此值始终是传递给发布方法的值,或者是进入条件等待时的当前状态值。该值在其他方面是未解释的,可以表示您喜欢的任何内容。
     返回值: true 如果此对象现在处于完全释放状态,则任何等待的线程都可以尝试获取;否则 false 。
     抛出: IllegalMonitorStateException – 如果释放会使此同步器处于非法状态。必须以一致的方式引发此异常,同步才能正常工作。UnsupportedOperationException – 如果不支持独占模式
     */
    protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
    }

(2)tryAcquireShared(int arg) 和 tryReleaseShared(int arg):子类实现,分别用于共享模式下尝试获取和释放同步状态。

    /**
     尝试在共享模式下获取。此方法应查询对象的状态是否允许在共享模式下获取它,如果允许,则查询获取它。
     此方法始终由执行 acquire 的线程调用。如果此方法报告失败,则 acquire 方法可能会对线程进行排队(如果尚未排队),直到其他线程发出释放信号。
     默认实现抛出 UnsupportedOperationException.
     形参: arg – 获取参数。此值始终是传递给 acquire 方法的值,或者是在进入条件等待时保存的值。该值在其他方面是未解释的,可以表示您喜欢的任何内容。
     返回值: 失败时为负值;如果共享模式下的采集成功,但后续的共享模式采集无法成功,则为零;如果共享模式下的采集成功,并且后续共享模式采集也可能成功,则为正值,在这种情况下,后续等待线程必须检查可用性。(支持三种不同的返回值,使此方法能够在获取有时仅以独占方式执行操作的上下文中使用。成功后,该对象已被收购。
     抛出: IllegalMonitorStateException – 如果获取会使此同步器处于非法状态。必须以一致的方式引发此异常,同步才能正常工作。UnsupportedOperationException – 如果不支持共享模式
     */
    protected int tryAcquireShared(int arg) {
        throw new UnsupportedOperationException();
    }



    /**
     尝试设置状态以反映共享模式下的发布。
     此方法始终由执行释放的线程调用。
     默认实现抛出 UnsupportedOperationException.
     形参: arg – 释放参数。此值始终是传递给发布方法的值,或者是进入条件等待时的当前状态值。该值在其他方面是未解释的,可以表示您喜欢的任何内容。
     返回值: true 如果此共享模式版本可能允许等待获取(共享或独占)成功;以及 false 其他
     抛出: IllegalMonitorStateException – 如果释放会使此同步器处于非法状态。必须以一致的方式引发此异常,同步才能正常工作。UnsupportedOperationException – 如果不支持共享模式
     */
    protected boolean tryReleaseShared(int arg) {
        throw new UnsupportedOperationException();
    }

(3)acquire(int arg)acquireInterruptibly(int arg)tryAcquireNanos(int arg, long nanosTimeout):独占模式下阻塞获取同步状态,支持中断和超时。

release(int arg):独占模式下释放同步状态。

/**
主 acquire 方法,由所有导出的 acquire 方法调用。
形参:
node – 除非重新获取条件,否则为 null arg – 获取参数 shared – 如果共享模式为真,否则为独占 interruptible – 如果中止并在中断时返回负数 timed – 如果为 true,则使用定时等待 time – 如果定时,则超时的 System.nanoTime 值
返回值:
如果获取,则为正,如果超时,则为 0,如果中断,则为负
*/
final int acquire(Node node, int arg, boolean shared,
          boolean interruptible, boolean timed, long time) {
Thread current = Thread.currentThread();
byte spins = 0, postSpins = 0;   // retries upon unpark of first thread
boolean interrupted = false, first = false;
Node pred = null;                // predecessor of node when enqueued

/*
* Repeatedly:
*  Check if node now first
*    if so, ensure head stable, else ensure valid predecessor
*  if node is first or not yet enqueued, try acquiring
*  else if node not yet created, create it
*  else if not yet enqueued, try once to enqueue
*  else if woken from park, retry (up to postSpins times)
*  else if WAITING status not set, set and retry
*  else park and clear WAITING status, and check cancellation
*/

for (;;) {
if (!first && (pred = (node == null) ? null : node.prev) != null &&
    !(first = (head == pred))) {
    if (pred.status < 0) {
        cleanQueue();           // predecessor cancelled
        continue;
    } else if (pred.prev == null) {
        Thread.onSpinWait();    // ensure serialization
        continue;
    }
}
if (first || pred == null) {
    boolean acquired;
    try {
        if (shared)
            acquired = (tryAcquireShared(arg) >= 0);
        else
            acquired = tryAcquire(arg);
    } catch (Throwable ex) {
        cancelAcquire(node, interrupted, false);
        throw ex;
    }
    if (acquired) {
        if (first) {
            node.prev = null;
            head = node;
            pred.next = null;
            node.waiter = null;
            if (shared)
                signalNextIfShared(node);
            if (interrupted)
                current.interrupt();
        }
        return 1;
    }
}
if (node == null) {                 // allocate; retry before enqueue
    if (shared)
        node = new SharedNode();
    else
        node = new ExclusiveNode();
} else if (pred == null) {          // try to enqueue
    node.waiter = current;
    Node t = tail;
    node.setPrevRelaxed(t);         // avoid unnecessary fence
    if (t == null)
        tryInitializeHead();
    else if (!casTail(t, node))
        node.setPrevRelaxed(null);  // back out
    else
        t.next = node;
} else if (first && spins != 0) {
    --spins;                        // reduce unfairness on rewaits
    Thread.onSpinWait();
} else if (node.status == 0) {
    node.status = WAITING;          // enable signal and recheck
} else {
    long nanos;
    spins = postSpins = (byte)((postSpins << 1) | 1);
    if (!timed)
        LockSupport.park(this);
    else if ((nanos = time - System.nanoTime()) > 0L)
        LockSupport.parkNanos(this, nanos);
    else
        break;
    node.clearStatus();
    if ((interrupted |= Thread.interrupted()) && interruptible)
        break;
}
}
return cancelAcquire(node, interrupted, interruptible);
}
    /**
     以独占模式获取,中断时中止。通过首先检查中断状态,然后至少调用一次 tryAcquire,成功返回来实现。否则,线程将排队,可能会反复阻塞和取消阻塞,直到 tryAcquire 成功或线程中断。此方法可用于实现方法 Lock.lockInterruptibly。
    形参: arg – 获取参数。此值被传达给 tryAcquire ,但在其他方面不会被解释,并且可以表示您喜欢的任何内容。
    抛出: InterruptedException – 如果当前线程中断     
    */
    public final void acquireInterruptibly(int arg)
        throws InterruptedException {
        if (Thread.interrupted() ||
            (!tryAcquire(arg) && acquire(null, arg, false, true, false, 0L) < 0))
            throw new InterruptedException();
    }

    /**
     尝试在独占模式下获取,如果中断则中止,如果给定的超时时间过长,则失败。通过首先检查中断状态,然后至少调用一次 tryAcquire,成功返回来实现。否则,线程将排队,可能会重复阻塞和取消阻塞,直到tryAcquire 成功或线程中断或超时结束。此方法可用于实现方法 Lock.tryLock(long, TimeUnit)。
     形参: arg – 获取参数。此值被传达给 tryAcquire ,但在其他方面不会被解释,并且可以表示您喜欢的任何内容。 nanosTimeout – 等待的最大纳秒数
     返回值: true 如果获得; false 如果超时
     抛出: InterruptedException –如果当前线程中断      
     */
    public final boolean tryAcquireNanos(int arg, long nanosTimeout)
        throws InterruptedException {
        if (!Thread.interrupted()) {
            if (tryAcquire(arg))
                return true;
            if (nanosTimeout <= 0L)
                return false;
            int stat = acquire(null, arg, false, true, true,
                               System.nanoTime() + nanosTimeout);
            if (stat > 0)
                return true;
            if (stat == 0)
                return false;
        }
        throw new InterruptedException();
    }


    /**
     以独占模式发布。通过在返回 true 时 tryRelease 取消阻止一个或多个线程来实现。此方法可用于实现方法 Lock.unlock。
     形参: arg – 释放参数。此值被传达给 tryRelease ,但在其他方面不会被解释,并且可以表示您喜欢的任何内容。
     返回值: 返回的值 tryRelease     
    */
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            signalNext(head);
            return true;
        }
        return false;
    }

(4)acquireShared(int arg)acquireSharedInterruptibly(int arg)tryAcquireSharedNanos(int arg, long nanosTimeout):共享模式下阻塞获取同步状态,支持中断和超时。

releaseShared(int arg):共享模式下释放同步状态。

    /**
     在共享模式下获取,忽略中断。通过至少一次首次调用 tryAcquireShared来实现,成功返回。否则,线程将排队,可能会重复阻塞和取消阻塞,直到 tryAcquireShared 调用成功。
     形参: arg – 获取参数。此值被传达给 tryAcquireShared ,但在其他方面不会被解释,并且可以表示您喜欢的任何内容     
    */
    public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            acquire(null, arg, true, false, false, 0L);
    }

    /**
     在共享模式下获取,如果中断则中止。通过首先检查中断状态,然后至少调用一次 tryAcquireShared,成功返回来实现。否则,线程将排队,可能会反复阻塞和取消阻塞,直到 tryAcquireShared 成功或线程中断。
     形参: arg – 获取参数。此值被传达给 tryAcquireShared ,但在其他方面不会被解释,并且可以表示您喜欢的任何内容。
     抛出: InterruptedException – 如果当前线程中断     
    */
    public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
        if (Thread.interrupted() ||
            (tryAcquireShared(arg) < 0 &&
             acquire(null, arg, true, true, false, 0L) < 0))
            throw new InterruptedException();
    }

    /**
     尝试在共享模式下获取,如果中断则中止,如果给定超时过,则失败。通过首先检查中断状态,然后至少调用一次 tryAcquireShared,成功返回来实现。否则,线程将排队,可能会重复阻塞和取消阻塞,直到 tryAcquireShared 成功或线程中断或超时结束。
     形参: arg – 获取参数。此值被传达给 tryAcquireShared ,但在其他方面不会被解释,并且可以表示您喜欢的任何内容。 nanosTimeout – 等待的最大纳秒数
    返回值: true 如果获得; false 如果超时
    抛出: InterruptedException – 如果当前线程中断     
    */
    public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        if (!Thread.interrupted()) {
            if (tryAcquireShared(arg) >= 0)
                return true;
            if (nanosTimeout <= 0L)
                return false;
            int stat = acquire(null, arg, true, true, true,
                               System.nanoTime() + nanosTimeout);
            if (stat > 0)
                return true;
            if (stat == 0)
                return false;
        }
        throw new InterruptedException();
    }

    /**
     在共享模式下发布。通过在返回 true 时 tryReleaseShared 取消阻止一个或多个线程来实现。
     形参: arg – 释放参数。此值被传达给 tryReleaseShared ,但在其他方面不会被解释,并且可以表示您喜欢的任何内容。
     返回值: 返回的值 tryReleaseShared     
    */
    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            signalNext(head);
            return true;
        }
        return false;
    }

三、CountDownLatch、CompletableFuture、CyclicBarrier、Semaphore

CountDownLatch:

通过计数法(倒计时器),让一些线程堵塞直到另一个线程完成一系列操作后才被唤醒;该⼯具通常⽤来控制线程等待,它可以让某⼀个线程等待直到倒计时结束,再开始执⾏。具体可以使用countDownLatch.await()来等待结果。多用于多线程信息汇总。

CompletableFuture:

通过设置参数,可以完成CountDownLatch同样的多平台响应问题,但是可以针对其中部分返回结果做更加灵活的展示。

CyclicBarrier:

字面意思是可循环(Cyclic)使用的屏障(Barrier)。他要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活,线程进入屏障通过CyclicBarrier的await()方法。可以用于批量发送消息队列信息、异步限流。

Semaphore:

信号量主要用于两个目的,一个是用于多个共享资源的互斥作用,另一个用于并发线程数的控制。SpringHystrix限流的思想 

具体使用案例:保证多个线程的执行顺序,参考我的另一篇文章:北京--面试1(设计模式、反射、队列、线程、锁、Linux命令、JVM调优参数)-CSDN博客

四、附:AQS序图

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

计算机是如何工作的6

因此&#xff0c;往往就把“并行”和“并发”统称为“并发” 对应的编程方式&#xff08;解决一个问题&#xff0c;同时搞多个任务来执行&#xff0c;共同协作解决&#xff09;就称为“并发” 此处cpu的百分数&#xff0c;就是你的进程在cpu舞台上消耗时间的百分比 如果有一…

字符串2s总结

4.字符串 字符串理论基础 什么是字符串 字符串是若⼲字符组成的有限序列&#xff0c;也可以理解为是⼀个字符数组&#xff0c;但是很多语⾔对字符串做了特殊的规定&#xff0c;接下来我来说⼀说C/C中的字符串。 在C语⾔中&#xff0c;把⼀个字符串存⼊⼀个数组时&#xff0c…

Android Activity 介绍

Activity Activity 是一个应用组件&#xff0c;用户可与其提供的屏幕进行交互&#xff0c;以执行拨打电话、拍摄照片、发送电子邮件或查看地图等操作。 每个 Activity 都会获得一个用于绘制其用户界面的窗口。窗口通常会充满屏幕&#xff0c;但也可小于屏幕并浮动在其他窗口之…

Cherno CPP学习笔记-01-背景知识

0、工具网站收集 C语言版本特性 https://en.cppreference.com https://www.cplusplus.com https://www.tutorialspoint.com/cplusplus https://www.learncpp.com https://github.com/fffaraz/awesomecpp https://stackoverflow.com 网页CPP编译器 [C] gcc 12.1.0 - Wa…

expected scalar type long but found float

在报这个错误的情况下&#xff0c;找到报错的路径&#xff0c;将target 改为target.long()就可以解决了

Chatgpt掘金之旅—有爱AI商业实战篇|虚拟助理|(九)

演示站点&#xff1a; https://ai.uaai.cn 对话模块 官方论坛&#xff1a; www.jingyuai.com 京娱AI 一、AI技术创业在虚拟助理业务有哪些机会&#xff1f; 人工智能&#xff08;AI&#xff09;技术作为当今科技创新的前沿领域&#xff0c;为创业者提供了广阔的机会和挑战。随…

【Leetcode每日一题】 递归 - 二叉树剪枝(难度⭐⭐)(50)

1. 题目解析 题目链接&#xff1a;814. 二叉树剪枝 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 2.算法原理 想象一下&#xff0c;你有一堆层层叠叠的积木&#xff0c;你想从底部开始&#xff0c;把那些标记为0的积木拿走。如…

设计模式之观察者模式讲解

概念&#xff1a;定义对象间一种一对多的依赖关系&#xff0c;使得当每一个对象改变状态&#xff0c;则所有依赖于它的对象都会得到通知并被自动更新。 抽象主题&#xff1a;或者叫被观察者&#xff0c;可以持有、增加、删除观察者对象。具体主题&#xff1a;实现抽象主题定义的…

定时任务原理方案综述

定时任务原理方案综述 背景概述 定时任务&#xff0c;顾名思义&#xff0c;就是指定时间点进行执行相应的任务。业务场景中包括&#xff1a; 每天晚上12点&#xff0c;将当日的销售数据发送给各个VP&#xff1b;订单下单十分钟未付款将自动取消订单&#xff1b;用户下单后发…

【JavaScript】预解析 ② ( 预解析示例分析 | 分步骤分析预解析过程 )

文章目录 一、预解析示例分析一1、要分析的代码2、代码预解析分析3、作用域链分析 二、预解析示例分析二1、要分析的代码2、代码预解析分析 三、预解析示例分析三1、要分析的代码2、预解析过程分析 一、预解析示例分析一 1、要分析的代码 要分析的 代码示例 : <!DOCTYPE ht…

人工智能前沿成科技竞争新高地

以下文章来源&#xff1a;经济参考报 近日&#xff0c;首届中国具身智能大会&#xff08;CEAI 2024&#xff09;在上海举行。作为人工智能领域的前沿热点&#xff0c;具身智能正逐步走进现实&#xff0c;成为当前全球科技竞争的新高地、未来产业的新赛道、经济发展的新引擎。 “…

人工智能_大模型018_AssistantsAPI_01_---人工智能工作笔记0154

先来说一下一些问题: 尽量不要微调,很麻烦,而且效果需要自己不断的去测试. 如果文档中有图表,大量的图片去分析就不合适了. 是否用RAG搜索,这个可以这样来弄,首先去es库去搜能直接找到答案可以就不用去RAG检索了,也可以设置一个分,如果低于60分,那么就可以去进行RAG检索 微…

视频实例分割 | 基于ViT实现的端到端end-to-end+query-based的视频实例分割

项目应用场景 面向视频实例分割场景&#xff0c;项目采用 Vision-Transformer 深度学习算法来实现。 项目效果 项目细节 > 具体参见项目 README.md (1) 创建 python 开发环境 conda create --name tevit python3.7.7 conda activate tevit (2) 安装依赖 torch1.9.0 torch…

XC7A35T-2FGG484 嵌入式FPGA现场可编程门阵列 Xilinx

XC7A35T-2FGG484 是一款由Xilinx&#xff08;赛灵思&#xff09;制造的FPGA&#xff08;现场可编程门阵列&#xff09;芯片 以下是XC7A35T-2FGG484 的主要参数&#xff1a; 1. 系列&#xff1a;Artix-7 2. 逻辑单元数量&#xff1a;33280个 3. 工艺技术&#xff1a;28nm 4. …

postgresql发布和订阅

一、发布订阅介绍 发布和订阅使用了pg的逻辑复制的功能&#xff0c;通过发布端创建publication与表绑定&#xff0c;订阅端创建subscription同时会在发布端创建逻辑复制槽实现逻辑复制功能 逻辑复制基于 发布&#xff08;Publication&#xff09; 与 订阅&#xff08;Subscri…

Few-Shot目标检测数据集 | Few-Shot目标检测数据集_已经整理成MS-COCO数据格式_含60000+张图_可直接用于目标检测算法训练

项目应用场景 面向 Few-Shot 目标检测场景&#xff0c;项目提供 6000 张图&#xff0c;已经整理成 MS-COCO 数据格式&#xff0c;可用于 Few-Shot 目标检测的训练数据集&#xff0c;或作为 Few-Shot 目标检测数据集的补充。 数据集展示 数据集下载 > 具体参见项目 README.m…

FreeBuf 全球网络安全产业投融资观察(3月)

综述 据不完全统计&#xff0c;2024年3月&#xff0c;全球网络安全市场共发生投融资事件53起&#xff0c;其中国内4起&#xff0c;国外49起。 3月全球络安全产业投融资统计表&#xff08;数据来源&#xff1a;航行资本、36氪&#xff09; 整体而言&#xff0c;国内4起投融资事…

数字图像处理基础

目录 概述 仿射变换 常见的灰度处理算法 空间域滤波原理 空间域平滑滤波&#xff08;低通滤波&#xff09; 空间域锐化滤波&#xff08;高通滤波&#xff09; 傅里叶变换 频率域与空间域的对应关系 频率域滤波 形态学处理基础知识 边缘检测原理 检测孤立点 检测线…

软考之零碎片段记录(九)+复习巩固(四)

一、学习 1. 英语单词 delivery:交付 automation:自动化 build-in:内置 Iwell-konwn:众所周知 modern:现代 hands-off:无干预 labor-free:免人工 visual:可视化 object-oriented:面向对象的 structural:结构化的 2. 案例 E1: 租户信息 E2: 农户 E3: 租户 E4: 用户 3. 案例…

giteegit的连结使用

目标&#xff1a;在windows的本地的git上操作的项目存放到Gitee云端上 不适用于linux的terminal终端下 1.先下载好Git这个软件 2.创建一个文件夹&#xff08;项目名称&#xff09; 然后用gitbash的形式打开 3.创建ssh密钥到Gitee上 因为我们在Git与Gitee上的传输是通过ssh…