一.并发的定义

并发:对于这个概念一直就是没怎么搞懂,就是感觉特别的生疏,(自己从从字面上理解就是多个东西,一起出发),所以就上网上查了一些资料:

同时拥有两个或多个线程,如果程序在单核处理器上运行,多个线程将交替地换入或者换出内存,这些线程是同时“存在”的,每个线程都处于执行过程中的某个状态,如果运行在多核处理器上,此时,程序中的每个线程都将分配到一个处理器核上,因此可以同时运行。

高并发(High Concurrency):

是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计保证系统能够同时并行处理很多请求。

两者区别:

并发:多个线程操作相同的资源,保证线程安全,合理使用资源。

高并发(High Concurrency):服务能同时处理很多请求,提高程序性能

二.线程的定义

1.线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。

在网上查资料线程是这么定义的,其实说简单点,线程其实就是可以多个同时进行,也称之为多线程。下面是个生活中的例子

比如一天突然来一个电话,张三跟我说:“他的朋友李四的卡里面没有钱了,想让我借给他的朋友点

,我之后便去银行,到了银行,我先把钱转给张三,之后张三再把钱转到他的朋友李四的卡里。

其实,这个例子就是多线程的同时操作,第一个线程是  我把钱转到张三的卡里面  ;第二个线程是  张三在把钱转到李四的卡里。
在有一个例子:都听过龟兔赛跑的事情吧!这其实也是多线程的问题

兔子和乌龟同时从原点出发,比谁先到终点,这其实就是两个多线程共同进行,且互不影响。
在譬如说:咱们运行一个程序,程序没开始运行的时候是静态的,经过进程,再到线程,

它真正执行的也就是线程,其中main()称之为主线程。
在实际生活中多线程也是非常有用的,

比如,我们在浏览器上可以同时下载几张图片。

上面所说的都是线程的例子,它还是与我们生活密切相关的,用处也是非常大的。

线程就是独立的执行路径

在程序运行时,即使没有自己创建线程,后台也会有多少少线程,如主线程,gc线程

main()称之为主线程,为系统的入口,用于执行整个程序

在一个进程中,如果开辟了多少线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能认为的干预的。

对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制

每个线程在自己的工作内存交互,内存控制不会造成吧数据不一致。

三.怎样实现多线程?

1.继承Thread类实现多线程的能力

package com.kuang.demo1;

//创建Thread类  重写run()方法  调用start()方法
//线程开始不一定立即执行,由CPU执行
public class TestThread1 extends Thread{
@Override
public void run() {
//run()方法线程
for(int i=0;i<20;i++) {
System.out.println("我爱写代码--" +i);
}
} public static void main(String[] args) {
//main线程 主线程 //创建一个线程
TestThread1 testThread1 = new TestThread1(); //调用start()方法开启线程
testThread1.start(); for(int i=0;i<20;i++) {
System.out.println("我爱学习多线程" + i);
}
}
}

2.实现Runnable接口建立多线程

package com.kuang.demo1;

//创建线程的第二种 首先实现runnable接口,接着重写run()方法,执行线程时需要丢入runnable接口实现类,调用start方法
public class TestThread2 implements Runnable{
@Override
public void run() {
//run方法线程体
for(int i=0;i<20;i++) {
System.out.println("我在看代码--" + i);
}
} public static void main(String [] args) {
//创建runnable实现接口类的线程
TestThread2 testThread2 = new TestThread2();
new Thread(testThread2).start(); for(int i=0;i<20;i++) {
System.out.println("我在学习多线程--" + i);
}
}
}

3.实现Callable接口(这个我就不举代码例子了,了解即可)

1.实现Callable接口,需要返回值类型

2.重写call方法,需要抛出异常

3.创建目标对象

4.创建执行服务:ExecutorService ser = Excutors.newFixedThreadPool(1);

5.提交执行:Future<Boolean> result1 = ser.submit(t1);

6.获取结果:boolean r1 = result.get();

7.关闭服务:ser.shutdownNow();

项目实例——龟兔赛跑

package com.kuang.demo1;

