Java面试复习7 多线程
声明:本面试复习都基于一本未署名的Java面试宝典所述,根据自己的理解写出了这一专栏的复习博客
-
传统多线程实现方式
继承Thread类创建线程
- 定义子类继承Thread类。
- 子类中重写Thread类中的run方法。
- 创建Thread子类对象,即创建了线程对象。
- 调用线程对象start方法:启动线程,调用run方法
实现Runnable接口
- 定义子类,实现Runnable接口。
- 子类中重写Runnable接口中的run方法。
- 通过Thread类含参构造器创建线程对象。
- 将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。
- 调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法。
-----------------第一种写法--------------------实现Runnable接口 public class MyThread2 implements Runnable{ @Override public void run() { for(int i = 0 ; i < 10000;i++) { System.out.println("Runable线程执行+++"+i); } } } public class TestThread { public static void main(String[] args) { Thread t = new MyThread1(); t.start();//启动线程 Runnable r = new MyThread2(); Thread t2 = new Thread(r); t2.start();//启动第二个线程 for(int i = 0 ; i < 10000;i++) { System.out.println("主线程执行****************************************"+i); } } } -----------------第二种写法--------------------继承Thread创建 public class MyThread1 extends Thread{ @Override public void run() { for(int i = 0 ; i < 10000;i++) { System.out.println("自定义线程执行-----------------------"+i); } } } public class TestThread { public static void main(String[] args) { Thread t = new MyThread1(); t.start();//启动线程 for(int i = 0 ; i < 10000;i++) { System.out.println("主线程执行****************************************"+i); } } } -----------------第三种写法--------------------成员内部类 public class TestThread { public static void main(String[] args) { Thread t1 = new TestThread().new MyThread3(); t1.start(); Thread t2 = new Thread(new TestThread().new MyThread4()); t2.start(); for(int i = 0 ; i < 10000;i++) { System.out.println("主线程执行****************************************"+i); } // t.start(); } class MyThread3 extends Thread { @Override public void run() { for(int i = 0 ; i < 10000;i++) { System.out.println("Thread线程执行-----------------------"+i); } } } class MyThread4 implements Runnable{ @Override public void run() { for(int i = 0 ; i < 10000;i++) { System.out.println("Runable线程执行+++"+i); } } } } -----------------第四种写法--------------------匿名内部类 public class TestThread { public static void main(String[] args) { new Thread() { @Override public void run() { for(int i = 0 ; i < 10000;i++) { System.out.println("Thread线程执行-----------------------"+i); } } }.start(); new Thread(new Runnable() { @Override public void run() { for(int i = 0 ; i < 10000;i++) { System.out.println("Runable线程执行+++"+i); } } }).start(); for(int i = 0 ; i < 10000;i++) { System.out.println("主线程执行****************************************"+i); } } }
-
线程的生命周期
新建(线程声明),
就绪(Start()方法执行后,此时一切准备就绪,只需要获得CPU的资源就可以运行),
运行(得到CPU后开始运行,失去后或者在线程得到让步(yield()方法)后又进入就绪状态),
阻塞(正在运行的线程在遇到sleep(),wait(),suspend()方法或者join()方法或者等待同步锁后进入阻塞状态,当其获得同步锁,notify,notifyall,resume,或者sleep时间到又从阻塞状态变为就绪态,等待CPU的资源分配),
死亡(stop或者遇到Erro/Exception,或者整个run执行完成后死亡)。---------------------------线程中的常用方法------------------------- void start(): 启动线程,并执行对象的run()方法 run(): 线程在被调度时执行的操作 String getName(): 返回线程的名称 void setName(String name):设置该线程名称 static Thread currentThread(): 返回当前线程。 在Thread子类中就 是this,通常用于主线程和Runnable实现类 static void yield():线程让步 //暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程 //若队列中没有同优先级的线程,忽略此方法 join() :当某个程序执行流中调用其他线程的 join() 方法时, 调用线程将被阻塞,直到 join() 方法加入的 join 线程执行完为止 //低优先级的线程也可以获得执行 static void sleep(long millis):(指定时间:毫秒) //令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行, 时间到后 重排队。 //抛出InterruptedException异常 stop(): 强制线程生命期结束,不推荐使用 boolean isAlive():返回boolean,判断线程是否还活着
-
程序,进程,线程的概念区别
- 程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。
- 进程(process)是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。即生命周期
如:运行中的QQ,运行中的MP3播放器
程序是静态的,进程是动态的
进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域 - 线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路径。
若一个进程同一时间并行执行多个线程,就是支持多线程的
线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小
一个进程中的多个线程共享相同的内存单元/内存地址空间—>它们从同一堆中分配对象,可以 访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资 源可能就会带来安全的隐患。
-
简述线程的调度方式
通常情况下是两种:
时间片轮循(多个线程之间轮流循环进行),
抢占式(高优先级的线程抢占CPU)同优先级线程组成先进先出队列(先到先服务),使用时间片策略
对高优先级,使用优先调度的抢占式策略线程的优先级.
MAX_PRIORITY:10
MIN _PRIORITY:1
NORM_PRIORITY:5 (通常情况下的优先级)
涉及的方法
getPriority() :返回线程优先值
setPriority(int newPriority) :改变线程的优先级
说明
线程创建时继承父线程的优先级
低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调用 -
叙述线程的分类
Java中的线程分为两类:一种是守护线程,一种是用户线程。它们在几乎每个方面都是相同的,唯一的区别是判断JVM何时离开。
守护线程是用来服务用户线程的,通过在start()方法前调用 thread.setDaemon(true)可以把一个用户线程变成一个守护线程。
Java垃圾回收就是一个典型的守护线程。
若JVM中都是守护线程,当前JVM将退出。 -
线程的同步的方式
借助synchronized关键字。加一把同步锁,来实现多线程的安全。
同步代码块一个用synchronized声明的代码块
同步方法一个用synchronized声明的方法
上述两种都可以是静态的也可以是非静态的。
同步代码块的锁对象?
当在非静态代码块中 对存在线程安全问题的代码实行同步策略 此时的锁对象可以是任意对象 只需要保证所有的线程使用的是同一个锁对象 this也可以充当锁对象
在静态代码块中 可以使用静态对象来作为锁对象 保证所有的线程使用的是同一个锁对象
同步方法的锁对象?
对于非静态方法 锁对象为this
对于静态方法 字节码对象 本类的class对象
synchronized (对象){ //这个对象可以使任意对象,但是被锁的代码需要保证是 //同一把锁,不能使用匿名对象 // 需要被同步的代码; } public synchronized void show (String name){ … }
-
线程的死锁
出现原因:死锁的出现是因为锁的嵌套
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃 自己需要的同步资源,就形成了线程的死锁在开发中 应尽量避免死锁的出现 应尽量不要使用锁的嵌套,使用一些算法也可以避免死锁
出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续
-
线程的通信
其实就是方法的使用。这里只不过具体了那些方法的用途,切记wait和notify/notifyAll必须在同步代码中执行
这里主要是对于线程一些基础知识的复习,线程部分的面试题都比较繁多,还未彻底看完, 后续会在这里继续进行更新。
更新ing…