初识Java多线程

1、什么是线程,Java中的多线程是什么意思。

线程不是进程,但其行为很像进程,线程是更小的执行单位。每个线程都有自己的生命周期(新建、运行、中断、死亡)。

在Java语言中有一个重大特性就是内置对多线程的支持,多线程指的是一个应用程序内有多个执行体存在,按照几条不同的执行线索共同工作的情况,在计算机中因为执行速度过快会给人一个错觉——“多个执行体同时在工作”,真实情况是Java虚拟机会快速地把控制从一个线程转到另一个线程中,使得所有线程都能有机会使用CPU资源。

进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间

线程:

  • 是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行. 一个进程最少有一个线程
  • 线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分成若干个线程

2、线程的生命周期

2.1 新建

​ 当一个Thread类或其子类被声明并创建时,新生的线程就处于新建状态,拥有对应的内存空间和资源。

2.2 运行

​ 当线程被创建后,它已经拥有了属于自己的内存空间和资源,但是它还没有被JVM所识别,这个时候需要使用start()方法通知JVM,这样这个线程就可以开始排队了。当JVM将CPU使用权交给线程时,如果线程是Thread类的子类创建的,它必须立刻使用类中的run()方法。run()方法规定了该线程的任务。所以子类必须重写Thread类的run()方法,因为Thread类中的run()方法是没有具体内容的。(注意)在run()方法结束前不得再调用start()方法。

2.3 中断

中断有四个原因:1、JVM将资源交给其他线程使用。2、线程使用了sleep()方法,让出了CPU的使用权。3、线程使用了wait()方法,使得当前线程进入等待状态,不主动进入队列排队,需要使用notiy(),notifyAll()方法唤醒。4、在使用CPU资源的时候发生阻塞。只有当阻塞消失才能重新排队,从中断处继续执行。

2.4 死亡

死亡的线程不具备继续执行的能力。死亡原因有两个:1、执行完run()方法中的全部语句。2、被强制性终止,释放了线程实体

2.5 线程的6个状态

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

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

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

3、线程的创建

3.1 Thread类

通过继承Thread类来创建线程,需要使用start启动这个线程。(不推荐使用)

3.2 Runnable接口

通过实现Runnable接口来创建线程,接口只有一个run方法。

3.3 实现Runnable 与继承Thread相比优势

  • 通过创建任务,然后给线程分配的方式来实现多线程,更适合多个线程同时执行相同任务的情况。
  • 可以避免单继承所带来的局限性。
  • 任务与线程本身是分离的,提高了程序的健壮性。
  • 后续学习的线程池技术,接受Runnable类型的任务不接收Thread类型的线程。

4、线程同步与异步&并发与并行

4.1 线程同步与异步

区别:

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

多个线程使用同一个资源时出现并发问题,线程不安全,数据会发生紊乱。
线程同步就是若干线程都需要使用一个synchronized(同步)修饰的方法。
多个线程调用这个被关键词synchronized修饰的方法时都要遵循同步机制。
——同步机制:当一个线程A使用synchronized修饰的方法时,其他线程如果想使用必须等待线程A主动放弃或者使用完这个方法。

4.2 同步方法与同步块

——同步方法

代码:

public class test03 {public static void main(String[] args) {Bank bank = new Bank();bank.account.start();bank.cashier.start();bank.setMoney(0);}
}
class Bank implements Runnable{Thread account,cashier;int money=0;Bank() {account=new Thread(this);cashier=new Thread(this);account.setName("会计");cashier.setName("出纳");}@Overridepublic void run() {if(Thread.currentThread()==account){saveOrTake(300);}else if(Thread.currentThread()==cashier){saveOrTake(150);}}public  int setMoney(int n){money=n;return money;}public synchronized void saveOrTake(int amount){if(Thread.currentThread()==account){for(int i=0;i<3;i++){money += amount/3;System.out.println("会计存入:"+amount/3+"万元;账上金额:"+money+"万元。需要休息2s");try {Thread.sleep(2000);}catch (InterruptedException e){}}}else if(Thread.currentThread()==cashier){for (int i=0;i<3;i++){money -=amount/3;System.out.println("出纳取出:"+amount/3+"万元;账上金额:"+money+"万元。需要休息2s");try {Thread.sleep(2000);}catch (InterruptedException e){}}}}
}

