从操作系统的角度讲,os会维护一个ready queue(就绪的线程队列)。并且在某一时刻cpu只为ready queue中位于队列头部的线程服务。
但是当前正在被服务的线程可能觉得cpu的服务质量不够好,于是提前退出,这就是yield。
或者当前正在被服务的线程需要睡一会,醒来后继续被服务,这就是sleep。
sleep方法不推荐使用,可用wait。
线程退出最好自己实现,在运行状态中一直检验一个状态,如果这个状态为真,就一直运行,如果外界更改了这个状态变量,那么线程就停止运行。
sleep()使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会执行;yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
sleep()可使优先级低的线程得到执行的机会,当然也可以让同优先级和高优先级的线程有执行的机会;yield()只能使同优先级的线程有执行的机会。
当调用wait()后,线程会释放掉它所占有的“锁标志”,从而使线程所在对象中的其它synchronized数据可被别的线程使用。
waite()和notify()因为会对对象的“锁标志”进行操作,所以它们必须在 synchronized函数或synchronized block中进行调用。如果在non-synchronized函数或non- synchronized block中进行调用,虽然能编译通过,但在运行时会发生IllegalMonitorStateException的异常。

线程的状态
线程有四种状态,任何一个线程肯定处于这四种状态中的一种:
1)    产生(New):线程对象已经产生,但尚未被启动,所以无法执行。如通过new产生了一个线程对象后没对它调用start()函数之前。
2)    可执行(Runnable):每个支持多线程的系统都有一个排程器,排程器会从线程池中选择一个线程并启动它。当一个线程处于可执行状态时,表示它可能正处于线程池中等待排排程器启动它;也可能它已正在执行。如执行了一个线程对象的start()方法后,线程就处于可执行状态,但显而易见的是此时线程不一定正在执行中。
3)    死亡(Dead):当一个线程正常结束,它便处于死亡状态。如一个线程的run()函数执行完毕后线程就进入死亡状态。
4)    停滞(Blocked):当一个线程处于停滞状态时,系统排程器就会忽略它,不对它进行排程。当处于停滞状态的线程重新回到可执行状态时,它有可能重新执行。如通过对一个线程调用wait()函数后,线程就进入停滞状态,只有当两次对该线程调用notify或notifyAll后它才能两次回到可执行状态。

用法
sleep()
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。

由于sleep()方法是Thread类的方法,因此它不能改变对象的机锁。所以当在一个Synchronized方法中调用sleep()时,线程虽然休眠了,但是对象的机锁没有被释放,其他线程仍然无法访问这个对象。sleep()方法不需要在同步的代码块中执行。但是sleep()可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException。

wait()和notify()和notifyAll()
wait()方法则会在线程休眠的同时释放掉机锁,其他线程可以访问该对象。wait()必须在同步的代码块中执行。当一个线程执行到wait()方法时,它就进入到一个和该对象相关的等待池中,同时失去了对象的机锁,可以允许其它的线程执行一些同步操作。但是wait()可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException。

wait可以让同步方法或者同步块暂时放弃对象锁,而将它暂时让给其它需要对象锁的人(这里应该是程序块,或线程)用,这意味着可在执行wait()期间调用线程对象中的其他同步方法!在其它情况下(sleep啊,suspend啊),这是不可能的.但是注意我前面说的,只是暂时放弃对象锁,暂时给其它线程使用,我wait所在的线程还是要把这个对象锁收回来的呀.wait什么?就是wait别人用完了还给我啊!

好,那怎么把对象锁收回来呢?

第一种方法,限定借出去的时间.在wait()中设置参数,比如wait(1000),以毫秒为单位,就表明我只借出去1秒中,一秒钟之后,我自动收回.

第二种方法,让借出去的人通知我,他用完了,要还给我了.这时,我马上就收回来.哎,假如我设了1小时之后收回,别人只用了半小时就完了,那怎么办呢?靠!当然用完了就收回了,还管我设的是多长时间啊.

那么别人怎么通知我呢?相信大家都可以想到了,notify(),这就是最后一句话"而且只有在一个notify()或notifyAll()发生变化的时候,线程才会被唤醒"的意思了.

