1.线程和进程的区别
当一个程序被运行,从磁盘加载这个程序到内存的过程,叫做开启了一个进程
一个进程可以包含一到多个线程
-
进程是正在运行程序的实例,进程中包含了线程,每个线程执行不同的任务
-
不同的进程使用不同的内存空间,在当前进程下的所有线程可以共享内存空间
-
线程更轻量,线程上下文切换成本一般要比进程上下文切换低
2. 并行和并发的区别
并发:同一时间应对多件事情的能力
并行:同一时间动手做多件事情的能力
-
在多核CPU下,并发是同一时间多个线程轮流使用一个或多个CPU
-
并行是4核CPU同时执行4个线程
3. 创建线程的方式有那些
-
继承Thread类实现
-
public class MyThread extends Thread{ @Override public void run() { System.out.println("继承Thread类"); } public static void main(String[] args) { //创建线程 MyThread t1 = new MyThread(); MyThread t2 = new MyThread(); //启动线程 t1.start(); t2.start(); } }
-
-
实现Runnable接口
-
public class MyRunnable implements Runnable{ @Override public void run() { System.out.println("实现Runnable接口"); } public static void main(String[] args) { MyRunnable myRunnable = new MyRunnable(); Thread t1 = new Thread(myRunnable); Thread t2 = new Thread(myRunnable); t1.start(); t2.start(); } }
-
-
实现Callable接口,这种方法创建线程有返回值
-
public class MyCallable implements Callable<String> { @Override public String call() throws Exception { System.out.println("实现Callable接口"); return "Callable"; } public static void main(String[] args) throws ExecutionException, InterruptedException { MyCallable mc = new MyCallable(); FutureTask<String> futureTask = new FutureTask<String>(mc); Thread t1 = new Thread(futureTask); t1.start(); String result = futureTask.get(); System.out.println(result); } }
-
-
线程池创建
-
public class MyExecutors implements Runnable { @Override public void run() { System.out.println("MyRunnable...run..."); } public static void main(String[] args) { //创建线程池对象 ExecutorService threadPool = Executors.newFixedThreadPool(3); threadPool.submit(new MyExecutors()); //关闭线程池 threadPool.shutdown(); } }
-
3.1 Runnable 和 Callable接口的区别
-
Runnable接口的run方法没有返回值
-
Callable接口的call方法有返回值,是个泛型,和Future、FutureTask配合使用
-
Callable接口的call方法允许抛出异常,而Runnable的run方法不能抛出异常,只能使用try...catch,不能向上抛出
3.2 run() 方法和 start() 方法有什么区别
-
run() 方法是一个普通的方法,没有开启一个线程,是串行的,可以执行多次
-
start() 方法是开启一个线程,不能开启多次
4. 线程中有那些状态
-
尚未启动状态(NEW)
-
有执行资格没有执行权(就绪状态)
-
有执行资格有执行权(运行)
-
线程死亡,变成垃圾(死亡状态)
-
阻塞状态
-
等待状态(wait)
-
计时等待(sleep)
-
创建线程对象是新建状态
-
调用了start() 方法转变为可执行状态
-
线程获取到了CPU的执行权,执行结束是终止状态
-
在可执行状态的过程中,如果没有获取CPU的执行权,可能会切换为其他状态
-
如果没有获取到锁,进入阻塞状态,获取到锁之后切换为可执行状态
-
线程调用wait()方法进入等待状态,其他线程调用notify()方法唤醒后切换为可执行状态
-
线程调用sleep()方法进入计时等待状态,到时间后切换为可执行状态
-
5. 新建T1、T2、T3三个线程,怎样保证线程按顺序执行
使用线程中的join方法
只要调用join方法的线程执行完毕后才可往下执行
public static void main(String[] args) { Thread t1 = new Thread(()->{ System.out.println("t1"); }); Thread t2 = new Thread(()->{ try { t1.join(); //只有t1线程执行完毕才可往下执行 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("t2"); }); Thread t3 = new Thread(()->{ try { t2.join();//只要t2线程执行完毕才可往下执行 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("t3"); }); t1.start(); t2.start(); t3.start(); }
6. notify() 和 notifyAll() 的区别
-
notify:随机唤醒一个线程
-
notifyAll:唤醒所有线程
7. java中的wait方法和sleep方法有什么不同
共同点:
wait() wait(long) sleep(long) 的效果都是让当前线程暂时放弃CPU的使用权,进入阻塞状态
不同点:
-
方法归属不同
-
sleep是Tread的静态方法
-
wait是Object的成员方法,每个对象都有
-
-
醒来时机不同
-
执行sleep和wait的线程都会在等待相应毫秒后醒来
-
wait(long) wait()可以被notify唤醒,wait() 如果不唤醒就会一直等待下去
-
都可以被打断唤醒
-
-
锁特性不同
-
wait方法的调用必须先获取wait对象的锁,而sleep则无此限制(wait方法必须放到synchronized代码块中)
-
wait方法执行后会释放对象锁,允许其他线程获得该对象锁(我放弃CPU,但你们还能用)
-
sleep如果在synchronized代码块中执行,并不会释放对象锁(我放弃CPU,但你们也用不了)
-
8. 如何打断正在执行的线程
-
使用标志位打断,在主线程中的更改子线程执行的条件标志,依次来使子线程结束
-
使用stop方法强行终止,此方法现在已经作废,不推荐使用
-
使用Interrupt方法打断线程,本质上也是使用标志位打断,不过这个标志位使用的是current.isInterrupted()方法