目录

  • 一、JMM
    • 1.volatile
    • 2.加载代码练习:
  • 二、JUC基础
    • 1.什么是进程/线程,并发/并行
      • 进程/线程
      • 并发/并行
    • 2.线程的状态
    • 3.线程 操作 资源类
    • 4.Lambda表达式
      • jdk8以后的interface
    • 5.判断/干活/通知
    • 6.防止虚假唤醒(while not if)
    • 7.标志位
      • lock精准通知
      • condition
    • 8.多线程八锁
  • 三、JUC集合类
    • 1.List
    • 2.set
  • 四、Callable接口
  • 五、强大的辅助类
    • 1.countDownLatch
    • 2.CyclicBarrier
    • 3.Semaphore
  • 六、ReentrantReadWriteLock
  • 七、阻塞队列
  • 八、线程池
    • 1.线程池的优点
    • 2.线程池的创建
      • Executors工具类的三种预设方案(实际开发中一般不用)
    • 3.七大参数
    • 4.线程池的底层工作原理
      • 阿里巴巴开发手册不让用三个预设(Executors),自定义自己写,这样也清晰(七大参数)
    • 5.自定义线程池:`ThreadPoolExecutor`核心类
    • 6.确定线程池的大小
  • 九、Java8新特性
    • 1.四大函数式接口
    • 2.Stream流式计算
  • 十、其他类
    • 1.分支合并框架
      • 相关类
      • 具体使用代码
    • 2.异步回调

一、JMM

1.volatile

volatile是java虚拟机提供的轻量级的同步机制(青春版synchronized)

可以保证可见性、有序性(指令重排序),但不能保证原子性

主内存是线程共享的。

JVM会从主内存中拷贝变量到线程中,即工作变量,工作变量是线程私有的。

volatile关键字的变量会在工作变量改变后,通知其他线程丢弃其拷贝重新在主内存中拷贝一份到自己的工作空间。

2.加载代码练习:

加载顺序:静态代码块>普通代码块>构造方法

静态代码直接放进方法区,第二次就不再需要加载了,直接指向方法区


二、JUC基础

  • 口诀:
  1. 高内聚低耦合前提下,线程操作资源类
  2. 判断/干活/通知
  3. 多线程交互中要防止多线程的虚假唤醒(判断只用while,不用if)
  4. 标志位

1.什么是进程/线程,并发/并行

进程/线程

进程就是idea,线程就是idea中的

详细一点进程就是操作系统分配资源的基本单位,线程比进程笑,是独立运行和调度的基本单位

并发/并行

并发就是同一时刻操作同一资源

并行就是同一时间操作同一资源(CPU在极短的时间段内完成时间片轮转)

2.线程的状态

public enum State {NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED;
}

new Thread().start()之后不是立马运行,只是就绪状态,要等待操作系统的cpu调度

3.线程 操作 资源类

操作就是对外暴露的调用方法。

  1. 创建资源类,不要去继承Thread类或者实现Runnable接口,就让他是一个纯净的资源类
  2. 用synchronized标识共享资源(旧) /

用ReentrantLock(可重入锁)来锁局部代码块(新),从而缩小我锁的粒度范围,不要一竿子打死,这样我的并发性就好、高

资源类中要:private Lock lock = new ReentrantLock();

ReentrantLock像公厕坑位的锁,synchronized就是公厕大门的锁

(学会用idea的live template来抽取代码为自定义快捷输入)(语法糖)

4.Lambda表达式

  • 口诀:拷贝小括号,写死右箭头,落地大括号

匿名内部类代码太长,所以要用lambda表达式简化代码

如果要用lambda表达式,接口必须是函数式接口(functional interface)

可以直接在接口上加@FuctionalInterface注解帮助校验,不加的话java也会自己帮我们加

jdk8以后的interface

可以有部分带实现的方法 default

可以有静态方法(接口名直接调用)

5.判断/干活/通知

这里判断直接上while是为了方便能够运行,教程中先是if为了引出虚假唤醒的问题。

老版写法(synchronized):