notify()唤醒在此对象监视器上等待的单个线程。当它被一个notify()方法唤醒时,等待池中的线程就被放到了锁池中。该线程将等待从锁池中获得机锁,然后回到wait()前的中断现场。

notifyAll()唤醒在此对象监视器上等待的所有线程。

suspend和resume()
join()
join()方法使当前线程停下来等待,直至另一个调用join方法的线程终止。值得注意的是,线程的在被激活后不一定马上就运行,而是进入到可运行线程的队列中。但是join()可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException。

yield()
Yield()方法是停止当前线程,让同等优先权的线程运行。如果没有同等优先权的线程,那么Yield()方法将不会起作用。

interrupt()
interrupt()中断线程。需要注意的是,InterruptedException是线程自己从内部抛出的,并不是interrupt()方法抛出的。对某一线程调用interrupt()时,如果该线程正在执行普通的代码,那么该线程根本就不会抛出InterruptedException。但是,一旦该线程进入到wait()/sleep()/join()后,就会立刻抛出InterruptedException。

这篇文章,给大家介绍一下 Thread 类的进阶知识,介绍并演示 interrupt()、join()、yield()的作用,还有守护线程的特点。

1 Thread 常用API

方法 描述
void start() 启动此线程,JVM将会执行这个线程的 run() 方法
void interrupt() 将线程置为中断状态,中断标识设置为true。
boolean isInterrupted() 判断此线程是否已处于中断状态,此方法不影响线程的中断状态。
static boolean interrupted() 静态方法,判断此线程是否已处于中断状态,并清空此线程的中断状态,也就是说,如果此线程已经被中断了,调用此方法会返回true,并将此线程设置为非中断状态,中断标识设置为false。
void join() 阻塞等待此线程死亡(运行结束),
举例:如果在主线程中执行了子线程(thread)thread.join(),那主线程会阻塞等待直到子线程thread死亡。
看不懂描述,可以下面的demo。
void join(long millis) 最多阻塞等待多少毫秒等待此线程死亡(运行结束),0表示永远等待直到此线程死亡
void sleep(long millis) 使当前执行的线程在指定的毫秒数内休眠(暂时停止执行)
void yield() 让出cpu的执行权,将线程从运行转到就绪状态,但是下个时间片,该线程依然有可能被再次选中运行。
void setDaemon(boolean on) 入参为true时,设置当前线程为守护线程,否则为用户线程,此方法在start()方法之前调用才有效。
守护线程:与父线程同时死亡(结束)。
用户线程:用户线程是独立存在的,不会因为其他用户线程退出而退出
boolean isDaemon() 判断当前线程是否是守护线程

Thread中几个方法的作用

2 如何安全停止线程

2.1 线程自然死亡(结束)

  • 自然执行完
  • 抛出未处理异常

2.2 stop(),destroy(),resume(),suspend()已过时,不建议使用

  • stop():强制结束线程,会导致线程不会正确释放资源,
  • suspend():挂起,线程不会释放资源,容易导致死锁。

2.3 推荐使用 interrupt(),安全停止线程

注意: java线程是 协作式,而非抢占式
合理方式: 应该调用一个线程的 interrupt() 方法中断一个线程,并不是强行关闭这个线程,只是跟这个线程打个招呼,将线程的中断标志位置为true,线程是否中断,由线程本身决定。

一般情况是: 我们在当前线程的run()方法中,判断当前线程的中断状态是否为true,进而决定是否退出当前线程的运行。

3 InterruptedException

Thread的sleep()join()和Object的wait()方法,都能够抛出InterruptedException
interrupt()方法只是将线程的中断标志位置为true,sleep()join() 、wait()会不断检查线程的中断状态,如果当前线程中断标识位为ture,则会抛出InterruptEdException

注意: 线程抛出InterruptEdException后,清空此线程的中断状态,也就是说,将此线程设置为非中断状态,中断标识设置为false。
抛出的InterruptEdException被catch后,再调用isInterrupted(),会返回false。

