在一个高并发的网站中,多线程是必不可少的。
1、提高前端请求的响应速度。当我们执行一个比较耗时的方法时,http请求得不到响应甚至会超时,这时如果业务上允许数据的延迟,我们可以使用多线程来进行处理比较耗时的方法。这样前端发送了请求,后端令开启了一个线程去处理任务,就不会阻塞主线程了。
2、减清服务器的压力。包括我们的web容器,如tomcat、jetty等,还有数据库服务器等。因为我们使用了多线程,并且线程池大小有限制,如30,那么同时请求数据库的链接就限制为30了,也就是说能够同时执行方法的线程只有30个,其余的任务都放在我们线程的任务队列了,这样数据库就不会被突然上来的请求给压垮了。当然对于缓解数据库压力来说,更建议使用消息队列,使用消息队列我们可以攒数据进行批量提交,而仅仅使用多线程,则不好实现攒数据的过程。
3、提高处理能力,增加性能,充分利用服务器资源。如我们要将三个表里的数据加载到缓存,最简单的我们一个表开启一个线程,共用三个线程去加载缓存则比用一个线程去挨着遍历三个表的数据高效的多。
在使用Thread类对象作为新启动的线程的时候,可以写以下的代码:
new Thread(){
@Override
public void run() {
for (int i=0;i<10;i++) {
try {
System.out.println("thread_01");
Thread.sleep(100);//Thread类的静态方法,睡眠100毫秒
}catch (Exception e){
e.printStackTrace();
}
}
}
}.start();
上面的代码通过new Thread()建立了一个线程并立即启动线程。
一般常用的两种方法建立线程,一个是继承Thread类,另一个是实现Runnable接口。
继承Thread类:
class ThreadDemo extends Thread{
@Override
public void run(){
for(int i=0;i<10;i++){
try {
Thread.sleep(100);
System.out.println("Thread_01");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
实现Runnable接口:
class ThreadDemo2 implements Runnable{
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println("thread_02");
}
}
}
因为java中类的继承是单一继承的,所以为了继承其他类,一般使用实现Runnable接口的方式写一个线程。
再说说多线程的作用,程序有一个主线程在执行代码,在写了一个线程的时候,这个线程会和主线程一起运行,直至run()里面的代码执行结束自动关闭(即使主线程关闭,子线程也会继续执行直至结束)。
在启动一个线程的时候,使用的是start()方法,下面是两种方法的区别:
1) start:
用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,这里方法 run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。
2) run:
run()方法只是类的一个普通方法而已,如果直接调用run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。
总结:调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。这两个方法应该都比较熟悉,把需要并行处理的代码放在run()方法中,start()方法启动线程将自动调用 run()方法,这是由jvm的内存机制规定的。并且run()方法必须是public访问权限,返回值类型为void。
在启动线程的时候,如果是用继承Thread类的话,就可以直接用
ThreadDemo threadDemo=new ThreadDemo();
threadDemo.start();//这两句启动
而通过实现Runnable接口,启动时用:
ThreadDemo2 aDemo=new ThreadDemo2();
new Thread(aDemo).start();
下面通过一个简单的例子说明线程的作用:
public class test{
public static void main(String[] args) {
ThreadDemo1 aDemo=new ThreadDemo1();
ThreadDemo2 bDemo2=new ThreadDemo2();
// aDemo.run();
// bDemo2.run();run方法并没有起到线程的作用!
new Thread(aDemo).start();
new Thread(bDemo2).start();
System.out.println("两个线程后面的操作");
}
class ThreadDemo1 implements Runnable{
@Override
public void run() {
for(int i=0;i<5;i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread_01");
}
}
}
class ThreadDemo2 implements Runnable{
@Override
public void run() {
for(int i=0;i<5;i++){
System.out.println("thread_02");
}
}
}
}
控制台打印的结果为:
两个线程后面的操作
thread_02
thread_02
thread_02
thread_02
thread_02
thread_01
thread_01
thread_01
thread_01
thread_01
明明是后面的代码却先打印出来,“两个线程后面的操作”这句话是和线程2一起同步执行的,它先被打印出来应该是因为它是在主线程里面的操作的原因,而线程2比线程1先打印就证明了3个线程是同步执行的。