1、为什么要使用多线程
选择多线程的原因,就是因为快。举个例子:
如果要把1000块砖搬到楼顶,假设到楼顶有几个电梯,你觉得用一个电梯搬运快,还是同时用几个电梯同时搬运快呢?这个电梯就可以理解为线程。
所以,我们使用多线程就是因为: 在正确的场景下,设置恰当数目的线程,可以用来程提高序的运行速率。更专业点讲,就是充分地利用CPU和I/O的利用率,提升程序运行速率。
当然,有利就有弊,多线程场景下,我们要保证线程安全,就需要考虑加锁。加锁如果不恰当,就很很耗性能。
2. 创建线程有几种方式?
Java中创建线程主要有以下这几种方式:
-
定义
Thread
类的子类,并重写该类的run
方法 -
定义
Runnable
接口的实现类,并重写该接口的run()
方法 -
定义
Callable
接口的实现类,并重写该接口的call()
方法,一般配合Future
使用 -
线程池的方式
2.1 定义Thread类的子类,并重写该类的run方法
public class ThreadTest { public static void main(String[] args) { Thread thread = new MyThread(); thread.start(); } } classMyThread extends Thread { @Override public void run() { System.out.println
2.2 定义Runnable接口的实现类,并重写该接口的run()方法
public class ThreadTest { public static void main(String[] args) { MyRunnable myRunnable = new MyRunnable(); Thread thread = newThread(myRunnable); thread.start(); } } class MyRunnableimplements Runnable { @Override public void run() { System.out.println
2.3 定义Callable接口的实现类,并重写该接口的call()方法
如果想要执行的线程有返回,可以使用Callable
。
public class ThreadTest { public static void main(String[] args) throws ExecutionException, InterruptedException { MyThreadCallable mc = new MyThreadCallable(); FutureTask<Integer> ft = newFutureTask<>(mc); Thread thread = new Thread(ft); thread.start(); System.out.println(ft.get()); } } classMyThreadCallable implements Callable { @Override public Stringcall()throws Exception
2.4 线程池的方式
日常开发中,我们一般都是用线程池的方式执行异步任务。
public class ThreadTest { public static void main(String[] args) throws Exception { ThreadPoolExecutor executorOne = newThreadPoolExecutor(5, 5, 1, TimeUnit.MINUTES, newArrayBlockingQueue<Runnable>(20), new CustomizableThreadFactory("Tianluo-Thread-pool")); executorOne.execute(() -> { System.out.println; }); //关闭线程池 executorOne.shutdown(); } } 复制代码
3. start()方法和run()方法的区别
其实start
和run
的主要区别如下:
-
start
方法可以启动一个新线程,run
方法只是类的一个普通方法而已,如果直接调用run
方法,程序中依然只有主线程这一个线程。 -
start
方法实现了多线程,而run
方法没有实现多线程。 -
start
不能被重复调用,而run
方法可以。 -
start
方法中的run
代码可以不执行完,就继续执行下面的代码,也就是说进行了线程切换。然而,如果直接调用run
方法,就必须等待其代码全部执行完才能继续执行下面的代码。
大家可以结合代码例子来看看哈~
public class ThreadTest { public static void main(String[] args){ Thread t=new Thread(){ public void run(){ pong(); } }; t.start(); t.run(); t.run(); System.out.println"+ Thread.currentThread().getName()); } static void pong(){ System.out.println+ Thread.currentThread().getName()); } }
4. 线程和进程的区别
-
进程是运行中的应用程序,线程是进程的内部的一个执行序列
-
进程是资源分配的最小单位,线程是CPU调度的最小单位。
-
一个进程可以有多个线程。线程又叫做轻量级进程,多个线程共享进程的资源
-
进程间切换代价大,线程间切换代价小
-
进程拥有资源多,线程拥有资源少地址
-
进程是存在地址空间的,而线程本身无地址空间,线程的地址空间是包含在进程中的
举个例子:
你打开QQ,开了一个进程;打开了迅雷,也开了一个进程。
在QQ的这个进程里,传输文字开一个线程、传输语音开了一个线程、弹出对话框又开了一个线程。
所以运行某个软件,相当于开了一个进程。在这个软件运行的过程里(在这个进程里),多个工作支撑的完成QQ的运行,那么这“多个工作”分别有一个线程。
所以一个进程管着多个线程。
通俗的讲:“进程是爹妈,管着众多的线程儿子”...
5. 说一下 Runnable和 Callable有什么区别?
-
Runnable
接口中的run()
方法没有返回值,是void
类型,它做的事情只是纯粹地去执行run()
方法中的代码而已; -
Callable
接口中的call()
方法是有返回值的,是一个泛型。它一般配合Future、FutureTask
一起使用,用来获取异步执行的结果。 -
Callable
接口call()
方法允许抛出异常;而Runnable
接口run()
方法不能继续上抛异常;
大家可以看下它俩的API
:
@FunctionalInterface public interface Callable<V> { /** * 支持泛型V,有返回值,允许抛出异常 */ V call()