【JUC并发编程1】GitHub使用教程:从GitHub上拉取文件到本地,从本地上传文件到GitHub,使用IDEA拉取和推送,管理项目版本

该博客记录了项目中使用GitHub和Gitee的常见操作,包括拉取文件、上传文件、更新仓库、创建秘钥等,还介绍了使用IDEA进行相关操作的方法。此外,博客还涉及线程池的定时执行、异常处理,以及多种锁机制如ReentrantLock、ReentrantReadWriteLock等的原理和应用。

博客背景:最近在做项目,经常会使用到GitHub,常用的需求包含:拉取远端GitHub文件保存到本地,从本地上传文件到GitHub仓库等等...命令经常会忘,因此整理一篇博客,

一、从GitHub上拉取文件到本地

使用命令:git clone

git clone 仓库地址

操作步骤:

第1步:进入到本地机中想要存储文件的目录中,右键,点击“Git Bash Here”

第2步:在GitHub上找到想要拉取的项目,点击Code,复制仓库地址,在git bash中输入"git clone 仓库地址"

 

如果出现下面错误:

可以输入下面代码,关闭证书校验:

git config --global http.sslVerify false

第3步:成功将资源从GitHub下载到本地文件夹,检查项目的完整性

二、从本地上传文件到GitHub

使用命令:git -c http.sslVerify=false push -u 仓库地址 分支名称

操作步骤:

 1.GitHub上创建新Repository

 在GitHub上创建名为springcloud-config的新Repository:

复制HTTPS地址,比如:git@github.com:pbjlovezjy/springcloud-config.git

2.推送本地仓库的项目到GitHub

在spring-config文件夹下的空白处单击右键,进入Git Bash

然后输入:git -c http.sslVerify=false push -u 仓库地址 分支名称

然后会弹出一个界面,选择 sign in with your browser,选择授权git系统,然后输入密码即可:

 

可以直接将文件夹内容提交至仓库:

三、更新GitHub仓库

假如我们现在删掉了本地文件夹里的README.md,我们想把这一修改同步到GitHub上,要如何做呢?

首先我们输入:git add .  (注意最后有个点)将所有修改的内容放入缓冲区:

然后输入:git commit -m '随便填写'

即可实现GitHub同步本地文件夹的内容。

假设我们现在修改了GitHub上的内容,添加了一个README.mdd文件夹:

 

此时,我们本地没有README.md文件,假如我们想同步远程仓库和本地:

只需要输入:git -c http.sslVerify=false pull --rebase origin master  

文件就被我们同步到了本地:

四、创建秘钥并设置

 第3步: 如果报错,需要创建并设置秘钥,见第四节

解释:如果想从GitHub的仓库中拉取文件到本地,需要上报自己的用户名、邮箱、密码等信息,这些信息会被存储为秘钥,作为访问GitHub的权限。

假如GitHub的用户名为:aabbccdd

密码:11223344

邮箱:12345@qq.com

第1步:打开Git bash设置用户名和邮箱

git config --global user.name "填你GitHub的用户名"

git config --global user.email "填你自己GitHub注册的邮箱"

示例如下:
git config --global user.name "aabbccdd"
git config --global user.email "12345@qq.com"

第2步:在Git Bash中输入以下代码,创建密钥:

1. 生成新的SSH密钥对(这一步是生成私钥):ssh-keygen -t rsa -b 4096 -C "你的邮箱"

示例:sh-keygen -t rsa -b 4096 -C "12345@qq.com"

2. 启动 SSH agent:eval "$(ssh-agent -s)"

3.添加你的私钥到SSH agent中:ssh-add ~/.ssh/id_rsa

4.复制公钥到剪贴板:clip < ~/.ssh/id_rsa.pub

