public class ZhouyuThread extends Thread{
public static void main(String[] args) {
ZhouyuThread thread = new ZhouyuThread();
thread.start();
}
@Override
public void run() {
System.out.println("hello zhouyu");
}
}
总结:重写的是run()方法,而不是start()方法,但是占用了继承的名额,Java中的类是单继承的。
讲到单继承,我们应该注意,java中的接口是可以多继承的
/**
* 作者:周瑜大都督
*/
public class ZhouyuThread implements Runnable{
public static void main(String[] args) {
Thread thread = new Thread(new ZhouyuThread());
thread.start();
}
public void run() {
System.out.println("hello zhouyu");
}
}
总结:实现Runnable接口,实现run()方法,使用依然要用到Thread,这种方式更常用
有时候,我们会直接使用匿名内部类的方式或者Lambda表达式的方式:
public class ZhouyuThread {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
public void run() {
System.out.println("hello zhouyu");
}
});
thread.start();
}
}
public class ZhouyuThread {
public static void main(String[] args) {
Thread thread = new Thread(() -> System.out.println("hello zhouyu"));
thread.start();
}
}
public class ZhouyuThread implements Callable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask futureTask = new FutureTask<>(new ZhouyuThread());
Thread thread = new Thread(futureTask);
thread.start();
String result = futureTask.get();
System.out.println(result);
}
public String call() {
return "hello zhouyu";
}
}
总结:实现Callable接口,实现call()方法,得使用Thread+FutureTask配合,这种方式支持拿到异步执行任务的结果
/**
* 作者:周瑜大都督
*/
public class ZhouyuThread implements Runnable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(new ZhouyuThread());
}
public void run() {
System.out.println("hello zhouyu");
}
}
总结:实现Callable接口或者Runnable接口都可以,由ExecutorService来创建线程
注意,工作中不建议使用Executors来创建线程池
以上几种方式,底层都是基于Runnable。
当我们使用Executors创建FixedThreadPool时,对应的构造方法为:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
发现创建的队列为LinkedBlockingQueue,是一个无界阻塞队列,如果使用该线程池执行任务,如果任务过多就会不断的添加到队列中,任务越多占用的内存就越多,最终可能耗尽内存,导致OOM。
当我们使用Executors创建SingleThreadExecutor时,对应的构造方法为:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
也是LinkedBlockingQueue,所以同样可能会耗尽内存。
除开有可能造成OOM之外,我们使用Executors来创建线程池也不能自定义线程的名字,不利于排查问题,所以建议直接使用ThreadPoolExecutor来定义线程池,这样可以灵活控制。
Accept new tasks and process queued tasks
表示线程池正常运行,既能接受新任务,也会正常处理队列中的任务
Don't accept new tasks, but process queued tasks
当调用线程池的shutdown()方法时,线程池就进入SHUTDOWN状态,表示线程池处于正在关闭状态,此状态下线程池不会接受新任务,但是会继续把队列中的任务处理完
Don't accept new tasks, don't process queued tasks, and interrupt in-progress tasks
当调用线程池的shutdownnow()方法时,线程池就进入STOP状态,表示线程池处于正在停止状态,此状态下线程池既不会接受新任务了,也不会处理队列中的任务,并且正在运行的线程也会被中断
All tasks have terminated, workerCount is zero, the thread transitioning to state TIDYING will run the terminated() hook method
线程池中没有线程在运行后,线程池的状态就会自动变为TIDYING,并且会调用terminated(),该方法是空方法,留给程序员进行扩展。
terminated() has completed
terminated()方法执行完之后,线程池状态就会变为TERMINATED
sychronized |
ReentrantLock |
Java中的一个关键字 |
JDK提供的一个类 |
自动加锁与释放锁 |
需要手动加锁与释放锁 |
JVM层面的锁 |
API层面的锁 |
非公平锁 |
公平锁或非公平锁 |
锁的是对象,锁信息保存在对象头中 |
int类型的state标识来标识锁的状态 |
底层有锁升级过程 |
没有锁升级过程 |
对于还在正常运行的系统:
对于已经发生了OOM的系统:
首先不管是公平锁和非公平锁,它们的底层实现都会使用AQS来进行排队,它们的区别在于线程在使用lock()方法加锁时:
公平锁的底层执行流程:
非公平锁的底层执行流程:
另外,不管是公平锁还是非公平锁,一旦没竞争到锁,都会进行排队,当锁释放时,都是唤醒排在最前面的线程,所以非公平锁只是体现在了线程加锁阶段,而没有体现在线程被唤醒阶段,ReentrantLock是可重入锁,不管是公平锁还是非公平锁都是可重入的。
一个Tomcat中可以部署多个应用,而每个应用中都存在很多类,并且各个应用中的类是独立的,全类名是可以相同的,比如一个订单系统中可能存在com.zhouyu.User类,一个库存系统中可能也存在com.zhouyu.User类,一个Tomcat,不管内部部署了多少应用,Tomcat启动之后就是一个Java进程,也就是一个JVM,所以如果Tomcat中只存在一个类加载器,比如默认的AppClassLoader,那么就只能加载一个com.zhouyu.User类,这是有问题的,而在Tomcat中,会为部署的每个应用都生成一个类加载器实例,名字叫做WebAppClassLoader,这样Tomcat中每个应用就可以使用自己的类加载器去加载自己的类,从而达到应用之间的类隔离,不出现冲突。另外Tomcat还利用自定义加载器实现了热加载功能。
表数据:
CREATE TABLE `t1` (
a int primary key,
b int ,
c int ,
d int ,
e varchar(20)
) ENGINE=InnoDB ;
insert into t1 values(4,3,1,1,'d');
insert into t1 values(1,1,1,1,'a');
insert into t1 values(8,8,8,8,'h');
insert into t1 values(2,2,2,2,'b');
insert into t1 values(5,2,3,5,'e');
insert into t1 values(3,3,2,2,'c');
insert into t1 values(7,4,5,5,'g');
insert into t1 values(6,6,4,4,'f');
索引情况:
a字段是主键,对应主键索引,bcd三个字段组成一个联合索引,e字段一个索引
去掉b=1的条件就不符合最左匹配原则了,导致所有失效
不用like能走索引:
正常使用like:
不正确使用like:
e字段的类型是vachar,下面这个sql需要把e字段中的字符转换成数字,会导致索引失效
b=1可以走索引,b<>1就不能走索引
就算利用索引,但是由于是select * 所以需要回表,而且回表成本比较高,所以不会走索引。
如果是select b就需要回表了,就会选择走索引
新增一些数据:
insert into t1 values(10,3,1,1,'d');
insert into t1 values(20,1,1,1,'a');
insert into t1 values(15,8,8,8,'h');
insert into t1 values(18,2,2,2,'b');
insert into t1 values(14,2,3,5,'e');
insert into t1 values(13,3,2,2,'c');
insert into t1 values(17,4,5,5,'g');
insert into t1 values(22,6,4,4,'f');
@RestController
public class ZhouyuController {
@GetMapping("/test")
public String test() {
return "zhouyu";
}
}
/**
* 作者:周瑜大都督
*/
@Component("/beanNameController")
public class ZhouyuBeanNameController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
response.getWriter().println("ZhouyuBeanNameController");
return null;
}
}
/**
* 作者:周瑜大都督
*/
@Component("/beanNameHandler")
public class ZhouyuBeanNameHandler implements HttpRequestHandler {
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.getWriter().println("ZhouyuBeanNameHandler");
}
}
@SpringBootApplication
public class MyApplication {
@Bean
public RouterFunction routerFunction(){
return route()
.GET("/getUserName", request -> ServerResponse.ok().body("zhouyu"))
.GET("/getUserAge", request -> ServerResponse.ok().body("88"))
.build();
}
public static void main(String[] args) {
SpringApplication.run(MyApplication.class);
}
}