文章目录

  • 多线程技术概述
    • 线程和进程
    • 线程调度
    • 同步与异步
    • 并发与并行
    • 两种创建方式
      • Thread
      • Runnable
    • 线程常用方法
      • getName()与setName()
      • sleep()
    • 线程阻塞
    • 线程中断
    • 守护线程
    • 线程安全问题
    • synchronized(线程同步)
      • 1、同步代码块
      • 2、同步方法
      • 3、显示锁(Lock)
    • 公平锁和非公平锁
    • 线程死锁
    • 多线程通信问题
    • 线程的六种状态
    • 特殊的创建方法
      • Callable和Runnable的区别
      • Runnable与Callable的相同点
      • Runnable与Callable的不同点
    • 线程池概述
      • 线程池Executors
      • 线程池的好处
      • 线程池原理图
      • Java中的四种线程池 .ExecutorService
        • 1、缓存线程池
        • 2、定长线程池
        • 3、单线程线程池
        • 4、周期性任务定长线程池
    • Lambda表达式

多线程技术概述

线程和进程

进程:

  • 是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间。并且进程互不共享内存空间,除非通过特殊手段。程序就是进程,例如:电脑的各个图标

线程:

  • 是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行,一个进程最少有一个线程。
  • 线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分成若干个线程。
  • 用到哪一个哪一个被唤醒,例如:和多人聊天 sleep 生产者和消费者

线程和进程的关系:一个进程由多个线程支撑运行,

线程调度

分时调度

  • 所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间。

抢占式调度

  • 优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调用。

  • CPU使用抢占式调度模式在多个线程间进行着高速的切换,对于CPU的一个核心而言,某个时刻,只能执行一个线程,而CPU在多个线程间切换的速度相对于我们的感觉要快,看上去就是在同一时刻运行。其实,多线程程序并不能提高程序的运行速度,但能提高程序的运行效率,让CPU的使用率更高。

同步与异步

同步:排队执行,效率低但是安全。

异步:同时执行,效率高但是数据不安全。

并发与并行

并发:指两个或多个事件在同一个时间段内发生。

并行:指两个或多个事件在同一时刻发生(同时发生)。

两种创建方式

Thread

public static void main(String[] args){MyThread m = new MyThread();m.start();for(int i=0;i<10;i++){System.out.println("汗滴禾下土"+i);}
}
class MyThread extends Thread{//run方法就是线程要执行的任务方法@Overridepublic void run(){//这里的代码 就是一条新的执行路径//这个执行路径的触发方式,不是调用run方法,而是通过thread对象的start来启动任务for(int i=0;i<10;i++){System.out.println("锄禾日当午"+i);}}
}
//结果主程序和线程的输出语句同时执行。

执行流程图如下所示:

注意:每个线程都拥有自己的栈空间,共用一份堆内存。

Runnable

public static void main(String[] args){//实现Runnable//1.创建一个任务对象MyRunnable r = new MyRunnable();//2.创建一个线程,并为其分配一个任务Thread t = new Thread(r);//3.执行这个线程t.start();for(int i=0;i<10;i++){System.out.println("疑是地上霜"+i);}
}
//这是用于给线程执行的任务
class MyRunnable implements Runnable{@Overridepublic void run(){//线程的任务for(int i=0;i<10;i++){System.out.println("床前明月光"+i);}}
}

实现Runnable与继承Thread相比有如下优势:

  1. 通过创建任务,然后给线程分配的方式来实现的多线程。更适合多个线程同时执行相同任务的情况;
  2. 可以避免单继承所带来的局限性;
  3. 任务与线程本身是分离的,提高了程序的健壮性;
  4. 线程池技术,只接受Runnable类型的任务,不接受Thread类型的线程;
public static void main(String[] args){new Thread(){//通过匿名内部类的方式创建一个线程@Overridepublic void run(){for(int i=0;i<10;i++){System.out.println("一二三四五")}}}.start();for(int i=0;i<10;i++){System.out.println("六七八九十"+i);}
}

线程常用方法

getName()与setName()

获取线程名称与设置线程名称:

public static void main(String[] args){System.out.println(Thread.currentThread().getName());//输出:mainnew Thread(new MyRunnable()).start();//输出:Thread-0new Thread(new MyRunnable()).start();//输出:Thread-1new Thread(new MyRunnable()).start();//输出:Thread-2new Thread(new MyRunnable(),"锄禾日当午").start;//输出:锄禾日当午     注意,这个方法是给线程命名Thread t = new Thread(new MyRunnable());//另一种命名的方法t.setName("锄禾日当午");t.start();
}
static class MyRunnable implements Runnable{@Overridepublic void run(){Sysmte.out.println(Thread.currentThread().getName());}
}

sleep()

线程的休眠:

public static void main(String[] args){for(int i=0;i<10;i++){System.out.println(i);Thread.sleep(1000);//每隔1秒输出一个数}
}

线程阻塞

线程阻塞不光指的是线程休眠,线程是一条执行路径,比如说:线程在执行代码时,它的执行路径有100行,从第一行到第一百行是它的整体执行路径,这100行中某10行可能是为了读取某个文件。这文件读取可能耗时1秒钟,那么这1秒钟也是阻塞的,停在那读文件,后面读完才会执行。可以把阻塞理解成所有消耗时间的操作。就像上面的读取文件,它会使线程等待在那个位置,直到读取完毕,不会往下执行,除非文件读完。就像控制台等待用户输入,用户不输入,程序就不会继续往下执行。这就是线程阻塞,我们也称其为耗时操作。

线程中断

一个线程是一个独立的执行路径,它是否应该结束,应该由其自身决定。线程启动过程中可能会使用N多资源,也涉及到N多需要释放的资源,那么这时如果由外部直接把线程掐死,那么极有可能导致线程占用的资源来不及释放而一直占用,从而产生内存垃圾,这个垃圾是你不能回收的垃圾。也有可能占用一些硬件资源,导致硬件资源来不及释放,其他软件没有办法再去使用。

线程中断不用stop方法,因为此方法是直接把线程掐死。我们应该使用interrupt方法,通过该方法给线程打上一个标记,在特殊情况下线程会检查iterrupt的状态,如果检查到就是报异常,就是告诉线程该自杀了,线程在捕获异常语句块里调用return方法就会中断线程。

public static void main(String[] args){Thread t1 = new Thread(new MyRunnable());t1.start();for(int i=1;i<=5;i++){System.out.println(Thread.currentThread().getName()+":"+i);try{Thread.sleep(1000);} catch(InterruptedException e) {e.printStackTrace();}}//给线程t1添加中断标记t1.interrupt();
}
static class MyRunnable implements Runnable{@Overridepublic void run(){for(int i=0;i<10;i++){System.out.println(Thread.currentThread().getName()+":"+i);try{Thread.sleep(1000);} catch(InterruptedException e) {System.out.println("发现了中断标记,我们这个线程自杀");return;}}}
}

守护线程

线程:分为守护线程和用户线程;

用户线程:当一个进程不包含任何存活的用户线程时,进程结束;

守护线程:守护用户线程的,当最后一个用户线程结束时,所有守护线程自动死亡。

public static void main(String[] args){Thread t1 = new Thread(new MyRunnable());t1.setDaemon(true);//设置t1为守护线程t1.start();for(int i=1;i<=5;i++){System.out.println(Thread.currentThread().getName()+":"+i);try{Thread.sleep(1000);} catch(InterruptedException e) {e.printStackTrace();}}
}
static class MyRunnable implements Runnable{@Overridepublic void run(){for(int i=0;i<10;i++){System.out.println(Thread.currentThread().getName()+":"+i);try{Thread.sleep(1000);} catch(InterruptedException e) {e.printStackTrace();}}}
}

线程安全问题

public static void main(String[] args){//线程不安全Runnable run = new Ticket();new Thread(run).start();new Thread(run).start();new Thread(run).start();
}
static class Ticket implements Runnable{private int count = 10;@Overridepublic void run(){wile(count>0){System.out.println("正在准备卖票");try{Thread.sleep(1000);} catch(InterruptedException e) {e.printStackTrace();}count--;System.out.println("出票成功,余票:"+count);}}
}//三个线程会有可能会同时进入循环,同时调用count,从而造成count<0的情况发生

synchronized(线程同步)

线程同步有三种方式,三种加锁的方式。

1、同步代码块

格式:synchronized(锁对象){ }

Java中任何对象都可以作为锁对象存在。多个线程应该抢一把锁,而不是一个线程一把锁。这样就不会出现同时操作造成数据混乱的情况了,因为都是排队执行代码块里的代码。

public static void main(String[] args){Runnable run = new Ticket();new Thread(run).start();new Thread(run).start();new Thread(run).start();
}
static class Ticket implements Runnable{private int count = 10;Object o = new Object();@Overridepublic void run(){//Object o = new Object();//注意:如果锁声明在这里,那三个线程调用run方法都会重新定义一把锁,那就还是同时执行,不是排队执行了。while(true){synchronized(o){//o作为锁对象,三个线程谁抢到了,谁执行代码块里的代码if(count>0){System.out.println("正在准备卖票");try{Thread.sleep(1000);} catch(InterruptedException e) {e.printStackTrace();}count--;System.out.println(Thread.currentThread().getName()+"出票成功,余票:"+count);} else {break;}}}}
}

2、同步方法

如果不是静态的(static)的同步方法,它的锁对象为this;如果是静态的同步方法,它的锁对象为类名.class,比如下方的例子中,如果是静态的,则锁对象为Ticket.class

public static void main(String[] args){Runnable run = new Ticket();new Thread(run).start();new Thread(run).start();new Thread(run).start();//new Thread(new Ticket()).start()//这里不是同一把锁,所以是同时执行(异步),不是排队执行(同步)了。
}
static class Ticket implements Runnable{private int count = 10;@Overridepublic void run(){//synchronized(){  }//如果这里同步代码块和同步方法同时用了一把锁,那么一个线程执行同步方法活同步代码块时,其他线程对同步方法和同步代码块都不能执行。就好比商店里的试衣间,大门和试衣间的门用的是同一把锁,那么只要有一个人(线程)在使用其中的一个试衣间,那么其他人连大门都进不去,只能等待里面的人用完试衣间把锁打开。while(true){boolean flag = sale();if(!flag){break;}}}public synchronized boolean sale(){if(count>0){System.out.println("正在准备卖票");try{Thread.sleep(1000);} catch(InterruptedException e) {e.printStackTrace();}count--;System.out.println(Thread.currentThread().getName()+"出票成功,余票:"+count);return true;} else {return false;}}
}

3、显示锁(Lock)

同步代码块和同步方法都属于隐式锁。

显示锁Lock子类ReentrantLock

public static void main(String[] args){Runnable run = new Ticket();new Thread(run).start();new Thread(run).start();new Thread(run).start();
}
static class Ticket implements Runnable{private int count = 10;private Lock l = new ReentrantLock();//显示锁@Overridepublic void run(){while(true){l.lock();if(count>0){System.out.println("正在准备卖票");try{Thread.sleep(1000);} catch(InterruptedException e) {e.printStackTrace();}count--;System.out.println(Thread.currentThread().getName()+"出票成功,余票:"+count);} else {break;}l.unlock();}}
}

公平锁和非公平锁

公平锁就是排队时谁先来,谁就先用这个锁。

非公平锁就是排队时抢着来,谁先抢到谁就用,上述Java线程同步的三种方式就是非公平锁。

在显示锁中定义时,构造函数的第一个参数设为true就是公平锁,false就是不公平锁,例如:

private Lock l = new ReentrantLock(true)//fair参数设为true,就表示公平锁

线程死锁

两个线程相互等待对方释放锁就造成了死锁。比如某商城有A、B两个试衣间,客户甲进入了A试衣间,客户乙进入了B试衣间,但是客户甲觉得A试衣间不好用,想换到B试衣间,所以客户甲在A试衣间中等客户乙用完B试衣间出来。巧的是客户已也觉得B试衣间不好用,也想换到A试衣间,所以客户乙也再等客户甲用完A试衣间出来。双方都不知道对方都在等自己用完试衣间出来,而造成的一种相互等待的状态,就叫做死锁。

public static void main(String[] args){Culprit c = new Culprit();Police p = new Police();new MyThread(c,p).start();c.say(p);//在这里主线程和MyThread线程同时执行,主线程拿到了罪犯类里的同步方法锁,只有主线程能调用罪犯类里的同步方法。而MyThread线程拿到了警察类里的同步方法的锁,只有MyThread线程能调用警察类里的同步方法。这样,主线程就需要等待MyThread线程释放所占用的锁,而MyThread线程也同样需要等待主线程释放所占用的锁,这样就造成了两线程相互等待的状态,从而形成了死锁。//如果想要两个线程不造成死锁,那么就需要在其中一个线程还没有抢占到锁时,程序就执行完毕。这样才不会造成死锁。//避免死锁的方法就是,你已经调用了一个同步方法,就不要再继续调用其他同步方法了,避免造成死锁。
}
static class MyThread extends Thread{private Culprit c;private Police p;public MyThread(Culprit c,Police p){this.c = c;this.p = p;}@Overridepublic void run(){p.say(c);}
}
//罪犯
static class Culprit{public synchronized void say(Police p){//说System.out.println("罪犯:你放了我,我放了人质");p.response();}public synchronized void response(){//回应System.out.println("罪犯被放了,罪犯也放了人质");}
}
//警察
static class Police{public synchronized void say(Culprit c){System.out.println("警察:你放了人质,我放了你");//警察说了话,需要罪犯的回应c.response();}public synchronized void response(){System.out.println("警察救到了人质,但是罪犯跑了")}
}

出现死锁的情况:

没有出现死锁的情况:

多线程通信问题

Object类里的notify()、notifyAll()、wait()方法。

生产者与消费者

就像厨师和服务员,厨师在做饭的时候,服务员是在休息的,等厨师做好了,服务员被唤醒,而厨师进入休息状态。

确保生产者在生产时消费者没有进行消费。

public static void main(String[] args){Food f = new Food();new Cook(f).start();new Waiter(f).start();
}
//厨师
static class Cook extends Thread{private Food f;public Cook(Food f){this.f = f;}@Overridepublic void run(){for(int i=0;i<100;i++){if(i%2==0){f.setNameAndTaste("老干妈小米粥","香辣味");} else {f.setNameAndTaste("煎饼果子","甜辣味");}}}
}
//服务员
static class Waiter extends Thread{private Food f;public Waiter(Food f){this.f = f;}@Overridepublic void run(){for(int i=0;i<100;i++){try{Thread.sleep(100);} catch(InterruptedException e) {e.printStackTrace();}f.get();}}
}
//食物
static class Food{private String name;private String taste;public void setNameAndTaste(String name,String taste){this.name = name;try{Thread.sleep(100);} catch(InterruptedException e){e.printStackTrace();}this.taste = taste;}public void get(){System.out.println("服务员端走的菜的名称是:"+name+",味道:"+taste);}
}

造成上述错乱的原因是厨师做食物Food的时候刚给食物赋名字name,就被服务员端走了,而味道taste有可能是上一个对象遗留的。这就是两个线程合作时出现的不协调现象。

而如果把食物的两个方法定义成同步方法,会造成更加错乱的现象,有可能,厨师做完了第一顿饭,有可能服务员还没来得及端,厨师就做完了第二顿饭,那么第一顿饭就没了。即厨师做饭做快了,或者服务员端菜端快了。

解决方法:厨师干活的时候,服务员歇着;厨师歇着的时候,服务员干活

static class Food{private String name;private String taste;private boolean flag = true;//饭菜是否做好public synchronized void setNameAndTaste(String name,String taste){if(flag){this.name = name;try{Thread.sleep(100);} catch(InterruptedException e){e.printStackTrace();}this.taste = taste;flag = false;this.notifyAll();//唤醒在当前this下睡着的所有线程try{this.wait();//厨师睡着} catch(InterruptedException e) {e.printStackTrace();}}}public synchronized void get(){if(!flag){System.out.println("服务员端走的菜的名称是:"+name+",味道:"+taste);flag = true;this.notifyAll();try{this.wait();} catch(InterruptedException e) {e.printStackTrace();}}}
}

线程的六种状态

线程状态。线程可以处于以下状态之一:

  • NEW
    尚未启动的线程处于此状态。
  • RUNNABL
    在Java虚拟机中执行的线程处于此状态。
  • BLOCKED
    被阻塞等待监视器锁定的线程处于此状态。
  • WAITING
    无限期等待另一个线程执行特定操作的线程处于此状态。
  • TIMED_WAITING
    正在等待另一个线程执行最多指定等待时间的操作的线程处于此状态。
  • TERMINATED
    已退出的线程处于此状态。

线程在给定时间点只能处于一种状态。 这些状态是虚拟机状态,不反映任何操作系统线程状态。

特殊的创建方法

Interface Callable

有两种用法:1、和主线程一起运行,和线程一样;2、主线程等待其完成

Callable和Runnable的区别

接口定义:

public interface Callable<V> {V call() throws Exception;
}
public interface Runnable {public abstract void run();
}

Callable使用步骤:

//1、编写类实现Callable接口,实现call方法
class xxx implements Callable<T> {@Overridepublic <T> call() throws Exception {return T;}
}
//2、创建FutureTask对象,并进入第一步编写的Callable对象
FutureTask<Integer> future = new FutureTask<>(Callable);
//3、通过thread启动线程
new Thread(future).start();

Runnable与Callable的相同点

  • 都是接口
  • 都可以编写多线程程序
  • 都采用Thread.start()启动线程

Runnable与Callable的不同点

  • Runnable没有返回值;Callable可以返回执行结果
  • Callable接口的call()允许抛出异常;Runnable的run()不能抛出
public static void main(String[] args){Callable<Integer> c = new MyCallable();FutureTask<Integer> task = new FutureTask<>(c);new Thread(task).start();Integer j = task.get();//调用get方法会导致主线程停在这里,等待MyCallable线程执行完毕//task.cancel(true);//取消线程System.out.println("返回值:"+j);for(int i=0;i<10;i++){try{Thread.sleep(100); } catch(InterruptedException e) {e.printStackTrace();}System.out.println(i);}
}
static class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {//Thread.sleep(3000);for(int i=0;i<10;i++){try{Thread.sleep(100); } catch(InterruptedException e) {e.printStackTrace();}System.out.println(i);}return 100;}
}

线程池概述

线程操作流程:创建线程 -> 创建任务 -> 执行任务 -> 关闭线程

其中最耗费时间的是创建线程和关闭线程。

线程池Executors

如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。线程池就是一个容纳多个线程的容器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。

线程池的好处

  • 降低资源的消耗;
  • 提高响应速度;
  • 提高线程的可管理性;

线程池原理图

Java中的四种线程池 .ExecutorService

1、缓存线程池

特点:长度无限制。

执行流程:

  1. 判断线程池是否存在空闲线程
  2. 存在则使用
  3. 不存在,则创建线程并放入线程池,然后使用
public static void main(String[] args){ExecutorService service = Executor.newCachedThreadPool();//向线程池中加入新的任务service.execute(new Runnable() {@Overridepublic void run() {System.out.println("线程的名称:"+Thread.currentThread().getName());}});service.execute(new Runnable() {@Overridepublic void run() {System.out.println("线程的名称:"+Thread.currentThread().getName());}});service.execute(new Runnable() {@Overridepublic void run() {System.out.println("线程的名称:"+Thread.currentThread().getName());}});
}
2、定长线程池

特点:长度是指定的数值

执行流程:

  1. 判断线程池是否存在空闲线程
  2. 存在则使用
  3. 不存在空闲线程,且线程未满的情况下,则创建线程,并放入线程池,然后使用
  4. 不存在空闲线程,且线程已满的情况下,则等待线程池存在空闲线程
public static void main(String[] args) {ExecutorService service = Executors.newFixedThreadPool(2);service.execute(new Runnable() {@Overridepublic void run() {System.out.println("线程的名称:"+Thread.currentThread().getName());}});service.execute(new Runnable() {@Overridepublic void run() {System.out.println("线程的名称:"+Thread.currentThread().getName());}});
}
3、单线程线程池

特点:效果与定长线程池创建时传入数值1效果一样。

执行流程:

  1. 判断线程池的那个线程是否空闲
  2. 空闲则使用
  3. 不空闲,则等待池中的单个线程空闲后使用
public static void main(String[] args){ExecutorService service = Executors.newSingleThreadExecutor();service.execute(new Runnable() {@Overridepublic void run() {System.out.println("线程的名称:"+Thread.currentThread().getName());}});service.execute(new Runnable() {@Overridepublic void run() {System.out.println("线程的名称:"+Thread.currentThread().getName());}});
}
4、周期性任务定长线程池

执行流程:

  1. 判断线程池是否存在空闲线程;
  2. 存在则使用;
  3. 不存在空闲线程,且线程池未满的情况下,则创建线程,并放入线程池,然后使用;
  4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程;

周期性任务执行时:定时执行,当某个时机触发时,自动执行某个任务

ScheduledExecutorService service = Executors.newScheduledThreadPool(2);

定时执行

参数1. runnable类型的任务

参数2. 时长数字

参数3. 时长数字的单位

service.schedule(new Runnable() {@Overridepublic void run() {System.out.println("俩人相视一笑~嘿嘿嘿");}
},5,TimeUnit.SECONDS);

周期执行

参数1. runnable类型的任务

参数2. 时长数字(延迟执行的时长)

参数3. 周期时长(每次执行的间隔时间)

参数4. 时长数字的单位

service.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {System.out.println("俩人相识一笑~嘿嘿嘿");}
},5,1,TimeUnit.SECONDS);

Lambda表达式

函数式编程思想

面向对象:创建对象调用方法解决问题。

Lambda表达式关注的是对象的方法。

//冗余的Runnable代码
public static void main(String[] args) {MyRunnable r = new MyRunnable();Thread t = new Thread(r);t.start();
}
public static class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("锄禾日当午");}
}
//改善之后的Runnable代码
public static void main(String[] args) {Thread t = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("锄禾日当午");}});t.start();
}
//使用Lambda表达式改善Runnable代码
public static void main(String[] args) {Thread t = new Thread(() -> System.out.println("锄禾日当午"));//效果和上述代码一致//Thread t = new Thread(() -> {System.out.println("锄禾日当午");});t.start();
}//自定义接口测试
public static void main(String[] args) {/*print(new MyMath() {@Overridepublic int sum(int x,int y) {return x+y;}},100,200);*/print((int x,int y) -> {return x+y;},100,200);
}
public static void print(MyMath m,int x,int y){int num = m.sum(x,y);System.out.println(num);
}
static interface MyMath {int sum(int x,int y);
}