//模拟龟兔赛跑
public class Race implements Runnable { //胜利者
private static String winner; @Override
public void run() {
for(int i=0;i<=100;i++) { //模拟兔子休息,设置让他十步卡以下
if(Thread.currentThread().getName().equals("兔子")&&( i+1)%10==0) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//判断比赛是否结束
boolean flag = gameOver(i);
//如果比赛结束,就停止程序
if(flag) {
break;
}
System.out.println(Thread.currentThread().getName() +"-->跑了" + i + "步");
}
} //判断是否完成比赛
private boolean gameOver(int steps) {
//判断是否有胜利者
if(winner!=null) {
return true;
}{
if(steps>=100) {
winner = Thread.currentThread().getName();
System.out.println("winner is " + winner);
return true;
}
}
return false;
} public static void main(String[] args) {
Race race = new Race(); new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
}

运行结果:

兔子-->跑了0步
兔子-->跑了1步
乌龟-->跑了0步
乌龟-->跑了1步
兔子-->跑了2步
乌龟-->跑了2步
乌龟-->跑了3步
乌龟-->跑了4步
兔子-->跑了3步
乌龟-->跑了5步
乌龟-->跑了6步
兔子-->跑了4步
兔子-->跑了5步
乌龟-->跑了7步
乌龟-->跑了8步
乌龟-->跑了9步
兔子-->跑了6步
兔子-->跑了7步
兔子-->跑了8步
乌龟-->跑了10步
乌龟-->跑了11步
乌龟-->跑了12步
乌龟-->跑了13步
乌龟-->跑了14步
乌龟-->跑了15步
乌龟-->跑了16步
乌龟-->跑了17步
乌龟-->跑了18步
乌龟-->跑了19步
乌龟-->跑了20步
乌龟-->跑了21步
乌龟-->跑了22步
乌龟-->跑了23步
乌龟-->跑了24步
乌龟-->跑了25步
乌龟-->跑了26步
乌龟-->跑了27步
乌龟-->跑了28步
乌龟-->跑了29步
乌龟-->跑了30步
乌龟-->跑了31步
乌龟-->跑了32步
乌龟-->跑了33步
乌龟-->跑了34步
乌龟-->跑了35步
乌龟-->跑了36步
乌龟-->跑了37步
乌龟-->跑了38步
乌龟-->跑了39步
乌龟-->跑了40步
乌龟-->跑了41步
乌龟-->跑了42步
乌龟-->跑了43步
乌龟-->跑了44步
乌龟-->跑了45步
乌龟-->跑了46步
乌龟-->跑了47步
乌龟-->跑了48步
乌龟-->跑了49步
乌龟-->跑了50步
乌龟-->跑了51步
乌龟-->跑了52步
乌龟-->跑了53步
乌龟-->跑了54步
乌龟-->跑了55步
乌龟-->跑了56步
乌龟-->跑了57步
乌龟-->跑了58步
乌龟-->跑了59步
乌龟-->跑了60步
乌龟-->跑了61步
乌龟-->跑了62步
乌龟-->跑了63步
乌龟-->跑了64步
乌龟-->跑了65步
乌龟-->跑了66步
乌龟-->跑了67步
乌龟-->跑了68步
乌龟-->跑了69步
乌龟-->跑了70步
乌龟-->跑了71步
乌龟-->跑了72步
乌龟-->跑了73步
乌龟-->跑了74步
乌龟-->跑了75步
乌龟-->跑了76步
乌龟-->跑了77步
乌龟-->跑了78步
乌龟-->跑了79步
乌龟-->跑了80步
乌龟-->跑了81步
乌龟-->跑了82步
乌龟-->跑了83步
乌龟-->跑了84步
乌龟-->跑了85步
乌龟-->跑了86步
乌龟-->跑了87步
乌龟-->跑了88步
乌龟-->跑了89步
乌龟-->跑了90步
乌龟-->跑了91步
乌龟-->跑了92步
乌龟-->跑了93步
乌龟-->跑了94步
乌龟-->跑了95步
乌龟-->跑了96步
乌龟-->跑了97步
乌龟-->跑了98步
乌龟-->跑了99步
winner is 乌龟

四.lambda表达式

Lambda 表达式(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。

Java 8的一个大亮点是引入Lambda表达式,使用它设计的代码会更加简洁。当开发者在编写Lambda表达式时,也会随之被编译成一个函数式接口。

函数式接口:只包含一个抽象方法

package com.kuang.lambda;

/*
*推到lambda表达式
*函数式接口:只包含一个抽象方法
*/ public class TestLambda1{ //2.静态内部类
static class Like2 implements ILike{
@Override
public void Lambda() {
System.out.println("I like Lambda2");
}
}
public static void main(String[] args) {
//用接口创建对象
ILike like = new Like();
like.Lambda(); like = new Like2();
like.Lambda(); //3.局部内部类
class Like3 implements ILike{
@Override
public void Lambda() {
System.out.println("I like Lambda3");
}
} like = new Like3();
like.Lambda(); //4.匿名内部类,需要new一个接口,没有类的名称,必须借助接口
like = new ILike() {
@Override
public void Lambda() {
System.out.println("I like Lambda4");
}
}; like.Lambda(); //5.用Lambda简化
like = ()->{
System.out.println("I like Lambda5");
};
like.Lambda(); }
} //1.定义一个接口
interface ILike{
void Lambda();
} //实现接口
class Like implements ILike{
@Override
public void Lambda() {
System.out.println("I like Lambda1");
}
} 运行结果:
I like Lambda1
I like Lambda2
I like Lambda3
I like Lambda4
I like Lambda5

在举一个就光是lambda用法的例子,lambda用法的核心语句就是

对象=(所要强调的)->{

};

package com.kuang.lambda;

public class TestLambda2{
public static void main(String[] args) {
ILove love =(int a )-> {
System.out.println("I Love You -->" + a);
};
love=(a)->{
System.out.println("I Love You -->" + a);
};
love.love(2);
}
} interface ILove{
void love(int a);
} 运行结果:
I Love You -->2

五.线程的5种状态

New(新建),Runnable(可运行),(堵塞和等待). 运行,Terminated(终止)

1.线程停止

不推荐使用JDK提供的stop(),destory()方法

推荐线程自己停止下来

建议使用一个标志位进行终止变量 当flag=false,则终止线程运行。

例如下列实例:

整体思路:首先设立一个标志flag为真,之后进行重写,当flag真的时候输出线程。

其次设立一个公开的方法停止线程,在这里我们不要其它的方法,用stop()就可以,令this.flag=false 主函数创建一个对象,

之后主函数new Thread().start();的形式启动它 进行设置循环多少次,输出一个main线程,令i等于几的时候线程停止 对象.stop();

下面那个例子我设定的是一共进行20次循环 main()线程第五次的时候,线程终止了。

这是代码的核心要点

public class TestStop implements Runnable{
//线程种定义线程体使用的标记
private boolean flag=true; @Override
public void run(){
//线程体使用该标记
while(flag){
System.out.println("run......Thread");
}
} //对外提供方法改变标识
public void stop(){
this.flag=false;
}
}

完整代码如下图所示:

package com.kuang.state;

import java.util.Iterator;

//测试stop
//j建议线程正常停止-->,利用次数,不建议死循环
//建议使用标志位-->设置一个标志为
//
public class TestStop implements Runnable { //1.设置一个标志为
private boolean flag = true;
@Override
public void run() {
int i=0;
while(flag) {
System.out.println("run......Thread" + i ++);
}
} //设置一个公开的方法停止线程,转换标志位
public void stop() {
this.flag=false;
}
public static void main(String[] args) {
TestStop teststop= new TestStop();
new Thread(teststop).start(); //启动 for(int i=0;i<20;i++) {
System.out.println("main线程" + i);
if(i==5) {
//调用stop方法切换标志位,让该线程停止
teststop.stop();
System.out.println("线程停止了");
}
}
}
}

运行结果:

main线程0
run......Thread0
main线程1
run......Thread1
run......Thread2
main线程2
run......Thread3
main线程3
main线程4
run......Thread4
main线程5
线程停止了
run......Thread5
main线程6
main线程7
main线程8
main线程9
main线程10
main线程11
main线程12
main线程13
main线程14
main线程15
main线程16
main线程17
main线程18
main线程19

2.线程礼让Yield

在API中有一个这样的方法 static void yield()

指的是:当前正在执行的线程向另一个线程交出运行权,注意,这是一个静态方法。

要点:

1.礼让(yield)线程,让当前正在执行的线程暂停,但不堵塞。

2.将线程从运行状态转为就绪状态

3.让CPU重新调度,礼让不一定成功!看CPU的心情。

代码实例:

package com.kuang.demo1;

//测试礼让线程
//礼让不一定成功,看cpu的心情
public class TestYiled { public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield ,"a").start();
new Thread(myYield ,"b").start(); }
} class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程开始了");
Thread.yield();//礼让
System.out.println(Thread.currentThread().getName()+ "线程结束了");
}
} 运行结果: 第一次:
b线程开始了
a线程开始了
b线程结束了
a线程结束了 第二次:
a线程开始了
a线程结束了
b线程开始了
b线程结束了

