java 多线程 总结_Java 多线程总结

昨天熬了个通宵,看了一晚上的视频,把java 的多线程相关技术重新复习了一遍,下面对学习过程中遇到的知识点进行下总结。

首先我们先来了解一下进程、线程、并发执行的概念:

进程是指:一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程。比如在Windows系统中,一个运行的exe就是一个进程。

线程是指:进程中的一个执行流程,一个进程中可以运行多个线程。比如java.exe进程中可以运行很多线程。线程总是属于某个进程,进程中的多个线程共享进程的内存。

一般来说,当运行一个应用程序的时候,就启动了一个进程,当然有些会启动多个进程。启动进程的时候,操作系统会为进程分配资源,其中最主要的资源是内存空间,因为程序是在内存中运行的。

在进程中,有些程序流程块是可以乱序执行的,并且这个代码块可以同时被多次执行。实际上,这样的代码块就是线程体。线程是进程中乱序执行的代码流程。当多个线程同时运行的时候,这样的执行模式成为并发执行。

线程的状态

1、线程共有下面4种状态:

新建状态(New):新创建了一个线程对象,当你用new创建一个线程时,该线程尚未运行。

就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。

运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。

阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:

a. 等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。

b. 同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM把该线程放入锁。

c. 其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

死亡状态(Dead):

a.  由于run方法的正常退出而自然死亡;

b.  没有捕获到的异常事件终止了run方法的执行,从而导致线程突然死亡

2、若要确定某个线程当前是否活着,可以使用isAlive方法。

如果该线程是可运行线程或者被中断线程,那么该方法返回true;如果该线程仍然是个新建线程,或者该线程是个死线程,那么该方法返回false

3、注意:你无法确定一个活线程究竟是处于可运行状态还是被中断状态,也无法确定一个可运行线程是否正处在运行之中。另外,你也无法对尚未成为可运行的线程与已经死掉的线程进行区分。

4、线程必须退出中断状态,并且返回到可运行状态,方法是使用与进入中断状态相反的过程:

a.如果线程已经处于睡眠状态,就必须经过规定的毫秒数

b.如果线程正在等待输入或输出操作完成,那么必须等待该操作完成

c.如果线程调用了wait方法,那么另外一个线程必须调用notifyAll或者notify方法

d.如果线程正在等待另一个线程拥有的对象锁,那么另一个线程必须放弃该锁的所有权

5、下面这副图很好的反映了线程在不同情况下的状态变化。

8f94ee885bc9b67bcca0b07a30246ef6.png

了解完多线程的相关知识,下面来介绍一下在java中多线程的实现方式

JAVA多线程实现方式

JAVA多线程实现方式主要有以下三种:

1、继承Thread类

2、实现Runnable接口

3、使用ExecutorService、Callable、Future实现有返回结果的多线程。

其中前两种方式线程执行完后都没有返回值,只有最后一种是带返回值的。其中最常用的也是前两种实现方式。下面对前两种实现方式分别做下讲解。

1、继承Thread类实现多线程

继承Thread类的方法尽管被我列为一种多线程实现方式,但Thread本质上也是实现了Runnable接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。

例如:

package thread;

public class MyThread extends Thread {

public void run() {

System.out.println("run()方法正在执行");

}

}

启动线程方式如下:

MyThread myThread1 = new MyThread();

MyThread myThread2 = new MyThread();

myThread1.start();

myThread2.start();

2、实现Runnable接口方式实现多线程

如果自己的类已经extends另一个类,就无法直接extends Thread,此时,必须实现一个Runnable接口。

方法如下:

package thread;

class OtherClass{

public void print(String str){

System.out.println(str);

}

}

public class MyThread extends OtherClass implements Runnable {

public void run() {

System.out.println("run()正在执行");

}

}

为了启动MyThread,需要首先实例化一个Thread,并传入自己的MyThread实例。

具体方法如下:

MyThread myThread = new MyThread();

Thread thread = new Thread(myThread);

thread.start();

事实上,当传入一个Runnable target参数给Thread后,Thread的run()方法就会调用target.run(),参考JDK源代码:

public void run() {

if (target != null) {

target.run();

}

}

学会了线程的创建方式,下面我们在举几个线程状态转换的例子

3、线程状态的转换实例

package thread;

public class ThreadStateDemo extends Thread {

Thread thread;

public ThreadStateDemo() {

thread = new Thread(this);

System.out.println("创建一个线程:thread");

thread.start();

}

public void run() {

try {

System.out.println("线程thread正在运行!");

System.out.println("线程thread睡眠3秒中...!");

Thread.sleep(3000); //静态方法,使当前正在执行的线程睡眠3秒

System.out.println("线程thread在睡眠后重新运行!");

}catch(InterruptedException e) {

System.out.println("线程被中断");

}

}

public static void main(String[] args) {

new ThreadStateDemo();

System.out.println("主线程main结束!");

}

}

【运行结果】如下:

创建一个线程:thread

主线程main结束!

线程thread正在运行!

线程thread睡眠3秒中...!

线程thread在睡眠后重新运行!

终止线程的实例:

package thread;

public class ThreadShutDownDemo {

public static void main(String args[]) {

Runner runner = new Runner();

Thread thread = new Thread(runner);

thread.start();

for(int i=0;i<10;i++) {

if(i%10!=0) {

System.out.println("在主线程中 i=" + i);

}

}

System.out.println("主线程main结束");

//通知线程结束

runner.shutDown();

}

}

class Runner implements Runnable {

//控制线程是否结束

private boolean flag = true;

public void run() {

int i=0;

while(flag == true) {

System.out.println("在子线程中 i=" + i++);

}

System.out.println("子线程结束");

}

//设置线程结束标志

public void shutDown() {

flag = false;

}

}

【运行结果】如下:

在主线程中 i=1

在子线程中 i=0

在主线程中 i=2

在子线程中 i=1

在主线程中 i=3

在子线程中 i=2

在主线程中 i=4

在子线程中 i=3

在主线程中 i=5

在子线程中 i=4

在主线程中 i=6

在主线程中 i=7

在主线程中 i=8

在主线程中 i=9

主线程main结束

在子线程中 i=5

子线程结束

join()方法实例:

package thread;

public class TheadJoinDemo {

public static void main(String[] args) {

Runner2 r = new Runner2();

Thread t = new Thread(r);

t.start();

try {

t.join();//主线程main将中断,直到线程t执行完毕

}catch(InterruptedException e) {

}

for(int i=0;i<5;i++) {

System.out.println("主线程:" + i);

}

}

}

class Runner2 implements Runnable {

public void run() {

for(int i=0;i<10;i++) {

System.out.println("子线程:" + i);

}

}

}

【运行结果】如下:

子线程:0

子线程:1

子线程:2

子线程:3

子线程:4

子线程:5

子线程:6

子线程:7

子线程:8

子线程:9

主线程:0

主线程:1

主线程:2

主线程:3

主线程:4

介绍完以上几个实例,我们下面对sleep()、wait()、yeid()、join()几个方法进行下区别总结

sleep方法与wait方法的区别:

sleep方法是静态方法,wait方法是非静态方法。

sleep方法在时间到后会自己“醒来”,但wait不能,必须由其它线程通过notify(All)方法让它“醒来”。

sleep方法通常用在不需要等待资源情况下的阻塞,像等待线程、数据库连接的情况一般用wait。

sleep/wait与yeld方法的区别:

调用sleep或wait方法后,线程即进入block状态,而调用yeld方法后,线程进入runnable状态。

wait与join方法的区别:

wait方法体现了线程之间的互斥关系,而join方法体现了线程之间的同步关系。

wait方法必须由其它线程来解锁,而join方法不需要,只要被等待线程执行完毕,当前线程自动变为就绪。

join方法的一个用途就是让子线程在完成业务逻辑执行之前,主线程一直等待直到所有子线程执行完毕。

线程的同步问题

在实际应用中,我们通常会遇到多线程安全问题。多线程安全问题:当多条语句在操作同一线程共享数据是,一个线程对多条语句只执行了一部分,还没有执行完, 此时另一个线程参与进来执行,导致共享数据的错误。