代码解释:当前程序有三个线程main(主线程)、casher(出纳线程)、account(会计线程)。出纳线程和会计线程都要对money这个变量进行操作,写了一个saveOrTake(int mount)方法来控制。使用了synchronized(同步)对该方法修饰。确保出纳线程在占用CPU资源的时候会计线程无法使用,必须等待出纳线程主动结束。如果方法被静态修饰则锁是类名.class。而不是this。

——同步块:synchronized 默认锁的是this ,同步块锁的是需要增删改的对象。

        synchronized (money) {if (Thread.currentThread() == account) {for (int i = 0; i < 3; i++) {money += amount / 3;System.out.println("会计存入:" + amount / 3 + "万元;账上金额:" + money + "万元。需要休息2s");try {Thread.sleep(2000);} catch (InterruptedException e) {}}} else if (Thread.currentThread() == cashier) {for (int i = 0; i < 3; i++) {money -= amount / 3;System.out.println("出纳取出:" + amount / 3 + "万元;账上金额:" + money + "万元。需要休息2s");try {Thread.sleep(2000);} catch (InterruptedException e) {}}}}

与前面同步方法不同的是,同步块锁的对象是变量money,money是需要增删改的对象。表现方式synchronized(object){}

4.3 JUC

​ (JUC后面会专门写一篇文章)

​ JUC全称java.util.concurrent 里面有一个方法CopyOnWriteArrayList方法是安全类型的集合

public static void main(String[] args) throws InterruptedException {CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();for (int i = 0; i < 10000; i++) {new Thread(()->{list.add(Thread.currentThread().getName());}).start();}Thread.sleep(300);System.out.println(list.size());}

4.3 并发与并行

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

4.4 死锁

死锁是指两个线程都需要对方占用的资源,但是双方都不释放自己拥有的资源,然后形成僵持。
死锁产生的四个必要条件:
1、互斥条件:一个资源只能被一个进程使用
2、请求与保持条件:一个进程因请求资源而阻塞时,对已获得资源不释放
3、不剥夺条件:进程已获得资源,在未使用完前不强行剥夺
4、循环等待条件:若干进程之间形成头尾相连的循环等待资源关系

如下是死锁代码:

public class test11 {public static void main(String[] args) {play hei = new play(1, "黑马王子");play bai = new play(0, "白马王子");hei.start();bai.start();}
}
class CarToy{}
class Remote{}
class play extends Thread{static CarToy carToy=new CarToy();static Remote remote=new Remote();int choice;String name;public play(int choice,String name) {this.choice=choice;this.name=name;}@Overridepublic void run() {super.run();try {play();} catch (InterruptedException e) {e.printStackTrace();}}private void play() throws InterruptedException {if(choice==0){synchronized (carToy){System.out.println(this.name+"获得玩具车");Thread.sleep(1000);synchronized (remote){System.out.println(this.name+"获得遥控");}}}else if(choice==1){synchronized (remote){System.out.println(this.name+"获得遥控");Thread.sleep(1000);synchronized (carToy){System.out.println(this.name+"获得玩具车");}}}}
}

破坏了死锁条件2-请求与保持条件:

private void play() throws InterruptedException {if(choice==0){synchronized (carToy){System.out.println(this.name+"获得玩具车");Thread.sleep(1000);}synchronized (remote){System.out.println(this.name+"获得遥控");}}else if(choice==1){synchronized (remote){System.out.println(this.name+"获得遥控");Thread.sleep(1000);}synchronized (carToy){System.out.println(this.name+"获得玩具车");}}}

4.5 Lock锁

​ 是java 5.0开始提供的更加强大的线程同步机制–通过显示定义同步锁来实现同步。同步锁使用JUC 的lock接口充当对多线程访问独占资源的工具。每次只能有一个线程对Lock对象加锁。线程访问前应先获得Lock对象。

Synchronized与Lock对比:

  • Lock锁是显示锁(需要手动开启和手动关闭),synchronized是隐式锁,出了作用域自动释放。

  • Lock锁只有代码块锁,synchronized有代码块锁和方法锁

  • Lock锁性能好,具有良好的扩展性


实现Lock类中的ReentrantLock(可重入锁)方法 :

可重入锁——某个线程已经获得某个锁,可以再次获取锁而不会出现死锁

public class test12 {public static void main(String[] args) {buyTicke buyTicke = new buyTicke();new Thread(buyTicke).start();new Thread(buyTicke).start();new Thread(buyTicke).start();}
}
class buyTicke implements Runnable{int tickeNum=10;final static ReentrantLock lock = new ReentrantLock();//可重入锁@Overridepublic void run() {while (true){try {lock.lock();//加锁if(tickeNum>0){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(tickeNum--);}else {break;}}finally {lock.unlock();//解锁}}}
}