公钥如下:

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCzEFpg4hNhzVRdmq/9At/hEealmjiprYUWFfkvuGSdU9PFQokWTLwOeR6z8yVjn0VrDFIGIyQ9q805Clcev1VqGN9CAy50adBTamW3mV1U8twTWS7VNc89WidA5OK5HyfL4xkuSg/NaOtOjj0m7+31+w3JANgavFjQaUi03peuGaCnp3xVsFWFO1r1msNTw0QwRnAPERwA3/w1jT7gNcJ/wS3yHJiNbKpwbJcLKAUJUFsijJ80C25b4/0jSjKyBl+W9ctDMW/2gTUMoMCFXiXgVHhdLJcbyHaqwC3xXP0hi8K20Yaqr4f8vxxEw3Wnfn/5Dzj4fZ24saMwff4RV8HwJ8BrQx+4la6RQhLYrWWCay6AA3+2jawhW/eW6FFnnywr4GnRnVXcc0F3iLlnEfPW5qOdLrG903vjL9xYe3bty8CTWRsF1ruTI2PjD06Om0hPK9mvTCPnoDzBktlsVX907qNaCVqjbwpfgtHae0fEDIWZXCkBaqaYvOm9wL5cPD0F6R9CqDs6BtrI4HSrh9tUvBKO9xHnFLAF2BLHuYcrzU62M1KGzLln4kPC9hlh8pMoxqQwkfccz3Z0F84b7q5ppg3PutoLfb+gLyCTfOTGKjuw3DWFaYPalGCYdQAPchsp8DylAnv4pecxGH79XnbZ46ExjkQ3znnotkPDqN/TIxfGaB7hQ== 3499045941@qq.com

第3步:打开GitHub粘贴密钥,完成关联:

点击右上角的头像,选择 "Settings":

在左侧菜单中选择 "SSH and GPG keys":

 

点击 "New SSH key":

在 "Title" 字段中,为你的密钥添加一个描述性的名称:

在 "Key" 字段中,粘贴你之前复制的公钥:

点击 "Add SSH key":

简单解释原理:私钥是存储在你自己本地电脑上的,公钥是放在GitHub上的,通过私钥来匹配公钥,使得本地机具有访问GitHub的权限。

五、使用IDEA拉取和推送

一、从Gitee上拉取文件到本地

二、从本地上传文件到Gitee

1.Gitee上创建新仓库

 

2.推送本地仓库的项目到Gitee

比如我现在想把JUC高并发这个文件夹下的JUCdemo项目上传到Gitee上。

1. 首先要进行Git的全局设置:

git config --global user.name "这里填你的用户名"
git config --global user.email "这里填你的邮箱"

2. 然后要创建git仓库:

选取一个本地git仓库所在的位置,可以是任意的文件夹。 

mkdir 仓库名称

 输入上面命令后会在当前文件夹下新建一个本地仓库,如下图:

3.仓库初始化

这一步包含下面3步:

进入仓库:

cd 仓库名

初始化仓库:

git init

 创建README.md文件(可选)

touch README.md

4.上传文件到本地仓库

比如:我想要上传的是JUCdemo文件,我需要先把JUCdemo文件给复制到本地仓库里,如下图:

然后输入下面命令,这个命令是将当前文件夹下的内容全部存入到缓冲区:

git add .

最后输入下面这个命令,这个命令可以把缓冲区中的数据提交到本地仓库:

git commit -m "提交名称"

注意,此时只是将数据提交到仓库的历史记录中,可以通过上面设定的“提交名称”来找到每一次提交的记录。

5.设置远程仓库

设置远程仓库的地址,输入如下命令:

git remote add origin 远程仓库地址

这里的origin指向的是Gitee仓库的原始位置(URL),相当于远程仓库的坐标,通过这个坐标可以定位到远程仓库。 

 

6.提交本地仓库数据到远程仓库

输入下面的命令,将本地仓库的内容提交到远程仓库:

git push origin master

分支在Git中用于隔离工作环境,不同的分支之间不会相互影响,能够独立工作。

上面这段代码的意思大概是:推送本地仓库的数据(git push -u)到远程仓库(名字为origin)的某个分支(默认分支名称)。

总结一下:想把本地的项目推送到Gitee上,流程:首先要把项目保存到本地仓库,然后再从本地仓库推送到远程仓库。 

三、更新Gitee仓库

创建分支

git branch "分支名称"

推送本地仓库数据到分支:

git push -u origin "分支名称"

四、使用IDEA拉取和推送

P226 ScheduledThreadPoolExecutor 定时执行

ScheduledThredPoolExecutor可以定时执行任务。

假如设置每隔1秒执行一次,因为休眠时间是2秒,所以会2秒执行一次。

@Slf4j(topic = "c.Test1")
public class Test1 {
    public static void main(String[] args) {
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
        log.debug("start...");
        pool.scheduleAtFixedRate(()->{
            log.debug("running...");
            sleep(2);
        },2,1,TimeUnit.SECONDS);
    }
}

每次任务开始的时间是从上一次任务结束时间点开始,睡眠时间+延迟时间:

@Slf4j(topic = "c.Test1")
public class Test1 {
    public static void main(String[] args) {
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
        log.debug("start...");
        pool.scheduleWithFixedDelay(()->{
            log.debug("running...");
            sleep(2);
        },1,1,TimeUnit.SECONDS);
    }
}

P227 正确处理线程池异常

方法1:用try-catch块捕获。

方法2:用submit方法然后返回为一个Future对象

@Slf4j(topic = "c.Test1")
public class Test1 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
        Future<Boolean> f = pool.submit(() -> {
            log.debug("task1");
            int i = 1 / 0;
            return true;
        });
        log.debug("result:{}", f.get());
    }
}

如果没有异常返回true,有异常返回错误。 

P228 线程池应用 定时任务

使用now来计算当前时间,使用time来计算目标时间,使用initialDelay来计算距离第1次执行的时间,period用来设置每次执行的时间间隔。

public class Test3 {
    public static void main(String[] args) {
        //获取当前时间
        LocalDateTime now = LocalDateTime.now();
        System.out.println(now);
        //获取周四时间
        LocalDateTime time = now.withHour(12).withMinute(3).withNano(0).with(DayOfWeek.SUNDAY);
        //如果当前时间>本周周四,必须找到下周周四
        if(now.compareTo(time)>0){
            time = time.plusWeeks(1);
        }
        System.out.println(time);
        long initialDelay = Duration.between(now,time).toMillis();
        long period = 1000;
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
        pool.scheduleAtFixedRate(()->{
            System.out.println("running...");
        },initialDelay,period, TimeUnit.MILLISECONDS);
    }
}

P229 线程池应用 定时任务 测试

下图是效果:

P230 tomcat 线程池

LiMitLatch:用来限流,控制最大连接数,避免服务器被压垮。

Acceptor:负责接收新的socket连接

Poller:监听Socket channel连接上是否有可读的事件发生。一旦可读,封装一个任务对象socketProcessor提交给Executor线程池处理。

Executor线程池中的工作线程(worker)最终负责处理请求。

Tomcat线程池扩展了ThreadPoolExecutor,行为稍有不同:

P231 tomcat 线程池 配置

P232 forkjoin 概念

P233 forkjoin 使用

@Slf4j(topic = "c.Test1")
public class Test1 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ForkJoinPool pool = new ForkJoinPool(4);
        System.out.println(pool.invoke(new MyTask(5)));
    }
}
@Slf4j(topic = "c.MyTask")
//1~n之间整数的和
class MyTask extends RecursiveTask<Integer>{
    private int n;
    public MyTask(int n) {
        this.n = n;
    }
    @Override
    public String toString(){
        return "(" + n + ")";
    }
    @Override
    protected Integer compute(){
        //终止条件
        if(n==1) {
            log.debug("join(){}",n);
            return 1;
        }
        MyTask t1 = new MyTask(n - 1);
        t1.fork();//让一个线程去执行此任务
        log.debug("fork(){}+{}",n,t1);
        int result = n + t1.join();
        log.debug("join(){}+{}",n,result);
        return result;
    }
}

中间会用到多线程来执行任务的拆分和合并:

P234 forkjoin 任务拆分优化

P235 aqs 概述

aqs的全称是:AbstractQueuedSynchronizer,是阻塞式锁和相关的同步器工具的框架。

其它同步器工具都是作为aqs的实现子类。