看!它的运行结果是变化的,因为“礼让”有时候不一定会成功,他需要看CPU的心情。

3.线程休眠sleep

线程休眠总结:

1.sleep(时间)指定当前堵塞的毫秒数

2.sleep存在异常interruptedException

3.sleep时间达到后线程进入就绪状态

4.*每个对象都有一个一个锁,sleep不会释放锁

下面例子是Thread.sleep()的用法

package com.kuang.demo1;

//模拟网络延迟的作用:放大问题的发生性
public class TestSleep implements Runnable { //票数
private int ticketNums=10; @Override
public void run() {
while(true) {
if(ticketNums<=0) {
break;
} //模拟延时
try {
Thread.sleep(100);
}catch(InterruptedException e) {
e.printStackTrace();
} System.out.println(Thread.currentThread().getName() + "-->拿到了第" + ticketNums-- + "票");
}
} public static void main(String[] args) {
TestSleep ticket = new TestSleep(); new Thread(ticket ,"小明").start();
new Thread(ticket ,"老师").start();
new Thread(ticket ,"黄牛党").start();
}
}

看下面的运行结果,结果显示黄牛党拿到了-1张票,通过模拟线程,好处是把问题给呈现出来了

小明-->拿到了第9票
黄牛党-->拿到了第9票
老师-->拿到了第10票
老师-->拿到了第8票
小明-->拿到了第8票
黄牛党-->拿到了第8票
黄牛党-->拿到了第7票
小明-->拿到了第6票
老师-->拿到了第6票
老师-->拿到了第5票
小明-->拿到了第4票
黄牛党-->拿到了第3票
老师-->拿到了第2票
小明-->拿到了第2票
黄牛党-->拿到了第2票
老师-->拿到了第0票
小明-->拿到了第1票
黄牛党-->拿到了第-1票