4 interrupt()、isInterrupted()、interrupted() 的使用

  • interrupt():只是将线程的中断标志位置为true,并不是强行关闭这个线程,线程是否中断,由线程本身决定。
  • isInterrupted():判断此线程是否已处于中断状态,此方法不影响线程的中断状态。
  • interrupted():静态方法,获取当前线程的中断状态,不管当前线程中断状态如何,接着清空当前线程的中断状态。

4.1 演示 interrupt() 和 isInterrupted() 安全地退出线程运行


@Test
public void testInterrupt() throws InterruptedException {
// 演示 interrupt() 、 isInterrupted() 和 InterruptedException
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println("子线程开始执行任务..." + formatter.format(LocalDateTime.now()));
// 如果当前线程不处于中断状态,则每秒打印当前时间
while (!Thread.currentThread().isInterrupted()) {
try {
// sleep() 方法会检测线程的中断状态,如果已中断,则抛出InterruptedException
TimeUnit.SECONDS.sleep(1);
System.out.println("打印当前时间:" + formatter.format(LocalDateTime.now()));
} catch (InterruptedException e) {
System.out.println("catch InterruptedException:" + e.getMessage());
// InterruptedException 会清空中断状态,所以此时的中断状态为 false
System.out.println("当前线程中断状态:" + Thread.currentThread().isInterrupted());
// e.printStackTrace();
// InterruptedException 会清空中断状态,需要再次执行interrupt(),才能将线程状态置为已中断,退出循环
Thread.currentThread().interrupt();
}
}
System.out.println("子线程任务结束..." + formatter.format(LocalDateTime.now()));
}
});
thread.start();
TimeUnit.SECONDS.sleep(3);
System.out.println("主线程执行子线程的interrupt()");
// 只是将线程的中断标志位置为true,并不是强行关闭这个线程,线程是否中断,由线程本身决定。
thread.interrupt();
}

运行结果:

子线程开始执行任务...2020-09-17 18:18:44
打印当前时间:2020-09-17 18:18:45
打印当前时间:2020-09-17 18:18:46
主线程执行子线程的interrupt()
catch InterruptedException:sleep interrupted
当前线程中断状态:false
子线程任务结束...2020-09-17 18:18:47

4.2 演示 interrupted()

    @Test
public void testInterrupted() throws InterruptedException {
// 演示 interrupted()
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// 静态方法 Thread.interrupted() 获取当前线程的中断状态,不管当前线程中断状态如何,接着清空当前线程的中断状态
System.out.println("静态方法 Thread.interrupted() 获取当前线程的中断状态:" + Thread.interrupted() + ",然后清空中断状态");
System.out.println("成员方法 interrupted() 获取当前线程的中断状态:"+Thread.currentThread().interrupted());
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println("子线程开始执行任务..." + formatter.format(LocalDateTime.now()));
// 如果当前线程不处于中断状态,则每秒打印当前时间
if (!Thread.currentThread().isInterrupted()) {
System.out.println("打印当前时间:" + formatter.format(LocalDateTime.now()));
}
System.out.println("子线程任务结束..." + formatter.format(LocalDateTime.now()));
}
});
thread.start();
thread.interrupt();
TimeUnit.SECONDS.sleep(3);
}

运行结果:

静态方法 Thread.interrupted() 获取当前线程的中断状态:true,然后清空中断状态
成员方法 interrupted() 获取当前线程的中断状态:false
子线程开始执行任务...2020-09-17 18:20:13
打印当前时间:2020-09-17 18:20:13
子线程任务结束...2020-09-17 18:20:13

5 join()、join(long millis) 的使用

join():阻塞等待此线程死亡(运行结束)。
join(long millis):设置等待的超时时间,阻塞等待此线程死亡(运行结束),若超时时间为0,则一直等待直到此线程死亡。
应用场景:这个经常用在线程间的协作,一个线程等另一个线程运行结束后,再继续运行。

线程自然死亡(结束)

  • 自然执行完
  • 抛出未处理异常

看不懂就看看代码,应该容易理解一点:

5.1 join() demo 演示一个线程等另一个线程运行结束后再继续运行

public class TestJoin {

    @Test
public void testJoin() throws InterruptedException {
// 演示 join() 方法使用
//线程自然死亡(结束):自然执行完或者抛出未处理异常 // main线程开始运行
System.out.println(Thread.currentThread().getName() + "开始运行...");
// t1 不需要等待其他线程运行结束
JoinThread t1 = new JoinThread("t1", null);
// t2 线程等待 t1 线程死亡,再继续往下运行
JoinThread t2 = new JoinThread("t2", t1);
// t3 线程等待 t2 线程死亡,再继续往下运行
JoinThread t3 = new JoinThread("t3", t2);
// t4 线程等待 t3 线程死亡,再继续往下运行
JoinThread t4 = new JoinThread("t4", t3); t1.start();
t2.start();
t3.start();
t4.start();
// main线程等待 t4 线程死亡,再继续往下运行
t4.join();
System.out.println(Thread.currentThread().getName() + "继续运行");
System.out.println(Thread.currentThread().getName() + "运行结束...");
}
} //自定义线程类
class JoinThread extends Thread { // previousThread:前一个线程
// 当前线程等待 previousThread 运行结束再继续运行
private Thread previousThread; public JoinThread(String name, Thread previousThread) {
super(name);
this.previousThread = previousThread;
} @Override
public void run() {
if (this.previousThread != null) {
System.out.println(Thread.currentThread().getName() + "调用" + this.previousThread.getName() + ".join(),等待" + this.previousThread.getName() + "死亡");
try {
// 当前线程等待 previousThread 运行结束再继续运行
this.previousThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "继续运行");
}
try {
// sleep 2 秒,模拟业务执行花了2秒
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行结束...");
}
}

运行结果:

main开始运行...
t2调用t1.join(),等待t1死亡
t4调用t3.join(),等待t3死亡
t3调用t2.join(),等待t2死亡
t1运行结束...
t2继续运行
t2运行结束...
t3继续运行
t3运行结束...
t4继续运行
t4运行结束...
main继续运行
main运行结束...

5.2 join(long millis) demo


@Test
public void testJoinWithTimeOut() throws InterruptedException {
// 演示 join(long millis) 方法使用: 设置等待的超时时间,阻塞等待此线程死亡(运行结束),若超时时间为0,则一直等待直到此线程死亡。 // main线程开始运行
System.out.println(Thread.currentThread().getName() + "开始运行...");
Thread thread = new Thread(()->{
System.out.println(Thread.currentThread().getName() + "开始运行...");
try {
// sleep 5 秒,模拟业务执行花了5秒
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "结束..."); });
thread.start();
// join(),阻塞等待 thread 运行结束,设置超时时间为 3000 ms
thread.join(3000);
System.out.println(Thread.currentThread().getName() + "继续运行");
System.out.println(Thread.currentThread().getName() + "运行结束...");
TimeUnit.SECONDS.sleep(3);
}

运行结果:

main开始运行...
Thread-0开始运行...
main继续运行
main运行结束...
Thread-0结束...

6 yield() 的使用

yield():当前线程让出cpu的执行权,将线程从运行转到就绪状态,但是下个时间片,该线程依然有可能被再次选中运行。
很多情况下,看不出它让出CPU的效果,因为下个时间片它依然有机会抢到cpu资源

public class TestYield {

    @Test
public void testYield() throws InterruptedException {
// 演示 yield() 方法使用
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
if (i == 2) {
System.out.println(Thread.currentThread().getName() + "--> " + i + " -- yield");
// yield():线程让出CPU,变成就绪状态
Thread.currentThread().yield();
}else
System.out.println(Thread.currentThread().getName() + "--> " + i);
}
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "=== " + i);
}
}
}, "t2");
t1.start();
t2.start();
TimeUnit.MILLISECONDS.sleep(1500);
}
}

运行结果:

t1--> 0
t1--> 1
t1--> 2 -- yield
t2=== 0
t2=== 1
t2=== 2
t2=== 3
t2=== 4
t1--> 3
t1--> 4