解决办法:

对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。

Java 对于多线程的安全提供了专业的解决方式。

线程的同步是保证多线程安全访问竞争资源的一种手段,对于同步,在具体的Java代码中需要完成一下两个操作:

把竞争访问的资源标识为private;

同步哪些修改变量的代码,使用synchronized关键字同步方法或代码。

synchronized(对象){

代码块

...

}

同步的前提:

1、必须要有两个或者两个以上的线程运行;

2、必须是多个线程使用同一个锁;

好处:解决了多线程的安全问题;

弊端:多个线程需要判断锁,较为消耗资源;

注意:非静态同步函数的对象锁为this,静态同步函数所使用的锁是该方法所在类的字节码文件对象,即类名.class,静态方法里的同步锁都是使用的是类的字节码对象。

//静态同步函数锁

public static synchronized void show(){

ticket++;

System.out.println(Thread.currentThread().getName()+"runtime..."+ticket--);

}

下面来例举一个线程同步的例子:(同步方法)

package thread;

public class SynchronizedThread {

public static void main(String[] args) {

User u = new User("王某", 100);

MyThread2 t1 = new MyThread2("线程A", u, 10);

MyThread2 t2 = new MyThread2("线程B", u, -50);

MyThread2 t3 = new MyThread2("线程C", u, -60);

MyThread2 t4 = new MyThread2("线程D", u, -40);

MyThread2 t5 = new MyThread2("线程E", u, 20);

MyThread2 t6 = new MyThread2("线程F", u, 28);

t1.start();

t2.start();

t3.start();

t4.start();

t5.start();

t6.start();

}

}

class MyThread2 extends Thread {

private User u;

private int y = 0;

MyThread2(String name, User u, int y) {

super(name);

this.u = u;

this.y = y;

}

public void run() {

u.oper(y);

}

}