模拟倒计时 主要练习tenDown()的用法

package com.kuang.demo1;

public class TestSleep1 {

	public static void main(String[] args) {
try {
tenDown();
}catch(InterruptedException e) {
e.printStackTrace();
}
} //模拟倒计时
public static void tenDown() throws InterruptedException{
int num=10;
while(true) {
Thread.sleep(1000);
System.out.println(num--);
if(num<=0) {
break;
}
}
}
}

运行结果:

10
9
8
7
6
5
4
3
2
1

模拟日常时间倒计时

在上一个的基础上添加上Data用法,和时间格式符

package com.kuang.demo1;

import java.text.SimpleDateFormat;
import java.util.Date; public class TestSleep1 { public static void main(String[] args) {
//打印当前系统时间
Date startTime = new Date(System.currentTimeMillis()); //获得系统当前时间
while(true) {
try {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime)); //输出时间格式化工厂
startTime = new Date(System.currentTimeMillis()); //更新当前时间
}catch(InterruptedException e) {
e.printStackTrace();
}
}
} //模拟时间倒计时
public static void tenDown() throws InterruptedException{
int num=10;
while(true) {
Thread.sleep(1000);
System.out.println(num--);
if(num<=0) {
break;
}
}
}
}

代码结果

16:36:18
16:36:19
16:36:20
16:36:21
16:36:22
16:36:23
16:36:24
16:36:25
16:36:26
16:36:27
16:36:28
16:36:29
16:36:30
16:36:31
16:36:32
16:36:33
16:36:34
16:36:35
16:36:36
.......
.......
....... 它会一直这样输出.