提供阻塞式锁,更类似于悲观锁。

特点:

1.用state属性来表示资源的状态,分为独占模式和共享模式,独占式式只有一个线程能够访问资源,共享式式允许多个线程访问资源。

2.提供了基于FIFO的等待队列,类似于Monitor的EntryList。

3.条件变量来实现等待、唤醒机制,支持多个条件变量,类似于Monitor的WaitSet。

子类主要实现了下面这些方法:

tryAcquire

tryRelease

tryAcquireShared

tryReleaseShared

isHeldExclusively

P236 aqs 自定义锁

不可重入锁,本线程加了锁,下次还是同一个线程来了不能重复加。

//自定义锁(不可重入锁)
class MyLock implements Lock{
    //独占锁,同步器类
    class Mysync extends AbstractQueuedSynchronizer{
        @Override
        protected boolean tryAcquire(int arg) {
            if(compareAndSetState(0,1)){//其它线程可能也想修改状态
                //加上了锁,并设置owner为当前线程
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        @Override
        protected boolean tryRelease(int arg) {
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }

        @Override
        protected boolean isHeldExclusively() {
            return getState()==1;
        }

        public Condition newCondition(){
            return new ConditionObject();
        }
    }
    private Mysync sync = new Mysync();

    public MyLock() {
        super();
    }

    @Override //加锁,加锁不成功会放入队列等待
    public void lock() {
        sync.acquire(1);
    }

    @Override //加锁,可打断
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    @Override //尝试加锁,只会尝试一次
    public boolean tryLock() {
        return sync.tryAcquire(1);
    }

    @Override //尝试加锁,带超时时间
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireNanos(1,unit.toNanos(time));
    }

    @Override //解锁
    public void unlock() {
        sync.release(1);
    }

    @Override //创建条件变量
    public Condition newCondition() {
        return sync.newCondition();
    }
}

P237 aqs 自定义锁 测试

//自定义锁(不可重入锁)
class MyLock implements Lock{
    //独占锁,同步器类
    class Mysync extends AbstractQueuedSynchronizer{
        @Override
        protected boolean tryAcquire(int arg) {
            if(compareAndSetState(0,1)){//其它线程可能也想修改状态
                //加上了锁,并设置owner为当前线程
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        @Override
        protected boolean tryRelease(int arg) {
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }

        @Override
        protected boolean isHeldExclusively() {
            return getState()==1;
        }

        public Condition newCondition(){
            return new ConditionObject();
        }
    }
    private Mysync sync = new Mysync();

    public MyLock() {
        super();
    }

    @Override //加锁,加锁不成功会放入队列等待
    public void lock() {
        sync.acquire(1);
    }

    @Override //加锁,可打断
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    @Override //尝试加锁,只会尝试一次
    public boolean tryLock() {
        return sync.tryAcquire(1);
    }

    @Override //尝试加锁,带超时时间
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireNanos(1,unit.toNanos(time));
    }

    @Override //解锁
    public void unlock() {
        sync.release(1);
    }