class Bakery{private int cake = 0;public synchronized void increment() throws InterruptedException {while(cake!=0){this.wait();}cake++;System.out.println(Thread.currentThread().getName()+"\t 数量:"+cake);this.notifyAll();}public synchronized void decrement() throws InterruptedException {while(cake==0){this.wait();}cake--;System.out.println(Thread.currentThread().getName()+"\t 数量:"+cake);this.notifyAll();}}public class ProducerAndConsumer {public static void main(String[] args) {Bakery bakery = new Bakery();new Thread(()->{try {for(int i =1;i<=10;i++) bakery.increment();} catch (InterruptedException e) {e.printStackTrace();}},"蛋糕师1").start();new Thread(()->{try {for(int i =1;i<=10;i++) bakery.increment();} catch (InterruptedException e) {e.printStackTrace();}},"蛋糕师2").start();new Thread(()->{try {for(int i =1;i<=10;i++) bakery.decrement();} catch (InterruptedException e) {e.printStackTrace();}},"消费者1").start();new Thread(()->{try {for(int i =1;i<=10;i++) bakery.decrement();} catch (InterruptedException e) {e.printStackTrace();}},"消费者2").start();}
}

新版本(ReentrantLock版本): (main方法就不写了)

lock配合condition的await和signal 对应synchronized配合object的wait和notify

class Bakery {private int cake = 0;private Lock lock = new ReentrantLock();private Condition condition = lock.newCondition();public void increment() throws InterruptedException {lock.lock();try {while (cake != 0) {condition.await();}cake++;System.out.println(Thread.currentThread().getName() + "\t 数量:" + cake);condition.signalAll();} finally {lock.unlock();}}public void decrement() throws InterruptedException {lock.lock();try {while (cake == 0) {condition.await();}cake--;System.out.println(Thread.currentThread().getName() + "\t 数量:" + cake);condition.signalAll();} finally {lock.unlock();}}}

6.防止虚假唤醒(while not if)

虚假唤醒的原因:

可能因为多个进程进入if后被wait,而if只判断一次,然后因为某个线程的notifyAll后再执行下去,导致连续多次++或—

解决:把原来的if换成while,让他们即使被唤醒了,仍要重新判断

7.标志位

使用标志位可以实现精准通知(我做完了你做,实现多线程之间的顺序调用)

lock精准通知

lock配合condition的await和signal 对应synchronized配合object的wait和notify

class SharedResource {private int sharedreference = 1;private Lock lock = new ReentrantLock();//condition1 != condition2 != condition3private Condition condition1 = lock.newCondition();private Condition condition2 = lock.newCondition();private Condition condition3 = lock.newCondition();private int num = 1;public void share01() throws InterruptedException {lock.lock();try {while (num != 1) {condition1.await();}System.out.println(Thread.currentThread().getName() + "\t进入");num = 2;condition2.signal();} finally {lock.unlock();}}public void share02() throws InterruptedException {lock.lock();try {while (num != 2) {condition2.await();}System.out.println(Thread.currentThread().getName() + "\t进入");num = 3;condition3.signal();} finally {lock.unlock();}}public void share03() throws InterruptedException {lock.lock();try {while (num != 3) {condition3.await();}System.out.println(Thread.currentThread().getName() + "\t进入");num = 1;condition1.signal();} finally {lock.unlock();}}}public class SequenceSignal {public static void main(String[] args) {SharedResource sharedResource = new SharedResource();new Thread(() -> {try {for (int i = 1; i <= 10; i++) sharedResource.share01();} catch (InterruptedException e) {e.printStackTrace();}}, "Thread 1").start();new Thread(() -> {try {for (int i = 1; i <= 10; i++) sharedResource.share02();} catch (InterruptedException e) {e.printStackTrace();}}, "Thread 2").start();new Thread(() -> {try {for (int i = 1; i <= 10; i++) sharedResource.share03();} catch (InterruptedException e) {e.printStackTrace();}}, "Thread 3").start();}
}

condition

为了搞清楚精准通知的原理,应该先要弄懂condition是到底是个什么东西