Java多线程技术概述(知识点整理)相关推荐

  1. Java多线程技术解析

    Java多线程技术 1.多线程的概述 1.1.进程与线程 进程是操作系统进行资源分配和调度的一个独立单位,它是一个内存中运行的应用程序的载体,每个进程都有一个独立的内存空间.进程一般由程序,数据集合和 ...

  2. 利用java多线程技术和图像显示技术来完成动画设计。

    利用java多线程技术和图像显示技术来完成动画设计. package p2;import java.applet.Applet; import java.awt.Graphics; import ja ...

  3. Java多线程技术~生产者和消费者问题

    Java多线程技术~生产者和消费者问题 本文是上一篇文章的后续,详情点击该连接 线程通信 应用场景:生产者和消费者问题 假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取 ...

  4. 高级JAVA开发 技术栈知识点总结

    高级JAVA开发 技术栈知识点总结 写在前面 MQ Redis Dubbo 分布式系统 JVM Java基础 写在前面 "金三银四"对于今年(2019)的互联网行业行情并不适用,面 ...

  5. JAVA物联所需技术_基于JAVA多线程技术解决物联云端服务雪崩效应的方法与流程...

    本发明涉及互联网技术领域,特别涉及一种基于JAVA多线程技术解决物联云端服务雪崩效应的方法. 背景技术: 目前,物联云系统已经作为普遍的智能电视平台出现在我们面前,而细致分析物联云系统我们可以发现,当 ...

  6. 百度 java基础_java基础知识点整理

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 java基础知识点整理1.&和&&的区别? &:逻辑与(and),运算符两边的表达式均为true时,整个结果才为true. ...

  7. 软件工程——第1章软件工程学概述知识点整理

    本专栏是博主个人笔记,主要目的是利用碎片化的时间来记忆软工知识点,特此声明! 文章目录 1.为什么要有软件工程这门学科? 2.软件分为哪些阶段? 3.软件危机的定义? 4.软件危机包含的问题有哪些? ...

  8. Java反射技术概述

    什么是Java反射 就是正在运行,动态获取这个类的所有信息. 反射机制的作用 1,反编译:.class-->.java 2.通过反射机制访问java对象的属性,方法,构造方法等: 反射机制的应用 ...

  9. Java多线程系列(一):最全面的Java多线程学习概述

    Java并发编程的技能基本涵括以下5方面: 多线程 线程池 线程锁 并发工具类 并发容器 多线程的4种创建方式 继承Thread 实现Runnable接口 实现Callable接口 以及线程池来创建线 ...

