文章目录
- 8.Java线程知识总结
- 8.1线程定义
- 8.2Java中创建线程的三种方式
- 8.2.1继承Thread类
- 8.2.2实现Runnable接口
- 8.2.3实现Callable接口(与线程池搭配使用)
- 8.2.4小结
- 8.3线程的六种状态
- 8.4sleep()和wait()的区别
- 8.5线程上下文切换
- 8.6线程之间的通信方式
- 8.6.1volatile和synchronized关键字
- 8.6.2等待/通知机制
- 8.6.3管道输入/输出流
- 8.6.4使用Thread.join()
- 8.6.5使用ThreadLocal
8.Java线程知识总结
8.1线程定义
- 定义:线程是进程内的一个”基本任务”,每个线程都有自己的功能,是CPU分配与调度的基本单位。
- 内存:对线程而言,除了CPU外,系统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源
- 开销方面:每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小
8.2Java中创建线程的三种方式
Java中创建线程主要有三种⽅式,分别为继承Thread类、实现Runnable接⼜、实现Callable接口。
8.2.1继承Thread类
- 继承Thread类,重写run()⽅法,调⽤start()⽅法启动线程
public class ThreadTest {
public static class MyThread extends Thread {
@Override
public void run () {
System.out.println( "This is child thread" ) ;
}
}
public static void main ( String [] args) {
MyThread thread = new MyThread ();
thread.start();
}
}
8.2.2实现Runnable接口
- 实现 Runnable 接口,重写
run()
方法,然后创建 Thread 对象,将 Runnable 对象作为参数传递给 Thread 对象,调用start()
方法启动线程。
class RunnableTask implements Runnable {
public void run() {
System.out.println("上岸、上岸!");
}
public static void main(String[] args) {
RunnableTask task = new RunnableTask();
Thread thread = new Thread(task);
thread.start();
}
}
8.2.3实现Callable接口(与线程池搭配使用)
- 实现 Callable 接口,重写
call()
方法 - 然后创建 FutureTask 对象,参数为 Callable 对象;紧接着创建 Thread 对象,参数为 FutureTask 对象,调用
start()
方法启动线程。 - 通过 实现Callable接口的对象 的get方法获取返回结果
class CallableTask implements Callable<String> {
public String call() {
return "上岸、上岸了!";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
CallableTask task = new CallableTask();
FutureTask<String> futureTask = new FutureTask<>(task);
Thread thread = new Thread(futureTask);
thread.start();
System.out.println(futureTask.get());
}
}
8.2.4小结
8.3线程的六种状态
状态 | 说明 |
---|---|
NEW | 初始状态:线程被创建,但还没有调用 start()方法 |
RUNNABLE | 运行状态:Java 线程将操作系统中的就绪和运行两种状态笼统的称作“运行” |
BLOCKED | 阻塞状态:表示线程阻塞于锁 |
WAITING | 等待状态:表示线程进入等待状态,进入该状态表示当前线程需要等待其他线程做出一些特定动作(通知或中断) |
TIME_WAITING | 超时等待状态:该状态不同于 WAITIND,它是可以在指定的时间自行返回的 |
TERMINATED | 终止状态:表示当前线程已经执行完毕 |
8.4sleep()和wait()的区别
sleep()
和wait()
是 Java 中用于暂停当前线程的两个重要方法- sleep 是让当前线程休眠,不涉及对象类,也不需要获取对象的锁,属于 Thread 类的方法;
- wait 是让获得对象锁的线程实现等待,前提要获得对象的锁,属于 Object 类的方法。
- 所属类不同:
- sleep()方法属于Thread类
- wait()方法属于Object类
- 锁行为不同:
- 当线程执行 sleep 方法时,它不会释放任何锁。
- 当线程执行 wait 方法时,它会释放当前拥有的锁
- 使用条件不同:
- sleep()方法可以在任何地方被调用;
- wait()方法必须在同步代码块或者同步方法中被调用,否则会抛出 IllegalMonitorStateException 异常
- 唤醒方式不同:
sleep()
方法在指定的时间过后,线程会自动唤醒继续执行。wait()
方法需要依靠notify()
、notifyAll()
方法或者wait()
方法中指定的等待时间到期来唤醒线程。
8.5线程上下文切换
- 并发,即一个CPU来应付多个线程,必然涉及到CPU资源的分配问题;
- CPU 资源的分配采用了时间片轮转,也就是给每个线程分配一个时间片,线程在时间片内占用 CPU 执行任务。
- 当线程使用完时间片后,就会处于就绪状态并让出 CPU 让其他线程占用,这就是上下文切换。
8.6线程之间的通信方式
- Java 中线程之间的通信主要是为了解决线程之间如何协作运行的问题。
- Java 提供了多种线程通信的方式,使得线程可以在合适的时间和地点进行同步。
- volatile和synchronized关键字
- 等待/通知机制
- 管道输入/输出流
- 使用Thread.join()
- 使用ThreadLocal
8.6.1volatile和synchronized关键字
- 关键字 volatile 用来修饰成员变量,告知程序任何对该变量的访问均需要从共享内存中获取,而对它的改变必须同步刷新回共享内存,保证所有线程对变量访问的可见性。
- 关键字 synchronized可以修饰方法,或者以同步代码块的形式来使用,确保多个线程在同一个时刻,只能有一个线程在执行某个方法或某个代码块。
8.6.2等待/通知机制
一个线程调用共享对象的 wait()
方法时,它会进入该对象的等待池,并释放已经持有的该对象的锁,进入等待状态,直到其他线程调用相同对象的 notify()
或 notifyAll()
方法。
一个线程调用共享对象的 notify()
方法时,它会唤醒在该对象等待池中等待的一个线程,使其进入锁池,等待获取锁。
8.6.3管道输入/输出流
管道输入/输出流和普通的文件输入/输出流或者网络输入/输出流不同,它主要用于线程之间的数据传输,而传输的媒介为内存。
[管道输入/输出流]主要包括了如下 4 种具体实现:PipedOutputStream、PipedInputStream、 PipedReader 和 PipedWriter,前两种面向字节,而后两种面向字符。
8.6.4使用Thread.join()
如果一个线程 A 执行了 thread.join()
语句,其含义是:当前线程 A 等待 thread 线程终止之后才从 thread.join()
返回。
8.6.5使用ThreadLocal
ThreadLocal是 Java 中提供的一种用于实现线程局部变量的工具。它允许每个线程都拥有自己的独立副本,从而实现线程隔离。ThreadLocal 可以用于解决多线程中共享对象的线程安全问题。