4.6 公平锁与非公平锁

当锁被打开时,线程遵循先到先得则被称为公平锁,反之需要争抢被称为非公平锁

目前默认使用的都是非公平锁,公平锁只有ReentrantLock锁在创建的时候给予true值才会成为公平锁

Lock lock = new ReentrantLock(true);

5、静态代理模式

静态代理:为一个其他对象提供一个代理。例如结婚时需要请婚庆公司帮忙,租房要请中介帮忙,代理就相当于婚庆公司和中介以求在不改变目标对象的情况下对原有的功能实现增强。实现过程:目标对象和代理对象都要实现同一个对象(接口),代理对象代理目标对象。优点:可以增强目标对象功能。

6、Lambda表达式

​ lambda表达式是简化程序代码,只支持函数式接口编程。函数式接口是指接口中只存在唯一一个抽象方法。

6.1 Lambda表达式特点

  • 无需指定参数类型,编译器可以识别。(如果不指定类型,所有变量都不允许写类型)
  • 如果只有一个参数类型可以省略圆括号。
  • 使用Lambda表达式只能引用内部final修饰的常量和外部final标记的static常量
  • 如果只有一个表达式,可以省略大括号
  • 如果计算代码不能使用一个语句写完可以使用大括号括起来
public class test07 {public static void main(String[] args) {//设置带参类型的Lambda表达式Ope add=(int a,int b)-> a+b;//设置不带参数类型的Lambda表达式Ope sub=(a,b)-> a-b;//一个参数可以不用圆括号Say world=s->System.out.println(s);//大括号指明了表达式返回一个值,如果要计算的代码无法放在一个语句 可以使用大括号括起来Ope multi=(a,b)->{return a*b;};//如果只有一个表达式,编译器会自动返回值Ope div=(a,b)-> a/b;final  String word1="hello,Java";System.out.println(add.operation(3,4));System.out.println(sub.operation(4,3));world.say(word1);//使用了内部的final修饰的常量word1;world.say(word2);//使用了外部的static修饰的final常量 word2.System.out.println(multi.operation(3,4));System.out.println(div.operation(12,3));}static final String word2="hello,World";
}
interface Ope{int operation(int a,int b);
}
interface Say{void say(String s);
}

7、线程的常用方法

7.1 start——使该线程开始执行

void start() 使该线程开始执行;Java 虚拟机调用run该线程的方法。

7.2 run——执行Thread类start方法

void run() 如果该线程是使用单独的 Runnable 对象构造的,则调用该 Runnable对象的run方法;否则,此方法不执行任何操作并返回。

7.3 currentThread——获取当前线程

static Thread currentThread() 返回对当前正在执行的线程对象的引用。

7.4 isAlive——测试此线程是否存活

boolean isAlive() 测试此线程是否存活。

7.5 interrupt——中断线程

void interrupt() 中断此线程。

7.6 sleep——线程休眠

static void sleep(long millis, int nanos) 使当前正在执行的线程休眠(暂时停止执行)指定的毫秒数加上指定的纳秒数,取决于系统计时器和调度程序的精度和准确性。

7.7 join——强制执行

void join() 等待这个线程死亡。

7.8 yield——线程礼让

static void yield() 向调度程序提示当前线程愿意放弃其当前对处理器的使用。

7.9 setDaemon——守护线程

void setDaemon(boolean on) 将此线程标记为守护线程或用户线程。

7.10 setPriority——设置优先级

void setPriority(int newPriority) 更改此线程的优先级。

7.11 setName——修改名字

void setName(String name) 将此线程的名称更改为等于参数name。

7.12 getName——获取线程名

String getName() 返回此线程的名称。

7.13 getId——获取标识符

long getId() 返回此线程的标识符。

每个类都有Object——这个超类,都要实现以下方法

7.14 wait——等待

void wait() 使当前线程等待直到它被唤醒,通常是通过通知或中断。

7.15 notify——唤醒单个线程

void notify() 唤醒在此对象的监视器上等待的单个线程。

7.16 notifyAll——唤醒所有线程

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

8、线程停止

线程停止有三种方式
1、使用标志位如下图

public class test08 implements Runnable {private boolean flag=true;@Overridepublic void run() {int i=0;while (flag){System.out.println("running..."+i++);}}public void stop(){this.flag=false;}public static void main(String[] args) {test08 test08 = new test08();new Thread(test08).start();for (int i = 0; i < 1000; i++) {System.out.println("main"+i);if(i==900){test08.stop();System.out.println("stop");}}}
}

2、使用stop方法(不推荐使用)
3、使用interrupt()方法如下图

public class test09  {public static void main(String[] args) {Thread testInter = new Thread(()-> System.out.println("正常"));testInter.start();for (int i = 0; i < 20; i++) {System.out.println("main"+i);if(i==10){System.out.println("我在这个时候停止"+i);testInter.interrupt();}}}
}
结果:(部分截图)

9、线程休眠、线程礼让、线程强制执行

9.1 线程休眠

使用sleep(Long millis)方法使得线程暂时休眠 单位毫秒

9.2 线程礼让

使用yield()方法使得当前线程从运行态转换为就绪态,不阻塞,但也不一定成功。

9.3 线程强制执行

​ 使用join()方法,线程A使用join方法参数时线程B 例:A.join(B) 意味着线程A将CPU资源给B,等待线程B执行完毕后A再执行。

public class test05 {public static void main(String[] args) {joinThread joinThread = new joinThread();joinThread.customer.start();}
}
class joinThread implements Runnable{Thread sale,cakeMaker,customer;public joinThread() {sale=new Thread(this);cakeMaker=new Thread(this);customer=new Thread(this);}@Overridepublic void run() {if(Thread.currentThread()==sale){try {cakeMaker.start();cakeMaker.join();}catch (InterruptedException e){}}else if(Thread.currentThread()==cakeMaker){System.out.println("正在制作蛋糕,请稍后...");try {Thread.sleep(2000);System.out.println("制作完成");}catch (InterruptedException e){}}else if(Thread.currentThread()==customer){try {sale.start();sale.join();}catch (InterruptedException e){}}}
}

代码解释:顾客(线程A)在向商店(线程B)要求一份蛋糕,商店自己不会制作,需要蛋糕师(线程C)制作。线程A提交要求后使用join方法把CPU资源交给线程B,线程B接受要求后把CPU资源交给线程C。

10、守护线程

线程默认是非守护线程,非守护线程被称为用户线程。当程序中所有的用户线程结束运行时,即使守护线程中的run()方法没有执行完毕,守护线程也立刻结束运行。线程运行前必须设置为守护线程 setDaemon(boolean)默认是false.

Thread tGod = new Thread(()->{while (true){System.out.println("god一直在保护你");}});Thread tYou=new Thread(()->{for (int i = 0; i < 20; i++) {System.out.println("你很开心");}System.out.println("==you dead==");});tGod.setDaemon(true);//设置为守护进程tYou.start();tGod.start();

11、Callable 接口

Callable使用步骤

1. 编写类实现Callable接口 , 实现call方法
class XXX implements Callable<T> {@Override
public <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()不能抛出

Callable获取返回值

