1、什么是多线程、什么场景下使用多线程?
要学习多线程,首先,我们要学习关于多线程的基本概念。 - 程序:为完成某任务,用某种语言编写所组成的静态代码
- • 进程:正在运行的程序,也就是加载到内存中。一个进程有一个方法区和一个堆
• 线程:一个程序内部的执行路径,一个进程通常会有多个线程。一个线程有一个虚拟机栈和一个程序计数器,也就是进程下的线程共享该进程的方法区和堆区,且每个线程都有自己独有的虚拟机栈和程序计数器(PC)(拓展:JVM调优也就是调整共享的这部分) • 并行:多个CPU同时执行多个任务
• 并发:一个CPU“同时”(采用时间片)执行多个任务,如:秒杀、多个人做同一个事
• 多线程优点:提高程序响应(对图形化界面更有意义,提升用户体验)、提高CPU利用率、改善程序结构,利于理解与修改
• 多线程场景:程序需要同时执行两个或多个任务、需要实现一些等待任务(文件读写、网络操作、搜索等)、需要一些后台运行的程序;现在的cpu基本是多核,我通常利用多线程利用并行,提升cpu性能。
2、如何创建多线程? 实现线程,首先,我们需要创建线程,在java基础中,我们会学习继承Thread和实现Runnable接口,还有后面加入的其他相关方式,但是其归根还是利用同一种方式实现。 1、实现 Runnable 接口
public class RunnableThread implements Runnable {
@Override
public void run() {
System.out.println("start the thread");
}
public static void main(String[] args) {
//创建实现runnable的实例
RunnableThread runnableThread = new RunnableThread();
//将创建的实例创建thread,即可开启线程
new Thread(runnableThread).start();
}
}
方式1中RunnableThread实现Runnable接口,重写run方法;将实现runnable 的接口传进Threa类,即可实现。
2、继承Thread
public class ExecuteThread extends Thread {
@Override
public void run() {
System.out.println("开启线程");
}
public static void main(String[] args) {
new ExecuteThread().start();
}
}
ExecuteThread继承Thread并重写run方法, 3、线程池创建线程 在使用线程池ThreadPoolExecutor或者其他线程池中,其构造函数会指定默认线程工厂DefaultThreadFactory 。 那DefaultThreadFactory 如何实现多线程的创建。
static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
多线程本质上也是通过newThread实现线程的创建。
4、有返回值的 Callable 创建线程
public class CallableThread implements Callable {
@Override
public Object call() throws Exception {
return "callable";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future submit = executorService.submit(new CallableThread());
System.out.println(submit.get());
System.out.println("main end");
}
}
第 4 种线程创建方式是通过有返回值的 Callable 创建线程,Runnable 创建线程是无返回值的,而 Callable 和与之相关的 Future、FutureTask,它们可以把线程执行的结果作为返回值返回,
5、总结
5.1、实现线程只有一种方式
多线程的实现都是通过Runnable 接口或继承 Thread 类实现的。,其他形式只不过对其进行了包装。
5.2、实现 Runnable 接口比继承 Thread 类实现线程要好
- 1. 在某些场合会带来性能的提升,继承Thread,每次执行任务时需要创建对象,特别当任务耗时特别短,创建对象的开销可能比任务执行更大。当使用runnable时可以将任务丢给线程池,复用线程来提升性能。
- 2. 由于java语言的特性,类只能单继承,可以多实现;使用继承不便于代码的扩展。
3、多线程有几种状态
- 1. New(新创建)当我们通过new创建线程之后,调用start方法之前的状态
2. Runnable(可运行)当线程调用start方法之后,线程处于ready或者running状态,这取决cpu的任务调度机制
- 3. Blocked(被阻塞)当我们加了锁,其便处于堵塞状态,需要等待其他线程将该锁释放,才能可能从Blocked变换为Runnable。
- 4. Waiting(等待) 当线程Runnable状态通过wait()、sleep()、join()、LockSupport.park()当前线程便处于堵塞状态,通过notify或者LockSupport.unpark()
- 5. Timed Waiting(计时等待)当线程Runnable状态通过wait(timeout)、sleep(timeout)、join(timeout)、LockSupport.park(timeout)当前线程便处于堵塞状态,通过notify或者LockSupport.unpark()
- 6. Terminated(被终止)即线程执行结束