4.新建线程

是通过new操作符创建一个新线程时,如new Thread(r),这个线程还没有运行,这就意味着它的状态是新建(new).

5.线程强制执行join

当线程等待另一个线程通过调度器出现一个条件,这个线程会进入等待状态.通过Object.wait(),Thread.join()方法,或者是等待java.util.concurrent库中的Lock或Condition时会出现这种情况。

总结:

1.Join合并线程,待此线程执行完成之后,在执行其他线程,其他线程堵塞

2.可以想象成插队

代码:

package com.kuang.demo1;

//测试join方法
//想象成排队
public class TestJoin implements Runnable {
@Override
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i=0;i<10;i++) {
System.out.println("线程VIP来了" + i);
}
} public static void main(String[] args) throws InterruptedException {
//启动我们的线程
TestJoin testjoin = new TestJoin();
Thread thread = new Thread(testjoin);
thread.start(); //主线程
for(int i=0;i<50;i++){
if(i==25) {
thread.join();//插队
}
System.out.println("main" + i);
}
}
}

运行结果:

main0
main1
main2
main3
main4
main5
main6
main7
main8
main9
main10
main11
main12
main13
main14
main15
main16
main17
main18
main19
main20
main21
main22
main23
main24
线程VIP来了0
线程VIP来了1
线程VIP来了2
线程VIP来了3
线程VIP来了4
线程VIP来了5
线程VIP来了6
线程VIP来了7
线程VIP来了8
线程VIP来了9
main25
main26
main27
main28
main29
main30
main31
main32
main33
main34
main35
main36
main37
main38
main39
main40
main41
main42
main43
main44
main45
main46
main47
main48
main49

在i=25之前是CPU调度的,随机,当i>25之后就开始先执行VIP了。

六.判断线程所处的状态

package com.kuang.demo1;
//观察测试线程的状态
public class TestState { public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
for(int i=0;i<5;i++) {
try {
Thread.sleep(1000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("我爱写代码");
}); //观察状态
Thread.State state = thread.getState();
System.out.println(state); //NEW thread.start(); //启动线程
state = thread.getState();
System.out.println(state); while(state!=Thread.State.TERMINATED){
Thread.sleep(100);
state = thread.getState(); //更新线程状态
System.out.println(state); //输出状态
} }
}

运行结果:从中可以看出一个线程所处的状态一次经过new Runnable 堵塞 运行 终止 这五个状态

NEW
RUNNABLE
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
我爱写代码
TERMINATED