一个Condition实例本质上绑定到一个锁

一定要看下面这个链接,这会马上备考概率论了所以没看

https://segmentfault.com/a/1190000037693391

8.多线程八锁

class Phone {public synchronized void sendEmail() throws InterruptedException {TimeUnit.SECONDS.sleep(2);System.out.println("============sendEmail");}public  synchronized void sendSMS() {System.out.println("============sendSMS");}public void hello(){System.out.println("======HelloWorld");}
}public class EightLock {public static void main(String[] args) {Phone phone = new Phone();new Thread(() -> {try {phone.sendEmail();} catch (Exception e) {e.printStackTrace();}}, "A").start();new Thread(() -> {try {phone.sendSMS();} catch (Exception e) {e.printStackTrace();}}, "B").start();}
}
邮件在先,短信在后1 标准访问,先打印短信还是邮件-----------------------------------邮件
2 停4秒在邮件方法内,先打印短信还是邮件---------------------------邮件
3 普通的hello方法,是先打邮件还是hello--------------------------hello
4 现在有两部手机,先打印短信还是邮件-----------------------------短信
5 两个静态同步方法,1部手机,先打印短信还是邮件-------------------邮件
6 两个静态同步方法,2部手机,先打印短信还是邮件-------------------邮件
7 1个静态同步方法,1个普通同步方法,1部手机,先打印短信还是邮件----短信
8 1个静态同步方法,1个普通同步方法,2部手机,先打印短信还是邮件----短信

main线程里写的线程代码先后执行顺序还真不一定 ,需要等待操作系统对线程优先级排序 阻塞的数字啥的

TimeUnit.SECONDS.sleep()

原因:(前提得锁的对象是一致才能发挥锁的作用)

1、2:synchronized锁的是当前对象this,即phone1

3:锁的东西不一样,普通方法不会去抢,各不相干

4:锁的不一样对象(2部手机),因为邮件要挂2s,所以先短信

5、6:synchronized修饰静态方法锁的是整个类,即class,而不是对象

7、8:锁的不是一个东西


三、JUC集合类

1.List

请举例说明List不安全:(add没有synchronized修饰)

报:java.util.ConcurrentModificationException

  • ArrayList、StringBuilder、CopyOnWriteArrayList底层都用到了Arrays.copyOf
  • Arrays.copyOf方法返回原始数组的副本,截断或填充空值以获得指定的长度。

ArrayList:

CopyOnWriteArrayList:

CopyOnWrite即写时复制的容器。往容器中添加元素的时候,不直接往当前容器添加,而是先复制出一个新的容器,往新的容器中添加元素。添加完成后再将原容器的引用指向新容器setArray(newElements);。这样就能并发的读而不需要加锁,这是一种读写分离的思想,读和写不同的容器。

StringBuilder:

解决:

ArrayList<Object> objects = new ArrayList<>();//线程不安全
Vector<Object> objects2 = new Vector<>(); //线程安全,全是synchronized修饰,基本不用,性能太差
Collections.synchronizedList(objects);//转化为线程安全
CopyOnWriteArrayList<Object> objects1 = new CopyOnWriteArrayList<>();//线程安全,写时复制

2.set

hashset的底层是hashmap

hashset的add调的是hashmap的put value是PRESENT的常量

concurrenthashmap copyonwriteset 线程安全(没有深入,最好深入一下)

https://segmentfault.com/a/1190000021144667 性能对比

又带着复习了一遍hashmap,数组+链表+红黑树

负载因子*当前容量=threshold 门槛


四、Callable接口

  • 与继承thread类、实现runnable接口相比,实现callable接口有返回值、有异常,重写方法不一样。

因为Thread的构造方法中只提供了runnable接口,没有callable接口

所以根据多态的性质,我们需要一个同时实现了runnable和callable接口的类,即futuretask类

FutureTask,实现了runnable接口,构造方法可以传入callable接口

然后在thread中传入futuretask,具体代码如下