    @Override //创建条件变量
    public Condition newCondition() {
        return sync.newCondition();
    }
}

P238 reentrantlock 加锁成功流程

P239 reentrantlock 枷锁失败流程

P240 reentrantlock 解锁竞争成功流程

下面是释放锁release的流程:

P241 reentrantlock 解锁竞争失败流程

P242 reentrantlock 锁重入原理

P243 reentrantlock 可打断原理

不可打断模式:即使被打断,仍会驻留在AQS队列中,等待获得锁后方能继续运行(是继续运行,只是打断标记被设为true)。

可打断模式:被打断之后直接抛出异常,不会再进入for循环。

P244 reentrantlock 公平锁原理

下图是非公平锁实现,任意节点都能竞争:

下图是公平锁实现,只有头节点的后一个节点能竞争:

P245 reentrantlock 条件变量 await

调用await,会进入addConditionWaiter流程,创建新的Node状态为-2

fullyRelease会把所有的锁都释放掉:

P246 reentrantlock 条件变量 signal

doSignal中transferForSignal返回true则转移成功,不会继续循环。如果转移失败的原因可能是线程放弃了对锁的竞争,因为超时等原因,就不会被加入队列。

waitStatus改为-1,表示它有责任去唤醒链表的下一个元素。

P247  reentrantreadwritelock 使用

ReentrantReadWriteLock支持重入的读写锁

当读操作远远高于写操作时,这时候要使用读写锁让读-读可以并发,提高性能。

但是写-写和读-写是互斥的。

加上读锁读取,隔了100毫秒后开始获取写锁,但是读休眠1秒,一秒后读锁释放,才能获取到写锁开始写入:

@Slf4j(topic = "c.Test1")
public class Test1 {
    public static void main(String[] args) throws InterruptedException {
        DataContainer dataContainer = new DataContainer();
        new Thread(()->{
            dataContainer.read();
        },"t1").start();
        Thread.sleep(100);
        new Thread(()->{
            dataContainer.write();
        },"t1").start();
    }
}
@Slf4j(topic = "c.DataContainer")
class DataContainer{
    private Object data;
    private ReentrantReadWriteLock rw = new ReentrantReadWriteLock();
    private ReentrantReadWriteLock.ReadLock r = rw.readLock();
    private ReentrantReadWriteLock.WriteLock w = rw.writeLock();
    public Object read(){
        log.debug("获取读锁...");
        r.lock();
        try {
            log.debug("读取");
            sleep(1);
            return data;
        } finally {
            log.debug("释放读锁");
            r.unlock();
        }
    }
    public void write(){
        log.debug("获取写锁...");
        w.lock();
        try {
            log.debug("写入");
        } finally {
            log.debug("释放写锁");
            w.unlock();
        }
    }
}

P248 reentrantreadwritelock 注意事项

读锁不支持条件变量。

1.重入时不支持升级:比如:不允许持有读锁的情况下去获取写锁,会导致获取写锁永久等待。

2.重入时支持降级:比如:持有写锁的情况下去获取读锁。

一开始持有读锁,在获取写锁前要释放读锁。

写锁可以降级为读锁,最后再释放锁。

P249  reentrantreadwritelock 应用之缓存

 

P250  reentrantreadwritelock 应用之缓存 问题分析

先清缓存,再查数据库设置缓存值,如果后面有新数据写入数据库,缓存中一直是旧值。

要求先更新数据库再清空缓存:

P251  reentrantreadwritelock 应用之缓存 实现

加写锁,保证原子:

双重检查,先查缓存再查数据库:

P252  reentrantreadwritelock 应用之缓存 补充

P253  reentrantreadwritelock 原理 t1 w.lock

写锁状态占state的低16位,读锁使用的是state的高16位。

P254  reentrantreadwritelock 原理 t2 r.lock

P255  reentrantreadwritelock 原理 t3 r.lock & t4 w.lock

P256  reentrantreadwritelock 原理 t1 w.unlock

P257  reentrantreadwritelock 原理 t1 w.unlock

P258  reentrantreadwritelock 原理 t2 r.unlock t3 r.unlock

P259 stampedlock 作用

P260 stampedlock 演示

乐观读锁并没有加读锁,只是验证戳,只有当戳被更改过才会真正加读锁。可以提升并发读的性能。

缺点:不支持条件变量,不支持可重入。

P261 semaphore 作用

Semaphore是信号量,用来限制能同时访问共享资源的线程上限。

P262 semaphore 演示

@Slf4j(topic = "c.Test1")
public class Test1 {
    public static void main(String[] args) throws InterruptedException {
        Semaphore semaphore = new Semaphore(3);
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                try {
                    semaphore.acquire();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                try {
                    log.debug("running...");
                    sleep(1);
                    log.debug("end...");
                }finally {
                    semaphore.release();
                }
            }).start();
        }
    }
}

限制访问共享资源的线程数: 

 

P263 semaphore 应用 改进数据库连接池

P264 semaphore 原理 acquire

P265 semaphore 原理 release

P266 countdownlatch 简介

P267 countdownlatch 改进

P268 countdownlatch 应用 等待多线程准备完毕

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值