7 setDaemon() 守护线程

守护线程:与父线程同时死亡(结束),也就是说,当父线程运行结束或者抛出异常导致死亡时,不管守护线程业务逻辑有没有执行结束,守护线程也会同时死亡。
用户线程:用户线程是独立存在的,不会因为其他用户线程退出而退出

注意: 守护线程就算有finally代码块,也不能保证一定执行

7.1 演示守护线程的finally代码块不能保证一定执行

public class TestDaemon {

    @Test
public void testDaemon() throws InterruptedException {
// 演示 setDaemon()、isDaemon() 方法使用
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
//判断当前线程是不是守护线程
System.out.println(Thread.currentThread().getName() + "是守护线程: " + Thread.currentThread().isDaemon());
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 如果当前线程不处于中断状态,则每秒打印当前时间
while (!Thread.currentThread().isInterrupted()) {
try {
// sleep() 方法会检测线程的中断状态,如果已中断,则抛出InterruptedException
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName() + " 打印当前时间:" + formatter.format(LocalDateTime.now()));
} catch (InterruptedException e) {
e.printStackTrace();
// InterruptedException 会清空中断状态,需要再次执行interrupt(),才能将线程状态置为已中断,退出循环
Thread.currentThread().interrupt();
}
}
System.out.println(Thread.currentThread().getName() + " 任务结束..." + formatter.format(LocalDateTime.now()));
} finally {
System.out.println(Thread.currentThread().getName() + "执行 finlly 代码块");
}
}
}, "t1");
//设置为守护线程,在start()方法之前调用才有效
t1.setDaemon(true);
t1.start();
TimeUnit.MILLISECONDS.sleep(3000);
}
}

运行结果:

t1是守护线程: true
t1 打印当前时间:2020-09-18 11:08:52
t1 打印当前时间:2020-09-18 11:08:53

通过上面的运行结果可以看出,当父线程(主线程)死亡(运行结束时),守护线程也终止运行(死亡),异常信息栈没有打印,finally代码块也没有执行。

通过上面的测试用例,给大家演示了 interrupt()、join()、yield()的作用,还有守护线程的特点。
这几个方法也是Java面试中经常碰到的知识点,希望大家能够在工作中学以致用,正在求职的朋友也能很好的应对面试。

作者:黑桃SEVEN_PIG
链接:https://www.imooc.com/article/310782?block_id=tuijian_wz
来源:慕课网
本文原创发布于慕课网 ,转载请注明出处,谢谢合作

各个方法之间的区别

线程方法名称 是否释放同步锁 是否需要在同步的代码块中调用 方法是否已废弃 是否可以被中断
sleep()
wait()
suspend      
resume()      
join()    