class Mythread implements Callable<Integer>{@Overridepublic Integer call() throws Exception {System.out.println("******进入future方法");TimeUnit.SECONDS.sleep(2);return 123;}
}
public class MyCallable {public static void main(String[] args) throws ExecutionException, InterruptedException {Mythread mythread = new Mythread();FutureTask<Integer> integerFutureTask = new FutureTask<>(mythread);for(int i=1;i<=10;i++){new Thread(integerFutureTask,String.valueOf(i)).start();}System.out.println("计算完成");System.out.println(integerFutureTask.get());//get不到会阻塞主线程,所以get一般放在最后}
}

注意细节:

get不到会阻塞主线程,所以get一般放在最后

一个futuretask 多个线程调用只会调用一次,内部有缓存。

futuretask.get 帮忙计算买水的例子


五、强大的辅助类

1.countDownLatch

上晚自习班长关门的例子

countDownLatch : 计数器

CountDownLatch countDownLatch = new CountDownLatch(5);
for(int i=1;i<=5;i++)
new Thread(()->{System.out.println(Thread.currentThread().getName()+"走出了教室");countDownLatch.countDown();},String.valueOf(i)).start();countDownLatch.await();
System.out.println("========班长关门了");

CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,这些线程会阻塞。

其它线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞),

当计数器的值变为0时,因await方法阻塞的线程会被唤醒,继续执行。

2.CyclicBarrier

CyclicBarrier : 到齐开会,集齐龙珠

CyclicBarrier cyclicBarrier = new CyclicBarrier(5, () -> {System.out.println("开始开会");});
for (int i = 1; i <= 5; i++) {new Thread(() -> {try {System.out.println(Thread.currentThread().getName() + "进入了会议室");cyclicBarrier.await();} catch (Exception e) {e.printStackTrace();}}, String.valueOf(i)).start();
}=

CyclicBarrier的字面意思是可循环(Cyclic)使用的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。

线程进入屏障通过CyclicBarrier的await()方法

3.Semaphore

信号量机制:争车位。

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

Semaphore semaphore = new Semaphore(2);
for (int i = 1; i <= 5; i++) {new Thread(() -> {try {semaphore.acquire();System.out.println(Thread.currentThread().getName() + "进入了停车场");TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();} finally {System.out.println(Thread.currentThread().getName() + "离开了停车场");semaphore.release();}}, String.valueOf(i)).start();
}

也能利用信号量机制实现synchronized实现不到的效果,如让一个线程每次拿到10s锁就释放


六、ReentrantReadWriteLock

读写锁。

