javaEE(多线程、线程通信、线程安全、线程池、线程池工具)
多线程
- 多线程的创建
- Thread类的方法
- 线程安全、线程同步
- 线程通信、线程池
- 定时器、线程状态..
Thread类
- java是通过java.lang.Thread类来代表线程的
- 按照面向对象的思想,Thread类应该提供了实现多线程的方式
创建线程
方式一
1.定义一个线程类:extends Thread
public class MyThread extends Thread{
//2.重写run方法
@Override
public void run(){
for(int i=0;i<5;i++){
System.out.print(i);
}
}
}
public clss ThreadDemo1{
main(){
//3.new 一个新线程
Tread t =new MyThread();
//4.调用start方法
t.start();
for(int i=0;i<5;i++){
System.out.print(i);
}
}
}
优缺点:
1.编码简单
2.线程类继承了Thread类不利于扩展
为什么用start()不用run()?
直接run()是普通方法执行,依然是一个主线程
不要把主线程的任务放到执行之前,还是单线程
方式二
创建一个线程任务实现接口Runnable接口,重写run()方法
创建线程类对象
把对象任务交给Tread处理
public MyThread implements Runnable{
@Override
public void run(){
for(int i=0;i<5;i++){
System.out.print(i);
}
}
}public class a{
main(){
//创建一个任务对象
Runnable a = new MyThread();
//任务对象交给Thread处理
Thread t = new Thread(a);
t.start();
for(int i=0;i<5;i++){
System.out.print(i);
}
}
}
Thread的构造器:
- public Thread(String name) 可以为当线程指定名称
- public Thread(Runnable target)分装Runnable对象为线程对象
- public Thread(Runnable target,String name)
优缺点:
- 实现接口,扩展性强
- 多一层封装,如果线程有执行结果是不可以直接返回的 run()没有返回值
匿名内部类:
public class a{
main(){
//创建一个任务对象
Runnable a = new Runable(){
@Override
public void run(){
for(int i=0;i<5;i++){
System.out.print(i);
}
};
//任务对象交给Thread处理
Thread t = new Thread(a);
t.start();
for(int i=0;i<5;i++){
System.out.print(i);
}
}
}
方式三:JDK5.0 实现Callable接口和FutureTask
得到任务对象
- 实现Callable重写call(),
- 用FutureTask把Callable对象分装成线程的任务对象
把线程对象交给Thread
调用Thread的strat()启动线程
线程执行完毕后,通过FutureTask的get方法区获取任务执行的结果
pubilc class A implements Callable{
private int n;
public A(n){
this.n=n;
}
@Override
public String Call() throw Exception{
String sum=0;
for(int i=0;i<n;i++){
sum+=i;
System.out.print(i);
}
return "结果是"+sum;
}
}public class B {
main(){
Callable c = new A(10);
FutureTask f = new FutureTask<>(c);
//FutureTask实现了Runable接口
Thread t =new Thread(f);
t.start();
//没有结果会等待
try{
String s =f.get();
}catch(Excetion e){
e.printStackTrace();
}
}
}
优缺点:
- 实现接口扩展性强
- 可以得到结果
- 较为复杂
线程的常用方法
getName()获取线程名字
setName()修改线程名字
currentThread()获取线程的对象
定义一个线程类:extends Threadpublic class MyThread extends Thread{
//2.重写run方法
@Override
public void run(){
for(int i=0;i<5;i++){
System.out.print(i);
}
}
}public clss ThreadDemo1{
main(){
//3.new 一个新线程
Tread t =new MyThread();
//4.调用start方法
t.setName("1号线程")
t.start();
Thread m =Thread,currentThread();
for(int i=0;i<5;i++){
System.out.print(i);
}}
}
主线程的名称就是main
线程的休眠方法:技术优化--百度网盘
public static void sleep(long time) 让当前的线程休眠,单位毫秒
main(){
for(int i =0;i<5;i++){
if(i==3){
//技术优化点
Thread.sleep(3000);
}
}
}
线程安全问题
存在多线程共享
同时访问共享资源
存在修改共享资源
public class ThreadDemo{
main(){
Account a =new Account ("郝泾钊",10000);
//小明小红
new DrawThread(acc,"小明").start();
new DrawThread(acc,"小红").start();
}
}@Date
@All..
@Null..
public class Account{
private String card;
private double money;
public void drawMoney (double money){
//判断是谁来取钱
String name= Thread.currentThread().getName();
//判断是否够钱
if(this.money>=money){
//这样更容易不安全
System.out.print(name+"来取钱成功,出"+money);
this.money-=moey;
}else{
//余额不足
System.out.print(name+"来取钱,余额不足");
}
}
}public class DrawThread extends Thread{
private Account acc;
public DrawThread(Account acc,String name){
super(name);
this.acc=acc;
}
@Override
public void run(){
acc.drawMoney(1000);
}
}
线程同步
加锁
@Date
@All..
@Null..
public class Account{
private String card;
private double money;
public void drawMoney (double money){
//判断是谁来取钱
String name= Thread.currentThread().getName();
synchronized("heima"){
//判断是否够钱
if(this.money>=money){
//这样更容易不安全
System.out.print(name+"来取钱成功,出"+money);
this.money-=moey;
}else{
//余额不足
System.out.print(name+"来取钱,余额不足");
}
}
}
}synchronized(){}:
锁用随机对象好不好? 不好 同步代码块
建议使用共享资源作为锁对象
@Date
@All..
@Null..
public class Account{
private String card;
private double money;
public void drawMoney (double money){
//判断是谁来取钱
String name= Thread.currentThread().getName();
synchronized(this){
//判断是否够钱
if(this.money>=money){
//这样更容易不安全
System.out.print(name+"来取钱成功,出"+money);
this.money-=moey;
}else{
//余额不足
System.out.print(name+"来取钱,余额不足");
}
}
}
}
- 实例方法用this
- 静态方法用类名.class
线程同步
方法上修饰synchronized
默认用this,但是代码要高度面向对象
@Date
@All..
@Null..
public class Account{
private String card;
private double money;
public synchronized void drawMoney (double money){
//判断是谁来取钱
String name= Thread.currentThread().getName();
//判断是否够钱
if(this.money>=money){
//这样更容易不安全
System.out.print(name+"来取钱成功,出"+money);
this.money-=moey;
}else{
//余额不足
System.out.print(name+"来取钱,余额不足");
}
}
}
- 同步代码块好还是同步方法好?
- 同步代码块好,性能好
- 同步方法原理了?
- 也是用synchronized修饰默认是this
Lock锁
JDK5 加入的,更丰富功能,Lock接口不能实例化
- public ReentrantLock() 获得Lock的对象
方法:
void lock() 获得锁
void unlock() 释放锁
@Date
@All..
@Null..
public class Account{
private String card;
private double money;
private final Lock lock =new ReentrantLock();
public void drawMoney (double money){
//判断是谁来取钱
String name= Thread.currentThread().getName();
//判断是否够钱
lock.lock();
if(this.money>=money){
//这样更容易不安全
System.out.print(name+"来取钱成功,出"+money);
this.money-=moey;
}else{
//余额不足
System.out.print(name+"来取钱,余额不足");
}
lock.unlock();
}
}
线程通信
- 线程之间相互发送数据
- 共享的数据的情况决定自己做什么
常见模型:
- 生产者与消费者模型:
- 生产者线程产生数据,唤醒消费者;然后等待自己;消费者消费完数据之后唤醒生产者,然后等待自己。
方法:
Object类中:
- void wait() 让当前线程等待并释放占用锁,直到另一个线程调用notify或notifyAll方法
- void notify() 唤醒末个等待的线程
- void notifyAll()唤醒正在等待的所有线程
上述方法要用同步锁对象来调用
例子:
@Date
@All..
@Null..
public class Account{
private String card;
private double money;
public synchronized void drawMoney (double money){
//判断是谁来取钱
String name= Thread.currentThread().getName();
//判断是否够钱
if(this.money>=money){
//这样更容易不安全
System.out.print(name+"来取钱成功,出"+money);
this.money-=moey;
//没钱了,唤醒别的线程
this.notifyAll();
this.wait();
}else{
//当前等待,唤醒别的线程 先叫醒别人在打晕自己
this.notifyAll();
this.wait();
}
}
public synchronized void deposit(double money){
//判断是谁来存钱
String name= Thread.currentThread().getName();
//判断是否够钱
if(this.money==0){
//这样更容易不安全
System.out.print(name+"来存钱成功,存钱"+money);
this.money+=moey;
//有钱了,唤醒别的线程
this.notifyAll();
this.wait();
}else{
//有钱不存钱
this.notifyAll();
this.wait();
}
}
}
main(){
Account acc =new Account("132",0);
//创建两个取钱线程
new DrawThread(acc,"小明");
new DrawThread(acc,"小红");
//创建两个存钱线程
new SaveThread(acc,"亲爹");
new SaveThread(acc,"亲爹");
new SaveThread(acc,"亲爹");
}
取钱:
public class DrawThread extends Thread{
private Account acc;
public DrawThread(Account acc,String name){
super(name);
this.acc=acc;
}
@Override
public void run(){
while(true){
acc.drawMoney(1000);
try{
Thread.sleep(3000);
}catch(Exception e){
e.printStackTrace();
}
}
}
}
存钱:
public class SaveThread extends Thread{
private Account acc;
public DrawThread(Account acc,String name){
super(name);
this.acc=acc;
}
@Override
public void run(){
acc.deposit(1000);
try{
Thread.sleep(3000);
}catch(Exception e){
e.printStackTrace();
}
}
}
线程池(重点)
- 创建线程的开销很大,----线程池解决
线程池的接口:ExecutorService
得到线程池对象
1.使用ExecutorService的实现类ThreadPoolExecytor自创建一个线程
ExecutorService------->ThreadPoolExecytor
2.使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象
public ThreadPoolExecutor(int corePoolSize,//核心线程数
int maximumPoolSize,//最大线程数
long keepAliveTime,//线程空闲时间
TimeUnit unit,//时间单位
BlockingQueue<Runnable> workQueue,//任务队列
ThreadFactory threadFactory,//线程工厂
RejectedExecutionHandler handler//拒绝策略)
{
...
}
- corePoolSize 核心线程数,默认为1。
- 设置规则:
CPU密集型(CPU密集型也叫计算密集型,指的是运算较多,cpu占用高,读/写I/O(硬盘/内存)较少):corePoolSize = CPU核数 + 1
IO密集型(与cpu密集型相反,系统运作,大部分的状况是CPU在等I/O (硬盘/内存) 的读/写操作,此时CPU Loading并不高。):corePoolSize = CPU核数 * 2
- 设置规则:
- maximumPoolSize
- 最大线程数,默认为Integer.MAX_VALUE 一般设置为和核心线程数一样
- keepAliveTime
- 线程空闲时间,默认为60s,一般设置为默认60s
- unit
- 时间单位,默认为秒
- workQueue
- 队列,当线程数目超过核心线程数时用于保存任务的队列。(BlockingQueue workQueue)此队列仅保存实现Runnable接口的任务。(因为线程池的底层BlockingQueue的泛型为Runnable)
无界队列
队列大小无限制,常用的为无界的LinkedBlockingQueue,使用该队列作为阻塞队列时要尤其当心,当任务耗时较长时可能会导致大量新任务在队列中堆积最终导致OOM。阅读代码发现,Executors.newFixedThreadPool 采用就是 LinkedBlockingQueue,而博主踩到的就是这个坑,当QPS很高,发送数据很大,大量的任务被添加到这个无界LinkedBlockingQueue 中,导致cpu和内存飙升服务器挂掉。
当然这种队列,maximumPoolSize 的值也就无效了。当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。
有界队列
当使用有限的 maximumPoolSizes 时,有界队列有助于防止资源耗尽,但是可能较难调整和控制。常用的有两类,一类是遵循FIFO原则的队列如ArrayBlockingQueue,另一类是优先级队列如PriorityBlockingQueue。PriorityBlockingQueue中的优先级由任务的Comparator决定。
使用有界队列时队列大小需和线程池大小互相配合,线程池较小有界队列较大时可减少内存消耗,降低cpu使用率和上下文切换,但是可能会限制系统吞吐量。
同步移交队列
如果不希望任务在队列中等待而是希望将任务直接移交给工作线程,可使用SynchronousQueue作为等待队列。SynchronousQueue不是一个真正的队列,而是一种线程之间移交的机制。要将一个元素放入SynchronousQueue中,必须有另一个线程正在等待接收这个元素。只有在使用无界线程池或者有饱和策略时才建议使用该队列
- 队列,当线程数目超过核心线程数时用于保存任务的队列。(BlockingQueue workQueue)此队列仅保存实现Runnable接口的任务。(因为线程池的底层BlockingQueue的泛型为Runnable)
- threadFactory
- 线程工厂,用来创建线程。
为了统一在创建线程时设置一些参数,如是否守护线程,线程一些特性等,如优先级。通过这个TreadFactory创建出来的线程能保证有相同的特性。
它是一个接口类,而且方法只有一个,就是创建一个线程。
如果没有另外说明,则在同一个ThreadGroup 中一律使用Executors.defaultThreadFactory() 创建线程,并且这些线程具有相同的NORM_PRIORITY 优先级和非守护进程状态。
通过提供不同的 ThreadFactory,可以改变线程的名称、线程组、优先级、守护进程状态,等等。
如果从newThread 返回 null 时ThreadFactory 未能创建线程,则执行程序将继续运行,但不能执行任何任务
- 线程工厂,用来创建线程。
- handler
- 拒绝策略,默认是AbortPolicy,会抛出异常。
当线程数已经达到maxPoolSize,且队列已满,会拒绝新任务。
当线程池被调用shutdown()后,会等待线程池里的任务执行完毕再shutdown。如果在调用shutdown()和线程池真正shutdown之间提交任务,会拒绝新任务。
AbortPolicy 丢弃任务,抛运行时异常。
CallerRunsPolicy 由当前调用的任务线程执行任务。
DiscardPolicy 忽视,什么都不会发生。
DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务。
- 拒绝策略,默认是AbortPolicy,会抛出异常。
核心线程--临时线程
临时线程什么时候创建?
新任务提交任务是发现核心任务都在忙时,任务队列也满了,并且可以创建临时线程,此时会创建临时线程
什么时候会开始拒绝任务?
核心线程和临时线程都在忙,任务队列也满了,新的任务会被拒绝。
线程池处理Runable、Callable任务
方法:
- void execute(Runnable command) 执行任务、命令,没返回值--一般Runable任务
- Future submit(Callable task)执行任务、命令,又返回值,--一般Callable任务
- void shutdown()等任务执行完毕后关闭线程
- List shutdownNow()立即关闭线程,停止执行的任务,并返回队列中未执行的任务
新任务的拒绝策略:
ThreadPoolExecutor.AborPolicy 丢弃任务并抛出RejectedExccutionException默认
ThreadPoolExecutor.DiscardPolicy丢弃任务,但是不抛出异常不推荐
ThreadPoolExecutor.DiscardOldestPolicy抛弃队列中等待最久的任务,然后把当前任务加入到队列中
ThreadPoolExecutor.CallerRunsPolicy由主线程负责调用任务的run()方法从绕过线程池执行
public class TreadPoll{
main(){
//创建线程池对象
ExcutorService pool =new ThreadPoolExcutor(3,5,6,TimeUnit.SECONDS,new ArrayBlockingQueue<>(5),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AborPolicy() )
}
//模拟线程处理
Runnable target =new MyRunnable();
pool.execute(target);
pool.execute(target);
pool.execute(target);
//任务队列
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);
//创建临时线程
pool.execute(target);
pool.execute(target);
//拒绝策略触发
pool.execute(target);
//关闭线程池(开发中一般不会使用)
pool.shutdownNow();
pool.shutdown();
}public class MyRunnable implement Runnable{
@Override
public void run(){
for(int i =0 ;i<5;i++){
System.out.print(Thread.cuurentThread().getNmae()+"编写了hello world");
}
try{
Thread.sleep(3000);
}catch(Exception e){
e.printStackTrace();
}
}
}
处理Callable任务
public class TreadPoll{
main(){
//创建线程池对象
ExcutorService pool =new ThreadPoolExcutor(3,5,6,TimeUnit.SECONDS,new ArrayBlockingQueue<>(5),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AborPolicy() )
}
Future<String> f1=pool.submit(new MyCallable(100));
Future<String> f2=pool.submit(new MyCallable(100));
Future<String> f3=pool.submit(new MyCallable(100));
String rs = f1.get();
String rs2 =f2.get();
String rs3 = f3.get();
}
Executors工具类
- public static ExecutorsService newCachedThreadPool() 线程池的数量随着任务的增加而增加,如果执行完毕空闲一段时间会被回收
- public static ExecutorsService newFixedThreadPool(int nThreads)创建固定的线程池,如果某个线程因为执行异常而结束,那么线程池会补充一个新的线程替代它
- public static ExecutorsService newSingleThreadExecutor()创建一个线程池对象,如果线程出现异常而结束,那么线程池会补充一个新的线程
- public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)创建一个线程池,可以实现在给定的延迟后运行任务,或者定期执行任务
Executors底层也是实现 ThreadPoolExecutor实现的
main(){
ExecutorService pool=Executors.ExecutorsService newFixedThreadPool(3);
}
但是大型的并发项目会出现系统分险
- 内存溢出,线程的内存溢出
- 任务没有限制
定时器
方式一:Timer
方法二:newScheduledThreadPool
Timer
构造器:
- public Timer() 创建定时器对象
方法:
- public void schedule(TimerTask task,long delay, long period) 开启一个定时器执行task任务
问题:
Timer是单线程,存在延时与定时器的时间有出入的情况
可能因为其中的某个任务的异常使Timer线程死掉,从而影响后续的任务执行
main(){
Timer timer =new Timer();
timer.scedule(new TimerTask(){
@Oberride
public void run(){
//业务
System.out.print("定时器");
}
},3000,2000)
}//3秒延时调用2秒周期调用
newScheduledThreadPool;
JDK1.5引入的并发包
Executors
- public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)创建一个线程池,可以实现在给定的延迟后运行任务,或者定期执行任务
方法:
- public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initalDelay ,long period,TimeUnit unit) 周期调度方法
优点:
基于线程池执行,某个任务情况不会影响其他的定时任务的执行
main(){
ScheduledExecutorService pool =Executor.newScheduledThreadPool(3);
pool.scheduleAtFixedRate(new TimeTrask(){
@Override
public void run (){
//业务
}
},0,2,TimeUnit.SECONDS);
}//初始化延迟事件0秒 周期延迟2秒单位秒
并发和并行
多线程:并发和并行同时进行
生命周期
Thread类的枚举类的6中状态
- new 新建状态
- Runnable 可运行状态start()
- Blocked锁阻塞状态
- waiting无限等待状态wait()
- Timed Waiting (计时等待)
- sleep状态的线程好了不用强锁、、、我不要脸
- wait状态的线程的时间到了,并得到锁,可以跑
- wait状态时间没到,被唤醒,并得到锁可以跑
- wait没有得到锁会进入锁阻塞
- Teminated被终止状态
javaEE(多线程、线程通信、线程安全、线程池、线程池工具)的更多相关文章
- Java并发编程扩展(线程通信、线程池)
之前我说过,实现多线程的方式有4种,但是之前的文章中,我只介绍了两种,那么下面这两种,可以了解了解,不懂没关系. 之前的文章-->Java并发编程之多线程 使用ExecutorService.C ...
- java多线程-线程通信
线程通信的目标是使线程间能够互相发送信号.另一方面,线程通信使线程能够等待其他线程的信号. 通过共享对象通信 忙等待 wait(),notify()和 notifyAll() 丢失的信号 假唤醒 多线 ...
- 多线程一共就俩问题:1.线程安全(访问共享数据) 2.线程通信(wait(),notify())
多线程一共就俩问题:1.线程安全(访问共享数据) 2.线程通信(wait(),notify()) 1.线程安全,无非就是加锁,访问共享资源时,synchronized 2.线程通信,就是控制各个线程之 ...
- Java多线程-同步:synchronized 和线程通信:生产者消费者模式
大家伙周末愉快,小乐又来给大家献上技术大餐.上次是说到了Java多线程的创建和状态|乐字节,接下来,我们再来接着说Java多线程-同步:synchronized 和线程通信:生产者消费者模式. 一.同 ...
- JAVA基础知识之多线程——线程通信
传统的线程通信 Object提供了三个方法wait(), notify(), notifyAll()在线程之间进行通信,以此来解决线程间执行顺序等问题. wait():释放当前线程的同步监视控制器,并 ...
- java多线程——线程通信
一.线程通信目标 1.线程通信的目标是使线程间能够互相发送信号 2.线程通信使线程能够等待其他线程的信号 二.几种方式 1.通过共享对象 2.忙等待 线程 B 运行在一个循环里,以等待信号 (不释放c ...
- Java线程同步和线程通信
一.线程同步 当多个线程访问同一个数据时,非常容易出现线程安全问题.这时候就需要用线程同步. 不可变类总是线程安全的,因为它的对象状态是不可改变的,但可变类对象需要额外的方法来保证线程安全. 1.同步 ...
- JavaSE——线程通信
线程通信: 如果线程A和线程B持有同一个MyObject类的对象object,这两个线程会去调用不同的方法,但是它们是同步执行的,比如:线程B需要等待线程A执行完了methodA()方法之后,它才能执 ...
- Python网络编程(线程通信、GIL、服务器模型)
什么是进程.进程的概念? 进程的概念主要有两点: 第一,进程是一个实体.每一个进程都有它自己的地址空间, 一般情况下,包括文本区域(text region).数据区域(data region)和堆栈( ...
- Java-多线程第三篇3种创建的线程方式、线程的生命周期、线程控制、线程同步、线程通信
1.Java使用Thread类代表线程. 所有的线程对象必须是Thread类或其子类的实例. 当线程继承Thread类时,直接使用this即可获取当前线程,Thread对象的getName() ...
随机推荐
- 深度学习之tensorflow2实战:多输出模型
欢迎来到CNN实战,尽管我们刚刚开始,但还是要往前看!让我们开始吧! 数据集 链接:https://pan.baidu.com/s/1zztS32iuNynepLq7jiF6RA 提取码:ilxh,请 ...
- jquery组件解决option选项框的样式自定义方案
记录一下今天工作中遇到的一个需求和自行找到的解决办法 需求: 在原始的select选项框中的增加一个标识.(我想增加一个具有样式的span元素,试了半天在option里无法添加span,更别说具有样式 ...
- 关于linux更改root用户下面的鼠标样式
前言 这几天一直研究关于 lightmd 显示管理器(也就是刚进入系统的用户登录界面)的主题配置问题,我发现鼠标样式和登录个人用户的鼠标样式不一样(之前我也发现了,懒得捣鼓)今天抽出时间研究了一下,这 ...
- 深入浅出学习透析 Nginx 服务器的基本原理和配置指南「运维操作实战篇」
Nginx前提回顾 Nginx 是一个高性能的 Web 和反向代理服务器, 它具有有很多非常优越的特性: Web服务器:相比 Apache,Nginx 使用更少的资源,支持更多的并发连接,体现更高的效 ...
- Android 内存缓存框架 LruCache 的实现原理,手写试试?
本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 前言 大家好,我是小彭. 在之前的文章里,我们聊到了 LRU 缓存淘汰算法,并且分析 Java 标准库中支持 ...
- go-carbon 1.5.3 版本发布, 修复已知 bug 和新增俄罗斯语翻译文件
carbon 是一个轻量级.语义化.对开发者友好的golang时间处理库,支持链式调用. 目前已被 awesome-go 收录,如果您觉得不错,请给个star吧 github.com/golang-m ...
- 一文带你搞懂 Google 发布的新开源项目 GUAC
随着软件供应链攻击的显著增加,以及 Log4j 漏洞带来的灾难性后果和影响,软件供应链面临的风险已经成为网络安全生态系统共同关注的最重要话题之一.根据业内权威机构 Sonatype 发布的2022软件 ...
- CH392/CH395常见问题解决方法指南
CH395 问题 1: CH395 初始化失败.解答: 1.首先检查"check_exist"命令,正常情况下 CH395 会将该命令的输入值按位取反后输出,若该命令不正常,则说明 ...
- MySQL优化三,SQL语法
## 1.3.MySQL调优 前言:在前面的基础之上把相应的数据库表设计得很完美,建立了好用的索引,如果SQL语句中没有使用到相应索引的话,也是白搭,如何设计好一点的SQL,则是一大问题 ### 1. ...
- [LeetCode]对角线遍历
题目 代码 class Solution { public: vector<int> findDiagonalOrder(vector<vector<int>>&a ...