赞
踩
博客背景:最近在做项目,经常会使用到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下载到本地文件夹,检查项目的完整性
使用命令:git -c http.sslVerify=false push -u 仓库地址 分支名称
操作步骤:
在GitHub上创建名为springcloud-config的新Repository:
复制HTTPS地址,比如:git@github.com:pbjlovezjy/springcloud-config.git
在spring-config文件夹下的空白处单击右键,进入Git Bash
然后输入:git -c http.sslVerify=false push -u 仓库地址 分支名称
然后会弹出一个界面,选择 sign in with your browser,选择授权git系统,然后输入密码即可:
可以直接将文件夹内容提交至仓库:
假如我们现在删掉了本地文件夹里的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的权限。
比如我现在想把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上,流程:首先要把项目保存到本地仓库,然后再从本地仓库推送到远程仓库。
创建分支
git branch "分支名称"
推送本地仓库数据到分支:
git push -u origin "分支名称"
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);
- }
- }
方法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,有异常返回错误。
使用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);
- }
- }
下图是效果:
LiMitLatch:用来限流,控制最大连接数,避免服务器被压垮。
Acceptor:负责接收新的socket连接
Poller:监听Socket channel连接上是否有可读的事件发生。一旦可读,封装一个任务对象socketProcessor提交给Executor线程池处理。
Executor线程池中的工作线程(worker)最终负责处理请求。
Tomcat线程池扩展了ThreadPoolExecutor,行为稍有不同:
- @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;
- }
- }
中间会用到多线程来执行任务的拆分和合并:
aqs的全称是:AbstractQueuedSynchronizer,是阻塞式锁和相关的同步器工具的框架。
其它同步器工具都是作为aqs的实现子类。
提供阻塞式锁,更类似于悲观锁。
特点:
1.用state属性来表示资源的状态,分为独占模式和共享模式,独占式式只有一个线程能够访问资源,共享式式允许多个线程访问资源。
2.提供了基于FIFO的等待队列,类似于Monitor的EntryList。
3.条件变量来实现等待、唤醒机制,支持多个条件变量,类似于Monitor的WaitSet。
子类主要实现了下面这些方法:
tryAcquire
tryRelease
tryAcquireShared
tryReleaseShared
isHeldExclusively
不可重入锁,本线程加了锁,下次还是同一个线程来了不能重复加。
- //自定义锁(不可重入锁)
- 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();
- }
- }
- //自定义锁(不可重入锁)
- 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();
- }
- }
下面是释放锁release的流程:
不可打断模式:即使被打断,仍会驻留在AQS队列中,等待获得锁后方能继续运行(是继续运行,只是打断标记被设为true)。
可打断模式:被打断之后直接抛出异常,不会再进入for循环。
下图是非公平锁实现,任意节点都能竞争:
下图是公平锁实现,只有头节点的后一个节点能竞争:
调用await,会进入addConditionWaiter流程,创建新的Node状态为-2
fullyRelease会把所有的锁都释放掉:
doSignal中transferForSignal返回true则转移成功,不会继续循环。如果转移失败的原因可能是线程放弃了对锁的竞争,因为超时等原因,就不会被加入队列。
waitStatus改为-1,表示它有责任去唤醒链表的下一个元素。
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();
- }
- }
- }
读锁不支持条件变量。
1.重入时不支持升级:比如:不允许持有读锁的情况下去获取写锁,会导致获取写锁永久等待。
2.重入时支持降级:比如:持有写锁的情况下去获取读锁。
一开始持有读锁,在获取写锁前要释放读锁。
写锁可以降级为读锁,最后再释放锁。
先清缓存,再查数据库设置缓存值,如果后面有新数据写入数据库,缓存中一直是旧值。
要求先更新数据库再清空缓存:
加写锁,保证原子:
双重检查,先查缓存再查数据库:
写锁状态占state的低16位,读锁使用的是state的高16位。
乐观读锁并没有加读锁,只是验证戳,只有当戳被更改过才会真正加读锁。可以提升并发读的性能。
缺点:不支持条件变量,不支持可重入。
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();
- }
- }
- }
限制访问共享资源的线程数:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。