class MyCache {private volatile Map<String, Object> map = new HashMap<>();private ReentrantReadWriteLock readwriteLock=new ReentrantReadWriteLock();public void put(String key, Object value) {readwriteLock.writeLock().lock();System.out.println(Thread.currentThread().getName() + "\t 正在写" + key);//暂停一会儿线程try {//TimeUnit.SECONDS.sleep(2);} catch (Exception e) {e.printStackTrace();}map.put(key, value);System.out.println(Thread.currentThread().getName() + "\t 写完了" + key);readwriteLock.writeLock().unlock();}public Object get(String key) {readwriteLock.readLock().lock();Object result = null;System.out.println(Thread.currentThread().getName() + "\t 正在读" + key);try {TimeUnit.SECONDS.sleep(2);result = map.get(key);System.out.println(Thread.currentThread().getName() + "\t 读完了" + result);} catch (Exception e) {e.printStackTrace();}finally {readwriteLock.readLock().unlock();}return result;//return 前会执行finally}
}public class ReadwriteLock {public static void main(String[] args) {MyCache myCache = new MyCache();for (int i = 1; i <= 5; i++) {final int num = i;new Thread(() -> {myCache.get(String.valueOf(num));}, String.valueOf(i)).start();}for (int i = 1; i <= 5; i++) {final int num = i;new Thread(() -> {myCache.put(String.valueOf(num), String.valueOf(num));}, String.valueOf(i)).start();}}
}

七、阻塞队列

add throwing an IllegalStateException if this queue is full

offer returning true upon success and false if this queue is full. This method is generally preferable to method add(E)

put 阻塞无返回值

offer(e,time,unit) 带返回值,阻塞超时退出 (这个好诶)


八、线程池

银行窗口,办理业务,请xx到x号窗口办理业务

1.线程池的优点

就像数据库连接池一样,我们频繁创建销毁线程需要消耗资源,于是用线程池来控制运行的线程数量。

处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果任务数量超过了最大线程数量,超出数量的任务排队等候(阻塞队列),等其他线程执行完毕,再从队列中取出任务来执行。

它的主要特点为:线程复用;控制最大并发数;管理线程。

2.线程池的创建

主要是通过Executor框架实现的,核心是ThreadPoolExecutor类,我们自定义也是用这个类

Executors工具类的三种预设方案(实际开发中一般不用)

3.七大参数

corePoolSize:核心线程数

maximumPoolSize:池子的最大线程数

keepAliveTime:非核心线程数空闲持续时间

unit:时间单位

workQueue:阻塞队列

threadFactory:线程创建的工厂,一般为默认

handler:拒绝策略

4.线程池的底层工作原理

先核心线程工作,如果繁忙,任务进阻塞队列

如果阻塞队列满了,就会扩容到池子最大线程数

如果还是让阻塞队列满了,就会进行拒绝策略

然后当一个线程无事可做超过keepAliveTime时间,那么如果当前线程数量大于corePoolSize,这个线程就会被停掉。所以线程池的所有任务完成后,最终收缩到corePoolSize的大小

阿里巴巴开发手册不让用三个预设(Executors),自定义自己写,这样也清晰(七大参数)

5.自定义线程池:ThreadPoolExecutor核心类

threadPool.execute(()→{}); 线程池执行

threadPool.shutdown(); 线程池关闭

public static void main(String[] args) {ExecutorService myThreadPool = new ThreadPoolExecutor(2,  //corePoolSie5, //maximumPoolSize2L,  //keepAliveTimeTimeUnit.SECONDS, //unitnew ArrayBlockingQueue<>(3), //workQueueExecutors.defaultThreadFactory(), //threadFactorynew ThreadPoolExecutor.AbortPolicy()   //handler 【默认】//new ThreadPoolExecutor.CallerRunsPolicy() //handler//new ThreadPoolExecutor.CallerRunsPolicy() //handler//new ThreadPoolExecutor.DiscardPolicy() //handler);try {//10个顾客请求for (int i = 1; i <= 10; i++) { //最大容纳数是:maximumPoolSize+workQueue 5+3=8myThreadPool.execute(() -> {System.out.println(Thread.currentThread().getName() + "\t 办理业务");});}} catch (Exception e) {e.printStackTrace();} finally {myThreadPool.shutdown();}
}

6.确定线程池的大小

不写死,Runtime.getRuntime().availableProcessors(); 获得cpu核数

根据具体业务设定最大线程数:

  • CPU密集型:大量计算,cpu 占用越接近 100%;Nthreads = Ncpu+1
  • IO密集型:大量网络,文件操作;Nthreads = Ncpu x Ucpu x (1 + W/C),

详见《Java并发编程实践》

  • Ncpu = CPU的数量
  • Ucpu = 目标CPU的使用率, 0 <= Ucpu <= 1
  • W/C = 等待时间与计算时间的比率

九、Java8新特性

1.四大函数式接口

使用 @FunctionalInterface 标识,有且仅有一个抽象方法,可被隐式转换为 lambda 表达式

如果代码量只有一行,可以适当简化lambda表达式

2.Stream流式计算

  • 集合讲的是数据,流讲的是计算
  • 注意:Stream流式计算 和 IO文件读写流 两者完全不是一回事

特点:

  • Stream自己不会存储数据,不会改变源对象,他们会返回一个持有结果的新Stream
  • Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

Stream 类:引入函数式编程风格,提供了很多功能,使代码更加简洁。方法包括 forEach 遍历、count 统计个数、filter 按条件过滤、limit 取前 n 个元素、skip 跳过前 n 个元素、map 映射加工、concat 合并 stream 流等。

这一些列方法需要传入四大函数式接口,传入的参数是stream流。

如:

User u1 = new User(1,"a",10);
User u2 = new User(2,"b",17);
User u3 = new User(3,"c",20);
User u4 = new User(4,"d",28);
User u5 = new User(5,"e",30);
List<User> users = Arrays.asList(u1, u2, u3, u4, u5);
users.stream().filter(p -> {return p.getId() % 2 == 0;
}).filter(p -> {return p.getAge() > 24;
}).map(f -> {return f.getUserName().toUpperCase();
}).sorted((o1, o2) -> {return o2.compareTo(o1);
}).limit(1).forEach(System.out::println);

十、其他类

1.分支合并框架

Fork:把一个复杂任务进行分拆,大事化小
Join:把分拆任务的结果进行合并

主要还是分治的思想,只不过这里是多个线程分治

相关类

  • ForkJoinPool:分支合并池(类似线程池)
  • ForkJoinTask:(类似FutureTask的构造方法接callable),这里要用Pool的submit方法
  • RecursiveTask:抽象类继承自ForkJoinTask,用来被继承,重写compute方法;这里作多线程的递归

具体使用代码

class MyTask extends RecursiveTask<Integer> {private static final Integer ADJUST_VALUE = 10;private int begin;private int end;private int result;public MyTask(int begin, int end) {this.begin = begin;this.end = end;}@Overrideprotected Integer compute() {if ((end - begin) <= ADJUST_VALUE) { //10以内太小了就没必要拆分了for (int i = begin; i <= end; i++) {result = result + i;}} else {int middle = (begin + end) / 2;MyTask task01 = new MyTask(begin, middle);MyTask task02 = new MyTask(middle + 1, end);task01.fork();task02.fork();result = task01.join() + task02.join();}return result;}
}public class ForkJoinDemo {public static void main(String[] args) throws Exception {MyTask myTask = new MyTask(0, 100); //继承了RecursiveTask,实现0~100的相加ForkJoinPool forkJoinPool = new ForkJoinPool();ForkJoinTask<Integer> forkJoinTask = forkJoinPool.submit(myTask); //分支合并池提交TaskSystem.out.println(forkJoinTask.get());forkJoinPool.shutdown();}
}

2.异步回调

public static void main(String[] args) throws Exception {CompletableFuture<Void> completableFuture1 = CompletableFuture.runAsync(()->{ //Void是void包装类System.out.println(Thread.currentThread().getName()+"没有返回值,update mysql ok");});completableFuture1.get();//------------------------------------------------------------------------------------------CompletableFuture<Integer> completableFuture2 = CompletableFuture.supplyAsync(()->{System.out.println(Thread.currentThread().getName()+"有返回值,insert mysql ok");int i = 1/0;return 1024;});System.out.println(completableFuture2.whenComplete((t, u) -> {  // BiConsumer<? super T, ? super Throwable> action 要有两个输入参数,且没有返回值System.out.println("t=" + t); //t:正确完成System.out.println("u=" + u); //u:异常}).exceptionally(f -> {   //如果异常,则走这里System.out.println("exception:" + f.getMessage());return 404;}).get());}

JUC基础(周阳老师笔记相关推荐

  1. JVM基础知识(周阳老师笔记

    目录 一.类加载器 二.native 三.程序计数器 四.方法区 栈管运行,堆管存储 五.Java栈 六.堆 1.数据存放 内存空间的分配 堆内存调优+日志信息解读 2.GC回收算法 a.引用计数法 ...

  2. JUC基础笔记(尚硅谷周阳JUC的笔记)

    JUC 概述 卖票 Lambda表达式 线程间的通信 一道经典的线程按顺序打印 多线程8锁 集合线程不安全 实现多线程方式 CountDownLatch CyclicBarrier 读写锁 阻塞队列 ...

  3. 周阳老师JUC并发编程

    1. 序章 1)JUC是什么? java.util.concurrent在并发编程中使用的工具包 对JUC知识的高阶内容讲解和实战增强 2)为什么学习并用好多线程极其重要? 硬件方面: 摩尔定律: 它 ...

  4. 【学习笔记】尚硅谷周阳老师的Docker教程学习笔记

    本文是尚硅谷周阳老师的Docker教程的相关学习笔记,暂时还没有检查勘误过. 一.Docker简介 1. 简介 Docker的出现是为了解决代码在本机可以正常运行,而部署到其他机器不能运行的问题.这是 ...

  5. 尚硅谷周阳老师 SpringCloud第二季学习笔记

    前言:首先感谢尚硅谷周阳老师的讲解,让我对springcloud有了很好的理解,周阳老师的讲课风格真的很喜欢,内容充实也很幽默,随口一说就是一个段子,我也算是周阳老师的忠实粉丝啦. 先说说课程总体内容 ...

  6. 尚硅谷周阳老师2020年 SpringCloud(H版和Alibaba) 视频教程学习时整理的笔记记录和代码

    尚硅谷周阳老师2020年 SpringCloud(H版和Alibaba)视频教程学习时整理的笔记记录和代码 尚硅谷周阳老师SpringCloud(H版和Alibaba)学习.代码摘录,下面是各个mod ...

  7. (CZ深入浅出Java基础)线程笔记

    这是刘意老师的JAVA基础教程的笔记 讲的贼好,附上传送门 传智风清扬-超全面的Java基础 一.线程的引入 1.多线程概述 进程 a.正在运行的程序,是系统进行资源分类和调用的独立单位. b.每个进 ...

  8. C语言基础入门学习笔记

    前言 我是一个初中生,过完暑假就是一个高一的学生了.在这个暑假里,我学习了韦东山老师和唐佐林老师的课程,所以我写下这个笔记来记录自己的成长历程. C语言基础入门学习笔记 格式 #include < ...

  9. springCloud2020尚硅谷周阳老师教程

    前言:今天看到周阳老师出了新课,十分欣喜,很喜欢周阳老师的讲课风格,内容也充实,我也算是周阳老师忠实粉丝啦. 新出的springcloud第二版很符合我现阶段的学习需求.但美中不足的是,目前只有视频资 ...

最新文章

  1. 【CV】Pytorch一小时入门教程-代码详解
  2. 53位作者只有1个华人?NeurIPS 2021大奖揭晓,Google、DeepMind、斯坦福百花齐放
  3. 如何才能写出一手高质量优美的代码
  4. 老男孩linux培训期中学生作业文档目录展示
  5. jq中ajax返回数据的json_encode,jQuery AJAX使用JSON返回对PHP脚本的调用
  6. sql语句Order by 报错列名不明确
  7. WCF-001:WCF的发布
  8. mui框架 页面无法滚动解决方法
  9. 蓝桥杯 ALGO-28 算法训练 星际交流
  10. 批量下载 Windows 零散系统更新的得力工具 -Windows Updates Downloader
  11. IPD开发流程TR1-TR6各个阶段简介
  12. python实验原理_python实验报告5
  13. 单链表的逆置(递归和非递归)
  14. 「cocos2d-x」垂直射击游戏之宇智波鼬 VS 九尾狐(1)
  15. VPS新手向折腾笔记
  16. SQL语句常见面试题(一)
  17. Python 基础|while 循环语句
  18. 折半查找判定树 二叉排序树 查找成功平均查找长度 查找失败平均查找长度
  19. 双11越来越“高大上”,你的工资还配得上它吗?
  20. 西门子s7-1200plc通过 458modbus通讯控制5台变频器

热门文章

  1. 基于matlab异步电机 s函数,基于MATLAB/S-函数的三相异步电机建模与仿真
  2. 电脑小白学习第六课---打包压缩软件WINRAR
  3. 安卓APP上传市场开通开发者权限\上传操作
  4. SaaS运维平台 云HIS系统源码 一体化电子病历系统
  5. UI设计——Figma新手教学
  6. MySQL连接查询(内连接,外连接,全连接)
  7. 编译 Unity 4.3.1 引擎
  8. 第六课,Extjs中常用表单介绍与应用
  9. 相机标定序列——Tsai两步标定
  10. 【macOS温度监控】-istats