class User {

private String code;

private int cash;

User(String code, int cash) {

this.code = code;

this.cash = cash;

}

public String getCode() {

return code;

}

public void setCode(String code) {

this.code = code;

}

/**

* 业务方法

* @param x 添加x万元

*/

public synchronized void oper(int x) {

try {

Thread.sleep(10L);

this.cash += x;

System.out.println(Thread.currentThread().getName() + "运行结束,增加“" + x + "”,当前用户账户余额为:" + cash);

Thread.sleep(10L);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

@Override

public String toString() {

return "User{" +

"code='" + code + '\'' +

", cash=" + cash +

'}';

}

}

【运行结果】如下:

线程A运行结束,增加“10”,当前用户账户余额为:110

线程F运行结束,增加“28”,当前用户账户余额为:138

线程E运行结束,增加“20”,当前用户账户余额为:158

线程D运行结束,增加“-40”,当前用户账户余额为:118

线程C运行结束,增加“-60”,当前用户账户余额为:58

线程B运行结束,增加“-50”,当前用户账户余额为:8

下面是线程不同步的情况,也就是去掉oper(int x)方法的synchronized修饰符,然后再运行程序

【运行结果】如下:

线程F运行结束,增加“28”,当前用户账户余额为:128

线程D运行结束,增加“-40”,当前用户账户余额为:88

线程B运行结束,增加“-50”,当前用户账户余额为:38

线程E运行结束,增加“20”,当前用户账户余额为:58

线程C运行结束,增加“-60”,当前用户账户余额为:-2

线程A运行结束,增加“10”,当前用户账户余额为:8

很显然,上面的结果是错误的,导致错误的原因是多个线程并发访问了竞争资源u,并对u的属性做了改动。

注意:当去掉synchronized修饰符后,线程不在同步,每次运行的结果将都不一样,可见同步的重要性。

再把以上实例改为同步代码块方式

对于同步,除了同步方法外,还可以使用同步代码块,有时候同步代码块会带来比同步方法更好的效果。

追其同步的根本的目的,是控制竞争资源的正确的访问,因此只要在访问竞争资源的时候保证同一时刻只能一个线程访问即可,因此Java引入了同步代码快的策略,以提高性能。

在上个例子的基础上,对oper方法做了改动,由同步方法改为同步代码块模式。代码如下:

package thread;

/**

* 同步代码块

* @author Chu

*

*/

public class SynchronizedThread2 {

public static void main(String[] args) {

User u = new User("张三", 100);

MyThread3 t1 = new MyThread3("线程A", u, 10);

MyThread3 t2 = new MyThread3("线程B", u, -50);

MyThread3 t3 = new MyThread3("线程C", u, -60);

MyThread3 t4 = new MyThread3("线程D", u, -40);

MyThread3 t5 = new MyThread3("线程E", u, 20);

MyThread3 t6 = new MyThread3("线程F", u, 28);

t1.start();

t2.start();

t3.start();

t4.start();

t5.start();

t6.start();

}

}

class MyThread3 extends Thread {

private User u;

private int y = 0;

MyThread3(String name, User u, int y) {

super(name);

this.u = u;

this.y = y;

}

public void run() {

u.oper(y);

}

}

class User2 {

private String code;

private int cash;

User2(String code, int cash) {

this.code = code;

this.cash = cash;

}

public String getCode() {

return code;

}

public void setCode(String code) {

this.code = code;

}

/**

* 业务方法

* @param x 添加x万元

*/

public void oper(int x) {

try {

Thread.sleep(10L);

synchronized (this) {

this.cash += x;

System.out.println(Thread.currentThread().getName() + "运行结束,增加“" + x + "”,当前用户账户余额为:" + cash);

}

Thread.sleep(10L);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

@Override

public String toString() {

return "User{" +

"code='" + code + '\'' +

", cash=" + cash +

'}';

}

}

【运行结果】如下:

线程A运行结束,增加“10”,当前用户账户余额为:110

线程F运行结束,增加“28”,当前用户账户余额为:138

线程D运行结束,增加“-40”,当前用户账户余额为:98

线程E运行结束,增加“20”,当前用户账户余额为:118

线程C运行结束,增加“-60”,当前用户账户余额为:58

线程B运行结束,增加“-50”,当前用户账户余额为:8

用到线程的同步,随之可能会带来死锁问题。

导致死锁的原因:两个线程互相等待竞争资源,导致两边都无法得到资源,而使自己无法运行。

下面例举一个导致死锁的一个实例,代码如下:

package thread;

class Demo1{

static Object obj1=new Object();

static Object obj2=new Object();

}

class Demo2 implements Runnable{

boolean flag;

Demo2(boolean flag){

this.flag=flag;

}

@Override

public void run(){

if(flag){

while(true){

synchronized(Demo1.obj1){

System.out.println("1");

synchronized(Demo1.obj2){

System.out.println("2");

}

}

}

}

else{

while(true){

synchronized(Demo1.obj2){

System.out.println("2");

synchronized(Demo1.obj1){

System.out.println("1");

}

}

}

}

}

}

最后我再说说:生产者消费者的问题

对于多线程程序来说,不管任何编程语言,生产者和消费者模型都是最经典的。

实际上,准确说应该是“生产者-消费者-仓储”模型,离开了仓储,生产者消费者模型就显得没有说服力了。

对于此模型,应该明确一下几点:

1、生产者仅仅在仓储未满时候生产,仓满则停止生产;

2、消费者仅仅在仓储有产品时候才能消费,仓空则等待;

3、当消费者发现仓储没产品可消费时候会通知生产者生产;

4、生产者在生产出可消费产品时候,应该通知等待的消费者去消费。

此模型将要结合java.lang.Object的wait与notify、notifyAll方法来实现以上的需求。这是非常重要的。

具体实现代码如下:

package thread;

/**

* Java线程:生产者消费者模型

* @author Chu 2013-06-15 05:32:29

*/

public class ProductTest {

public static void main(String[] args) {

Godown godown = new Godown(20);

Consumer c1 = new Consumer(80, godown);

Consumer c2 = new Consumer(30, godown);

Consumer c3 = new Consumer(20, godown);

Producer p1 = new Producer(5, godown);

Producer p2 = new Producer(5, godown);

Producer p3 = new Producer(5, godown);

Producer p4 = new Producer(10, godown);

Producer p5 = new Producer(20, godown);

Producer p6 = new Producer(35, godown);

Producer p7 = new Producer(50, godown);

c1.start();

c2.start();

c3.start();

p1.start();

p2.start();

p3.start();

p4.start();

p5.start();

p6.start();

p7.start();

}

}

/** 仓库 */

class Godown {

public static final int max_size = 100; //最大库存量

public int curnum; //当前库存量

Godown() {

}

Godown(int curnum) {

this.curnum = curnum;

}

/**

* 生产指定数量的产品

* @param neednum

*/

public synchronized void produce(int neednum) {

//测试是否需要生产

while (neednum + curnum > max_size) {

System.out.println("要生产的产品数量" + neednum + "超过剩余库存量" + (max_size - curnum) + ",暂时不能执行生产任务!");

try {

//当前的生产线程等待

wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

//满足生产条件,则进行生产,这里简单的更改当前库存量

curnum += neednum;

System.out.println("已经生产了" + neednum + "个产品,现仓储量为" + curnum);

//唤醒在此对象监视器上等待的所有线程

notifyAll();

}

/**

* 消费指定数量的产品

* @param neednum

*/

public synchronized void consume(int neednum) {

//测试是否可消费

while (curnum < neednum) {

try {

//当前的生产线程等待

wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

//满足消费条件,则进行消费,这里简单的更改当前库存量

curnum -= neednum;

System.out.println("已经消费了" + neednum + "个产品,现仓储量为" + curnum);

//唤醒在此对象监视器上等待的所有线程

notifyAll();

}

}

/** 生产者 */

class Producer extends Thread {

//生产产品的数量

private int neednum;

//仓库

private Godown godown;

Producer(int neednum, Godown godown) {

this.neednum = neednum;

this.godown = godown;

}

public void run() {

//生产指定数量的产品

godown.produce(neednum);

}

}

/** 消费者 */

class Consumer extends Thread {

//生产产品的数量

private int neednum;

//仓库

private Godown godown;

Consumer(int neednum, Godown godown) {

this.neednum = neednum;

this.godown = godown;

}

public void run() {

//消费指定数量的产品

godown.consume(neednum);

}

}

【运行结果】如下:

已经消费了20个产品,现仓储量为0

已经生产了5个产品,现仓储量为5

已经生产了5个产品,现仓储量为10

已经生产了5个产品,现仓储量为15

已经生产了20个产品,现仓储量为35

已经生产了50个产品,现仓储量为85

已经消费了80个产品,现仓储量为5

已经生产了10个产品,现仓储量为15

已经生产了35个产品,现仓储量为50

已经消费了30个产品,现仓储量为20

说明:

对于本例,要说明的是当发现不能满足生产或者消费条件的时候,调用对象的wait方法,wait方法的作用是释放当前线程的所获得的锁,并调用对象的notifyAll() 方法,通知(唤醒)该对象上其他等待线程,使得其继续执行。这样,整个生产者、消费者线程得以正确的协作执行。

notifyAll() 方法,起到的是一个通知作用,不释放锁,也不获取锁。只是告诉该对象上等待的线程可以竞争执行了。

以上这个例子仅仅是生产者消费者模型中最简单的一种表示,在这个例子中,如果消费者消费的仓储量达不到满足,而又没有生产者,则程序会一直处于等待状态,这当然是不对的。实际上可以将此例进行修改,修改为,根据消费驱动生产,同时生产兼顾仓库,如果仓不满就生产,并对每次最大消费量做个限制,这样就不存在此问题了,当然这样的例子更复杂,更难以说明这样一个简单模型。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/346418.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基本服务-使用大使网关

这是我对Knative服务进行实验的延续&#xff0c;这次是围绕在Knative服务应用程序之上构建网关。 这是基于我之前的两篇文章- 使用Knative部署Spring Boot App以及在Knative中进行服务到服务的调用 。 为什么在Knative应用程序之上使用网关 为了解释这一点&#xff0c;让我谈谈…

mysql function 表名作为参数_mysql 常用的分组聚合函数

mysql 常用的分组聚合函数1.聚合运算一般情况下,需要的聚合数据(和,平均值,最大,最小等)并不总是存储在表中,但是可以执行存储数据的计算来获取它.根据定义,聚合函数对一组值执行计算并返回单个值.MySQL提供了许多聚合函数,包括AVG,COUNT,SUM,MIN,MAX等.除COUNT函数外,其它聚合…

java加减乘除运算顺序_java 实现加减乘除混合运算

初衷&#xff1a;解决小学一年级到四年级 基本加减乘除混合运算基本思路&#xff1a;表达式&#xff1a;10032-200(10000/5(100/2))此类表达式我们称之为中缀表达式(运算符在数字的中间),如果我们稍加转换&#xff0c;转化为100, 3, , 2, , 200, -, 10000, 5, /, 100, 2, /, , …

java integer valueof_对 Java Integer.valueOf() 的一些了解

从一道选择题开始分析选项A选项A中比较的是i01和i02&#xff0c;Integer i0159这里涉及到自动装箱过程&#xff0c;59是整型常量&#xff0c;经包装使其产生一个引用并存在栈中指向这个整型常量所占的内存&#xff0c;这时i01就是Integer 的引用。而int i0259由于int是基本类型…

java web 伪静态_【Java Web】使用URLRewrite实现网站伪静态

大部分搜索引擎都会优先考虑收录静态的HTML页面&#xff0c;而不是动态的*.jsp、*.php页面。但实际上绝大部分网站都是动态的&#xff0c;不可能全部是静态的HTML页面&#xff0c;因此互联网上大部分网站都会考虑伪静态——就是将*.jsp、*.php这种动态URL伪装成静态的HTML页面。…

使用Spring WebFlux从Corda节点流式传输数据

自上次发布以来已经有一段时间了&#xff0c;但我终于回来了&#xff01; 由于我仍在我的项目中&#xff0c;因此我将再次撰写有关使用Corda的文章。 这次&#xff0c;我们将不再关注Corda&#xff0c;而是将Spring与Corda结合使用。 更具体地说&#xff0c;Spring WebFlux。 为…

mysql 批量加索引_mysql优化:按期删数据 + 批量insert + 字符串加索引为何很傻

嗯&#xff0c;犯了一个很低级的错误&#xff0c;最近暴露出来了。html背景&#xff1a;mysql1. 内部平台&#xff0c;接口间断性无返回&#xff0c;查询日志注意到失败时&#xff0c;接口耗时达到4000(正常状态&#xff1a;100ms)git2. 增长日志打点&#xff0c;在关键步骤插入…

MySQL中引入存储引擎意义是_mysql学习九:存储引擎、存储过程和函数的引入

存储引擎&#xff1a;存储引擎是mysql特有的&#xff0c;共有7种&#xff0c;常用的有myisam、memory、innodb查看表的存储引擎&#xff1a;show create table 表名;修改表的存储引擎&#xff1a;alter table 表名 engine存储引擎名称;1.myisam存储引擎&#xff1a;可转换为压缩…

python土味情话_土味情话表情包下载

喵星人土味情话表情包是一款很甜的表情图片&#xff0c;现在的聊天模式三句话离不开表情包&#xff0c;而且小编带来的这款表情包非常的适合情侣日常撩&#xff0c;最新的土味情话&#xff0c;需要的朋友可以前来本站下载。土味情话大全一、“对不起。”“你永远都不要和我说对…

多云互操作性!=云服务聚合

多云定义为一种方法&#xff0c;它将来自多个云供应商的多个云&#xff08;公共云或私有云&#xff09;组合在一起。 但是&#xff0c;这不是来自不同供应商的各种服务的集合&#xff0c;它需要一种强制性的胶合剂–云不可知的方法&#xff0c;并在所有提供商之间实现互操作性。…

如何在Spring中将@RequestParam绑定到对象

您是否在请求映射方法中用RequestParam注释了多个参数&#xff0c;并认为它不可读&#xff1f; 当请求中需要一个或两个输入参数时&#xff0c;注释看起来非常简单&#xff0c;但是当列表变长时&#xff0c;您可能会感到不知所措。 您不能在对象内部使用RequestParam批注&…

webstorm前端调用后端接口_软件测试面试题:怎么去判断一个bug是前端问题还是后端问题...

大家好&#xff0c;在软件测试面试过程中&#xff0c;经常有面试官问到这个问题&#xff0c;那我们应该如何回答才好呢&#xff1f;少废话&#xff0c;直接看答案&#xff1a;答案&#xff1a;在页面上发现bug之后&#xff0c;要想判断这个问题属于后端还是前端&#xff0c;我就…

首次适应算法_CVPR 2020丨?商汤TSD目标检测算法获得Open Images冠军

编者按&#xff1a;此前&#xff0c;在文章《商汤科技57篇论文入选ICCV 2019&#xff0c;13项竞赛夺冠》里&#xff0c;商汤君报道了商汤科技荣获Open Images Object Detection Challenge 2019 冠军。由Google AI主办的Open Images大赛是目前通用物体检测和实例分割两个领域中数…

玩JDK 12的Switch表达式

在博客文章“操作中的JDK语言功能预览&#xff1a;切换表达式 ”中&#xff0c;我讨论了JEP 325 [“切换表达式&#xff08; 预览 &#xff09;”&#xff09;如何作为指定的“ 预览语言功能 ”的早期应用&#xff0c;如JEP 12所述。预览语言和VM功能”]。 JEP 325 适用于JDK 1…

python 验证码_4行Python代码生成图像验证码

点击上方蓝色字体&#xff0c;关注我们最近无意看到网上有人使用Python编写几十行代码生成图像验证码&#xff0c;感觉很是繁琐&#xff0c;这里为各位朋友推荐两种方法&#xff0c;使用4行Python代码即可生成验证码。1captcha库第1步&#xff1a;安装captcha库pip install cap…

python3 多线程_Python3多线程爬虫实例讲解

多线程概述多线程使得程序内部可以分出多个线程来做多件事情&#xff0c;充分利用CPU空闲时间&#xff0c;提升处理效率。python提供了两个模块来实现多线程thread 和threading &#xff0c;thread 有一些缺点&#xff0c;在threading 得到了弥补。并且在Python3中废弃了thread…

java多条件组合查询6_elasticsearch组合多条件查询实现restful api以及java代码实现

elasticsearch组合多条件查询实现restful api以及java代码实现实际开发中&#xff0c;基本都是组合多条件查询。elasticsearch提供bool来实现这种需求&#xff1b;主要参数&#xff1a;must文档 必须 匹配这些条件才能被包含进来。must_not文档 必须不 匹配这些条件才能被包含进…

基于java家教管理系统_基于jsp的家教信息管理-JavaEE实现家教信息管理 - java项目源码...

基于jspservletpojomysql实现一个javaee/javaweb的家教信息管理, 该项目可用各类java课程设计大作业中, 家教信息管理的系统架构分为前后台两部分, 最终实现在线上进行家教信息管理各项功能,实现了诸如用户管理, 登录注册, 权限管理等功能, 并实现对各类家教信息管理相关的实体…

如何从云功能调用外部REST API

在之前的博客文章中&#xff0c;我展示了如何创建您的第一个云功能 &#xff08;以及一个视频 &#xff09;。 您的云函数很可能需要调用外部REST API。 以下教程将向您展示如何创建此类功能&#xff08;非常简单&#xff09;。 登录到IBM Cloud帐户 点击目录 删除标签&…

机器学习分类算法_机器学习分类算法

人们曾在自身的神经元得到启发&#xff0c;将机器学习中给出的特征输入与权重之积作为输出与阈值作比较&#xff0c;得到0或者1的输出。这就是我们感知器的实现原理感知器在实现过程中的步骤如下&#xff1a;①将权值初始化称为一个很小的向量②迭代训练所有样本&#xff1a;计…