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

博客背景:最近在做项目,经常会使用到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 应用 等待多线程准备完毕

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值