最新文章

  1. 想知道垃圾回收暂停的过程中发生了什么吗?查查垃圾回收日志就知道了!
  2. Spring 事务失效?看这篇文章就够了!
  3. Linux定时器使用
  4. 演讲实录 | DevOps五大理念及其落地实践
  5. python xlrd读取excel-使用Python xlrd模块读取Excel格式文件的方法
  6. android edittext不可复制_精选Android中高级面试题:性能优化,JNI,设计模式
  7. [BZOJ 2957]楼房重建(线段树)
  8. java oop入门_Java OOP入门起源
  9. 云世界,一切如梦幻,数据也玩虚拟化。 SQL Server 2019 新特性系列一:数据虚拟化
  10. VSCode调试Python时终端输出中文乱码解决方法1
  11. windows浏览器访问虚拟机开的rabbitmq服务,无法访问
  12. 下:比拼生态和未来,Spark和Flink哪家强?
  13. 开源:OpenJDK8 MIPS64(龙芯)
  14. 阿里云原生专家复礼:多活容灾建设思路与经验分享
  15. php如何隐藏入口文件,php怎么隐藏入口文件
  16. windows10没有nfs服务_3GB+极度精简+不更新,被誉为最纯净Windows10,老爷机总算有救了...
  17. Window API 第五篇 WTSEnumerateProcesses
  18. 虚拟机中Linux扩容硬盘空间
  19. Python----操作MySql数据库2
  20. 09 数据存储Introduce

热门文章

  1. 8月8日晚8点,Cocos想和你约个会
  2. 什么是服务器虚拟化?
  3. 做ppt,字体图标音效动画,资源地址收藏
  4. http://www.cnedu.cn/news/2006/9/li6120475431159600217328.html
  5. 关于vio和ila的区别
  6. LaTeX中表格默认在页面中置顶怎么取消?
  7. 女生,能为程序员男友做点什么吗?
  8. QC协议+华为FCP+三星AFC快充取电5V9V芯片FS2601应用
  9. 移动端页面滚动后不触发touchend事件
  10. 来了!GitHub for mobile 发布!iOS beta 版已来,Android 版即将发布