Java多线程
基础知识
-
进程与线程的区别?
进程是程序的一次执行过程,是程序在执行过程中分配和管理资源的基本单位;线程是CPU调度和分派的基本单位,它可与同属一个进程的其他的线程共享进程所拥有的全部资源。线程是进程的一部分,一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。根本区别:进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位。
-
线程同步和异步的区别?
同步:排队执行,效率低但安全。
异步:同时执行,效率高但数据不安全。
-
并发和并行的区别?
并发:指多个事件在同一时间段内发生。
并行:指两个或多个事件在同一时刻发生。
-
线程调度
分时调度:所有线程轮流使用CUP使用权,平均分配每个线程占用CPU的时间。
抢占式调度:优先级高的线程优先使用CPU,若优先级相同,则随机选择一个。Java使用的是抢占式调度。
创建线程的方式?
-
实现Runnable接口
需要实现接口中的 run() 方法。
public class MyRunnable implements Runnable { @Override public void run() { // ... } }
使用 Runnable 实例再创建一个 Thread 实例,然后调用 Thread 实例的 start() 方法来启动线程。
public static void main(String[] args) { MyRunnable instance = new MyRunnable(); Thread thread = new Thread(instance); thread.start(); }
-
实现Callable接口
与 Runnable 相比,Callable 可以有返回值,返回值通过 FutureTask 进行封装。
public class MyCallable implements Callable<Integer> { public Integer call() { return 123; } }
public static void main(String[] args) throws ExecutionException, InterruptedException { MyCallable mc = new MyCallable(); FutureTask<Integer> ft = new FutureTask<>(mc); Thread thread = new Thread(ft); thread.start(); System.out.println(ft.get()); }
-
继承Thread类
同样也是需要实现 run() 方法,因为 Thread 类也实现了 Runable 接口。
当调用 start() 方法启动一个线程时,虚拟机会将该线程放入就绪队列中等待被调度,当一个线程被调度时会执行该线程的 run() 方法。
public class MyThread extends Thread { public void run() { // ... } }
public static void main(String[] args) { MyThread mt = new MyThread(); mt.start(); }
Thread类
static Thread currentThread()
long getId()
String getName()
int getPriority()
void interrupt()
static boolean interrupted()
boolean isInterrupted()
boolean isAlive()
boolean isDaemon() //是否是守护线程
void start()
static void sleep(long millis)
static void sleep(long millis, int nanos)
/**
* 线程状态。 线程可以处于以下状态之一:
* NEW
* 尚未启动的线程处于此状态。
* RUNNABLE
* 在Java虚拟机中执行的线程处于此状态。
* BLOCKED
* 被阻塞等待监视器锁定的线程处于此状态。
* WAITING
* 无限期等待另一个线程执行特定操作的线程处于此状态。
* TIMED_WAITING
* 正在等待另一个线程执行最多指定等待时间的操作的线程处于此状态。
* TERMINATED
* 已退出的线程处于此状态。
* 线程在给定时间点只能处于一种状态。 这些状态是虚拟机状态,不反映任何操作系统线程状态。
*/
线程同步与通信
线程同步的方式:
同步代码块
public void func() {
synchronized (this) {
// ...
}
}
同步方法
public synchronized void func () {
// ...
}
显式锁Lock
public class LockExample {
//参数设为true则采用排队方式实现公平锁 Lock l = new ReentrantLock(true)
private Lock lock = new ReentrantLock();
public void func() {
lock.lock();
try {
for (int i = 0; i < 10; i++) {
System.out.print(i + " ");
}
} finally {
lock.unlock(); // 确保释放锁,从而避免发生死锁。
}
}
}
线程通信
wait(), notify(), notifyAll()
调用 wait() 使得线程等待某个条件满足,线程在等待时会被挂起,当其他线程的运行使得这个条件满足时,其它线程会调用 notify() 或者 notifyAll() 来唤醒挂起的线程。
await() signal() signalAll()
java.util.concurrent 类库中提供了 Condition 类来实现线程之间的协调,可以在 Condition 上调用 await() 方法使线程等待,其它线程调用 signal() 或 signalAll() 方法唤醒等待的线程。
相比于 wait() 这种等待方式,await() 可以指定等待的条件,因此更加灵活。
线程池
缓存线程池
/**执行流程
* 1. 判断线程池是否存在空闲线程
* 2. 存在则使用
* 3. 不存在则创建线程并放入线程池,然后使用
*/
ExecutorService service = Executors.newCachedThreadPool();
service.execute(Runnable runnable); //向线程池加入多个任务
定长线程池
/**执行流程
* 1. 判断线程池是否存在空闲线程
* 2. 存在则使用
* 3. 不存在且在线程池未满的情况下创建线程并放入线程池,然后使用
* 4. 不存在且在线程池已满的情况下等待线程池存在空闲线程
*/
//参数为指定的线程池中的线程数量
ExecutorService service = Executors.newFixedThreadPool(5);
service.execute(Runnable runnable);
单线程线程池
ExecutorService service = Executors.newSingleThreadExecutor();
service.execute(Runnable runnable);
周期性任务定长线程池
/**执行流程
* 1. 判断线程池是否存在空闲线程
* 2. 存在则使用
* 3. 不存在且在线程池未满的情况下创建线程并放入线程池,然后使用
* 4. 不存在且在线程池已满的情况下等待线程池存在空闲线程
*/
//参数为指定的线程池中的线程数量
ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
/**定时执行
* 参数1. Runnable类型的任务
* 参数2. 时长数字(延迟执行的时长)
* 参数3. 时长数字的单位
*/
service.schedule(new Runnable() { //五秒后开始执行任务一,仅执行一次
@Override
public void run() {
System.out.println("任务一执行了!");
}
}, 5, TimeUnit.SECONDS);
/**定时执行
* 参数1. Runnable类型的任务
* 参数2. 时长数字(首次延迟执行的时长)
* 参数3. 周期时长(每次执行的间隔时间)
* 参数4. 时长数字的单位
*/
service.scheduleAtFixedRate(new Runnable() { //五秒后开始执行任务二,每隔2S周期执行
@Override
public void run() {
System.out.println("任务二执行了!");
}
}, 5, 2, TimeUnit.SECONDS);