  • Callable接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。

12、线程池

12.1 线程池概述(Executor)

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

线程池的好处

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

12.2 缓存线程池

/**
* 缓存线程池.
* (长度无限制)
* 执行流程:
* 1. 判断线程池是否存在空闲线程
* 2. 存在则使用
* 3. 不存在,则创建线程 并放入线程池, 然后使用
*/ExecutorService service = Executors.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());}});try{Thread.sleep(1000);}catch(Exception e){}service.execute(new Runnable() {@Overridepublic void run() {System.out.println("线程的名称:"+Thread.currentThread().getName());}});

12.3 定长线程池

/**
* 定长线程池.
* (长度是指定的数值)
* 执行流程:
* 1. 判断线程池是否存在空闲线程
* 2. 存在则使用
* 3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
* 4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
*/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());}});

12.4 单线程线程池

效果与定长线程池 创建时传入数值1 效果一致.
/**
* 单线程线程池.
* 执行流程:
* 1. 判断线程池 的那个线程 是否空闲
* 2. 空闲则使用
* 4. 不空闲,则等待 池中的单个线程空闲后 使用
*/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());}});

12.5 周期性任务定长线程池

/**
* 周期任务 定长线程池.
* 执行流程:
* 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,2,TimeUnit.SECONDS);

JavaSE——Day12相关推荐

  1. 泛型和异常、 Properties文件读取加载

    1.泛型 概念:允许在定义类,接口的时候通过一个字母标识表示类中的属性的类别或者方法返回值,使用时传入棱形尖括号的是引用类型 泛型版本: List list = new ArrayList(); // ...

  2. 东软大三上学期实训笔记-javase篇Day12

    javase篇Day12 1.类 2.对象的创建 3.构造方法: 4.面向对象三大特性 5.封装: 6.this 关键字 7.继承: 8.方法的重写(方法的覆盖): 9.super 10.多态: 11 ...

  3. JAVASE相关知识点

    JavaSE 文章目录 JavaSE IDEA快捷键 EXPAND TIPS MEMO1 前置内容 MEMO2 DeBug.标识符.数据类型.进制相关 MEMO3 运算符 MEMO4 switch语句 ...

  4. 刘意JavaSE 学习笔记——总纲

    开始学习JavaSE了,记录下自己学习的进程. 学习大纲 1 开发环境配置 day01 配置 2 基础语法(基本差不多,看一下就好) day02 命名规则,进制转换 day03 运算符,if语句 da ...

  5. 【JavaSE】day03_Date、SimpleDateFormat、Calendar、Collection

    [JavaSE]day03_Date.SimpleDateFormat.Calendar.Collection 1.Date及其经常使用API 1)JAVA 中的时间 Java中的时间使用标准类库的D ...

  6. java视频为什么这么多_为什么看java教学视频教的都是javase,两者难道语言相同吗?...

    Java 分类 Java SE(Java Platform Standard Edition) :Java平台标准版.主要用于桌面应用程序的开发,是Java技术的核心,提供基础 Java开发工具.执行 ...

  7. java多态口诀,Java之路---Day12(多态),多态Java

    Java之路---Day12(多态),多态Java 2019-10-26-22:40:09 目录: 1.多态的概念 2.多态的分类 3.实现多态的三个必要条件 4.多态的格式 5.多态成员变量的使用特 ...

  8. 简述JavaME,JavaSE,JavaEE

    javaME:微型版,应用于移动等 JavaSE:标准版,应用于桌面环境 JavaEE:企业版,应用于基于Java的应用服务器 Java SE(Java Platform,Standard Editi ...

  9. javaee, javaweb和javase的区别以及各自的知识体系

    javaee, javaweb和javase的区别以及各自的知识体系 来源 https://blog.csdn.net/weixin_39297312/article/details/79454642 ...

最新文章

  1. 如果不被吐槽,那我还是程序员吗
  2. ThinkPHP 数据库表结构处理类(简单实用)
  3. BUUCTF(pwn)bjdctf_2020_babystack2
  4. 函数传参和实际应用—JS学习笔记2015-6-5(第49天)
  5. Linux Qt打包应用程序--利用linuxdeployqt
  6. spark学习-72-源代码:Endpoint模型介绍(4)-Spark为何使用Netty通信框架替代Akka
  7. Android 四大组件之一(Activity)
  8. 获取div相对文档的位置
  9. 分享一个圆角自定义的漂亮AlertDialog
  10. HTML做成信纸格式,css实现一个写信的格式_html/css_WEB-ITnose
  11. 开学必备宿舍神器,续航好的蓝牙耳机推荐
  12. 从零开始设计RISC-V处理器——指令系统
  13. mysqldump: Got error: 1168 differently defined non-MyISAM LOCK TABLES
  14. Linux控制Nvidia显卡风扇转速
  15. Python实现海洋测绘基于最小二乘法的潮汐调和分析
  16. 见证云力量|飞马网技术沙龙“云计算专场”圆满结束
  17. linux服务器好管理吗,给初学者Linux服务器管理建议
  18. 最好用的xshell替代软件----FinalShell工具
  19. Python爬音乐--qq
  20. 来自腾讯相当好的文章:研发效能度量实践指南

热门文章

  1. Linux入门(6)- SecureCRT 和 SecureFX 的使用
  2. 结构图的分类--产品功能结构图、产品信息结构图、产品结构图
  3. 生日悖论的泛化问题的讨论
  4. MATLAB函数判断绝对素数,自定义函数,找出 以内所有素数(质数)并计算它们的和,matlab...
  5. 阿里云code结合git管理代码,运用webHook同步部署服务器代码(php)
  6. 2020 - 2021 年 Web 前端最新导航
  7. LayIM 3.9.1与ASP.NET SignalR实现Web聊天室快速入门(五)之使用RabbitMQ缓存消息
  8. 用 PHP 进行 HTTP 认证,Basic Auth
  9. 行测做的慢,如何提升做题速度?怎么提升正确率?
  10. 【凸优化】关于 KKT 条件 及其最优性