谈谈java中的并发(一)的更多相关文章

  1. 谈谈JAVA中的安全发布

    谈谈JAVA中的安全发布 昨天看到一篇文章阐述技术类资料的"等级",看完之后很有共鸣.再加上最近在工作中越发觉得线程安全性的重要性和难以捉摸,又掏出了<Java并发编程实战& ...

  2. 谈谈java中静态变量与静态方法在有继承关系的两个类中调用

    谈谈java中静态变量与静态方法在有继承关系的两个类中调用 学习的中如果遇到不明白或者不清楚的的时候,就是自己做些测试,自己去试试,这次我就做一个关于静态变量和静态方法在有继承关系的两个类中的问题测试 ...

  3. 谈谈java中成员变量与成员方法继承的问题

    谈谈java中成员变量与成员方法继承的问题 关于成员变量和成员方法的的继承问题,我也可以做一个小测试,来看看结果. 首先我们先创建一个父类:

  4. Java 中的并发工具类

    Java 中的并发工具类 CountDownLatch public class JoinCountDownLatchTest { public static void main(String[] a ...

  5. JAVA中关于并发的一些理解

    一,JAVA线程是如何实现的? 同步,涉及到多线程操作,那在JAVA中线程是如何实现的呢? 操作系统中讲到,线程的实现(线程模型)主要有三种方式: ①使用内核线程实现 ②使用用户线程实现 ③使用用户线 ...

  6. java编程思想-java中的并发(二)

    二.共享受限资源 有了并发就可以同时做多件事情了.但是,两个或多个线程彼此互相干涉的问题也就出现了.如果不防范这种冲突,就可能发生两个线程同时试图访问同一个银行账户,或向同一个打印机打印,改变同一个值 ...

  7. java基础(五):谈谈java中的多线程

    1.多线程 1.1.多线程介绍 学习多线程之前,我们先要了解几个关于多线程有关的概念. 进程:正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一 ...

  8. Java中的并发库学习总结

    我们都知道,在JDK1.5之前,Java中要进行业务并发时,通常需要有程序员独立完成代码实现,当然也有一些开源的框架提供了这些功能,但是这些依然没有JDK自带的功能使用起来方便.而当针对高质量Java ...

  9. 谈谈 Java 中的那些“琐”事

    一.公平锁&非公平锁 是什么 公平锁:线程按照申请锁的顺序来获取锁:在并发环境中,每个线程都会被加到等待队列中,按照 FIFO 的顺序获取锁. 非公平锁:线程不按照申请锁的顺序来获取锁:一上来 ...

随机推荐

  1. [Objective-C] 019_UIVIewController

    UIViewController是iOS程序中的一个重要组成部分,对应MVC设计模式的C,它管理着程序中的众多视图,何时加载视图,视图何时消,界面的旋转等. 1.UIViewController 创建 ...

  2. Java中的集合(十三) 实现Map接口的Hashtable

    Java中的集合(十三) 实现Map接口的Hashtable 一.Hashtable简介 和HashMap一样,Hashtable采用“拉链法”实现一个哈希表,它存储的内容是键值对(key-value ...

  3. MVC案例之新增与修改Customer

    新增Customer 添加的流程Add New Customer 超链接连接到 newcustomer.jsp新建 newcustomer.jsp: 在 CustomerServlet 的 addCu ...

  4. Druid数据库连接池的使用

    Druid  阿里提供的数据库连接池,集以上连接池优点于一身,开发使用此连接池 使用配置文件方式获取Druid数据库连接池 TestDruid package com.aff.connection; ...

  5. BZOJ1001 狼抓兔子 题解

    裸的最小割,转化成最大流即可. #include <bits/stdc++.h> int n,m; int S,T; int mincost; int head[6001000],tot= ...

  6. 透过 NestedScrollView 源码解析嵌套滑动原理

    NestedScrollView 是用于替代 ScrollView 来解决嵌套滑动过程中的滑动事件的冲突.作为开发者,你会发现很多地方会用到嵌套滑动的逻辑,比如下拉刷新页面,京东或者淘宝的各种商品页面 ...

  7. Chisel3 - 模块

    https://mp.weixin.qq.com/s/2vjM-gcauvHnn6KJzlOm4g   Chisel的模块和Verilog的模块很相似,都用来定义模块结构(hierarchical s ...

  8. 【Hadoop高级】Hadoop HA、hdfs安全模式

    Hadoop HA Safemode(安全模式) During start up the NameNode loads the file system state from the fsimage a ...

  9. SpringBoot整合Flyway(数据库版本迁移工具)

    简介 在团队开发当中,有可能每个人都是使用自己本地的数据库.当数据库的表或者字段更新时,往往需要告知团队的其他同事进行更新. Flyway数据库版本迁移工具,目的就是解决该问题而诞生的(我自己想的). ...

  10. 一招解决GitHub致命的下载速度(GitHub下载速度慢怎么办)

    通过码云来导入github,通过码云下载 第一步: 找一个你需要下载的GitHub项目 第二步: 复制链接 第三步: 打开码云,然后选择从GitHub导入 第四步: 复制刚才的连接,起个名字,点击导入 ...