java线程用法和区别的更多相关文章

  1. JAVA线程和进程区别

    1,JAVA线程和进程区别? (1)简单来讲一个运行的程序就是一个进程,一个进程中可以有多个线程(线程是程序执行的最小单元). (2)线程有四种状态:运行,就绪,挂起,结束 (3)使用多线程的好处 使 ...

  2. Java线程中yield与join方法的区别

    长期以来,多线程问题颇为受到面试官的青睐.虽然我个人认为我们当中很少有人能真正获得机会开发复杂的多线程应用(在过去的七年中,我得到了一个机会),但是理解多线程对增加你的信心很有用.之前,我讨论了一个w ...

  3. java 线程同步 原理 sleep和wait区别

    java线程同步的原理java会为每个Object对象分配一个monitor, 当某个对象(实例)的同步方法(synchronized methods)被多个线程调用时,该对象的monitor将负责处 ...

  4. java线程中的sleep和wait区别

                                                                            面试题:java线程中sleep和wait的区别以及其资 ...

  5. JAVA线程池shutdown和shutdownNow的区别

    一.区别介绍 shutDown()  当线程池调用该方法时,线程池的状态则立刻变成SHUTDOWN状态.此时,则不能再往线程池中添加任何任务,否则将会抛出RejectedExecutionExcept ...

  6. Java进程和线程关系及区别

    1.定义 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位. 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基 ...

  7. [ 原创 ] Java基础1--Java中super和this的用法和区别

    许多同学在学习Java时分不清楚this和super的用法和区别,今天偶然发现一片加精的博文,看完内容准备自己也写下来积累一下 1.如果想在子类的构造方法中调用父类的构造方法,必须在子类的构造方法中使 ...

  8. Java线程状态及 wait、sleep、join、interrupt、yield等的区别

    Java中的线程状态(详见Java线程状态及转换-MarchOn): wait:Object类的实例方法,释放CPU执行权,进入等待状态,直到  被中断.被拥有该对象锁的线程唤醒(notify或not ...

  9. Java 线程和操作系统的线程有啥区别?

    尽人事,听天命.博主东南大学硕士在读,携程 Java 后台开发暑期实习生,热爱健身和篮球,乐于分享技术相关的所见所得,关注公众号 @ 飞天小牛肉,第一时间获取文章更新,成长的路上我们一起进步 本文已收 ...

  10. 【转载】 Java线程面试题 Top 50

    Java线程面试题 Top 50 不管你是新程序员还是老手,你一定在面试中遇到过有关线程的问题.Java语言一个重要的特点就是内置了对并发的支持,让Java大受企业和程序员 的欢迎.大多数待遇丰厚的J ...

随机推荐

  1. Codeforces 1969 A-F

    题面 A B C D E F 难度:红 橙 绿 绿 蓝 紫

  2. 源码开放:WebSocket应用示例

    1 WebSocket概述 WebSocket是HTML5下一种新的协议(本质上是一个基于TCP的协议),它实现了浏览器与服务器之间的全双工通信,能够节省服务器资源和带宽,达到实时通讯的目的.WebS ...

  3. 操作方法分享:4G模组中移OneNET轻松上云平台

    ​ 一.简介 1.1 IoT_CLOUD的功能 IoT_CLOUD库本质就是上层设计一套通用的API,用库来实现每个平台功能的对接. 目前已经实现了各个平台的所有注册方式,其中自动注册会将相关验证信息 ...

  4. 教育账号无法登录OneDrive的一种解决方法

    众所周知,微软的服务总是能出现一些奇奇怪怪的问题,比如说教育账号无法登录OneDrive,尝试使用网上的临时解决方案失败 onedrive学生账号无法登录win10 OneDrive客户端 用户可以在 ...

  5. Java Cache系列之Cache概述和Simple Cache

    前记:最近公司在做的项目完全基于Cache(Gemfire)构建了一个类数据库的系统,自己做的一个小项目里用过Guava的Cache,以前做过的项目中使用过EHCache,既然和Cache那么有缘,那 ...

  6. java动态跟踪分析工具BTrace实现原理

    今天,Team Leader推荐了一个非常棒的动态跟踪分析工具 – BTrace.由于对它的实现原理非常感兴趣,于是花了点时间研究了一下,顺便写点心得. 什么是BTrace? BTrace是SUN K ...

  7. 基于JDBC的数据库连接池高效管理策略

    在基于JDBC的数据库应用开发中,数据库连接的管理是一个难点,因为它是决定该应用性能的一个重要因素.本文在对数据库连接进行透彻分析的基础上,提出并实现了一个高效的连接管理策略,使得开发高性能的数据库应 ...

  8. sqlite3之基础

    最近在用Python借助于pySimpleGui做一个桌面小工具, 奉行小巧,简单的宗旨, 使用了本地数据库sqlite3来进行本地数据的存储 参考: 官网: https://www.sqlite.o ...

  9. 轻量虚拟机之Multipass

    官网:https://multipass.run/ 它可以快速在电脑上快速搭建一个轻量级的虚拟机,并且相比于 Vmware 更加轻量,只需一行命令快速创建 Ubuntu 虚拟机. Multipass  ...

  10. elasticsearch权限验证(Auth+Transport SSL)

    转载:https://knner.wang/2019/11/26/install-elasticsearch-cluster-7-4.html 在新版的Elastic中,基础版(免费)的已经提供了基础 ...