1、什么是多线程
进程中有多个线程,对于多核cpu来说,多个线程可以同时执行,提高了cpu的利用率。
2、为什么要用多线程
(1)从计算机底层来说:线程是轻量级的进程,是程序的最小执行单元;对于多核cpu
而言,多个线程可以同时执行,减少了线程间切换的开销,即提高了cpu
的利用率,同时也提高了程序的效率。
(2)从当前业务场景来说:目前动不动就要求百万级、千万级的并发量,而多线程正是开发高并发系统的基础。
(3)在单核时代,多线程主要是用来提高cpu
和IO设备
的综合利用率。只有一个线程时,当cpu
计算时,IO
空闲,当IO
操作时,cpu
空闲,cpu
和IO
的利用率是50%。但是如果有两个线程,一个线程执行cpu
计算,一个线程执行IO
操作,在理想情况下可以说利用率达到100%了。
(4)在多核时代,多线程主要是提高cpu
的利用率,多个线程可以让多个cpu
同时被使用,提高了cpu
的利用率,同时也提高了程序的性能。
3、怎么使用多线程
(1)继承Thread
类,实现多线程
package com.kk.first.thread;
public class TestThread extends Thread {
private String name;
public TestThread(String threadName) {
this.name = threadName;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(name + "-" + i);
}
}
}
public class Test {
public static void main(String[] args) throws InterruptedException, ExecutionException {
test();
}
public static void test() {
Thread t1 = new TestThread("线程1");
Thread t2 = new TestThread("线程2");
Thread t3 = new TestThread("线程3");
t1.start();
t2.start();
t3.start();
}
}
继承java.lang.Thread
的程序类,重写run()
方法,在run()
方法中执行多线程的逻辑。(run()
方法不能直接调用,启动线程必须通过start()
方法)
(2)实现Runnable
接口实现多线程
package com.kk.first.thread;
public class TestRunnable implements Runnable{
private String name;
public TestRunnable(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(name + "-" + i);
}
}
}
public class Test {
public static void main(String[] args) throws InterruptedException, ExecutionException {
test();
}
public static void test() {
Thread t1 = new Thread(new TestRunnable("线程1"));
Thread t2 = new Thread(new TestRunnable("线程2"));
Thread t3 = new Thread(new TestRunnable("线程3"));
t1.start();
t2.start();
t3.start();
}
}
虽然通过继承Thread
可以实现多线程,但是继承是有单继承限制的,不利于自定义的多线程类扩充其他的功能。而通过实现Runnable
接口则没有这个限制。而且从Thread
源码可以看出,Thread
也是实现了Runnable
接口,Thread
是Runnable
的子类。
多线程的设计用到了代理模式,用户自定义的线程主体只负责核心功能的实现,其他辅助逻辑则交给Thread来实现。
关于代理模式,可以看看这篇博文深入解析Java代理模式(静态代理和动态代理)。
(3)实现Callable接口实现多线程
package com.kk.first.thread;
import java.util.concurrent.Callable;
public class TestCallable implements Callable<String>{
@Override
public String call() throws Exception {
for (int i =0; i < 10; i++) {
System.out.println("线程执行" + i);
}
return "线程执行完成";
}
}
public class Test {
public static void main(String[] args) throws InterruptedException, ExecutionException {
test();
}
public static void test() throws InterruptedException, ExecutionException {
FutureTask ft = new FutureTask(new TestCallable());
Thread t = new Thread(ft);
t.start();
System.out.println(ft.get());
}
}
一般开发多线程都用Runnable
来实现,但是Runnable
有一个缺点,当线程执行完后,无法获取一个返回值。所有在jdk1.5
之后推出了java.util.concurrent.Callable
接口来实现多线程,定义Callable
的时候可以设置一个泛型,这个泛型就是返回值的类型。
通过Callable
实现多线程,重写call()
方法可以设置线程执行完后的返回值。
下篇博文:Java高并发之synchronized关键字