面试复习-Java线程

原文章:多线程+线程池(知识分享)_多线程,线程池详解-CSDN博客

多线程

继承Thread类创建线程

(1)继承Thread类并重写run方法

(2)创建线程对象

(3)调用该线程对象的start()方法来启动线程

代码

public class CreateThreadTest {
 public static void main(String[] args) {
 new ThreadTest().start();
 new ThreadTest().start();
 }
}
class ThreadTest extends Thread{
 private int i = 0;
 @Override
 public void run() {
 for (; i < 100; i++) {
 System.out.println(Thread.currentThread().getName() + " is running: " + i);
 }
 }
}

实现Runnable接口创建线程

(1)定义一个类实现Runnable接口,并重写该接口的run()方法

(2)创建 Runnable实现类的对象,作为创建Thread对象的target参数,此Thread对象才是真正的线程对象

(3)调用线程对象的start()方法来启动线程

public class CreateThreadTest {
 public static void main(String[] args) {
 RunnableTest runnableTest = new RunnableTest();
 new Thread(runnableTest, "线程1").start();
 new Thread(runnableTest, "线程2").start();
 }
}
class RunnableTest implements Runnable{
 private int i = 0;
 @Override
 public void run() {
 for (; i < 100; i++) {
 System.out.println(Thread.currentThread().getName() + " is running: " + i);
 }
 }
}

使用Callable与Future创建线程(实现Callable)

和Runnable接口不一样,Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大:call()方法可以有返回值,可以声明抛出异常。

(1)定义一个类实现Callable接口,并重写call()方法,该call()方法将作为线程执行体,并且有返回值

(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象

(3)使用FutureTask对象作为Thread对象的target创建并启动线程

(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CreateThreadTest {
 public static void main(String[] args) {
 CallableTest callableTest = new CallableTest();
 FutureTask<Integer> futureTask = new FutureTask<>(callableTest);
 new Thread(futureTask).start();
 try {
 System.out.println("子线程的返回值: " + futureTask.get());
 } catch (InterruptedException e) {
 e.printStackTrace();
 } catch (ExecutionException e) {
 e.printStackTrace();
 }
 }
}
class CallableTest implements Callable{
 @Override
 public Integer call() throws Exception {
 int sum = 0;
 for (int i = 1; i < 101; i++) {
 sum += i;
 }
 System.out.println(Thread.currentThread().getName() + " is running: " + sum);
 return sum;
 }
}

三种方式实现方式对比

1.实现Runnable/Callable接口相比继承Thread类的优势

(1)适合多个线程进行资源共享

(2)可以避免java中单继承的限制

(3)增加程序的健壮性,代码和数据独立

(4)线程池只能放入Runable或Callable接口实现类,不能直接放入继承Thread的类

2.Callable和Runnable的区别

(1) Callable重写的是call()方法,Runnable重写的方法是run()方法

(2) call()方法执行后可以有返回值,run()方法没有返回值

(3) call()方法可以抛出异常,run()方法不可以

(4) 运行Callable任务可以拿到一个Future对象,表示异步计算的结果 。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果

线程的状态

线程状态解释
新建(New)当线程对象被创建时,它处于新建状态,还未调用start()方法
运行(Runnable)线程进入运行状态,一旦调用了 start() 方法后,线程即进入了就绪状态,等待 CPU 分配时间片进入运行状态。
阻塞(Blocked)线程因为某些原因被暂停执行,进入阻塞状态,比如等待 I/O 操作、等待获取锁、调用 Thread.sleep() 方法,等待监视器锁等。
等待(Waiting)线程进入等待状态,等待其他线程的通知或中断,等待状态可以由 Object.wait()、Thread.join()、LockSupport.park() 方法进入。
计时等待(Timed Waiting)类似于等待状态,但是在一定时间后会自动解除阻塞,等待状态可以由 Thread.sleep()、Object.wait(long)、Thread.join(long)、LockSupport.parkNanos()、LockSupport.parkUntil() 方法进入。
终止(Terminated)线程执行完毕或者出现异常时,进入终止状态。

线程池

线程池与多线程:

创建和销毁线程:在多线程中,每次需要执行任务时,需要创建一个新的线程。当任务完成后,需要销毁该线程。而在线程池中,线程的创建和销毁都由线程池管理,线程可以被重复利用,从而避免了频繁的创建和销毁线程所带来的开销。

线程数量控制:在多线程中,线程数量由开发人员手动控制,如果线程数量过多,会导致系统资源的浪费,如果线程数量过少,则会导致任务等待时间过长。而线程池可以根据系统的负载情况自动调整线程数量,从而使系统资源的利用更加高效。

任务队列管理:线程池中的任务队列可以用来缓存未执行的任务,当线程池中的线程都在执行任务时,新的任务可以暂时存放在任务队列中,等待线程空闲时再进行执行。而在多线程中,需要自己手动管理任务队列,实现起来比较麻烦。

线程池七大参数

  1. 核心线程数(corePoolSize):线程池中会维护一个最小的线程数量,即使这些线程处理空闲状态,他们也不会被销毁
  2. 最大线程数(maximumPoolSize):线程池允许创建的最大线程数量,当线程池的任务队列满了之后,可以创建的最大线程数。
  3. 空闲线程存活时间(keepAliveTime):当线程池中的线程数超过核心线程数时,多余的线程会被回收,此参数即为非核心线程的空闲时间,超过此时间将被回收。

  4. 存活时间单位(unit):keepAliveTime的计量单位

  5. 工作队列(workQueue):新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。

    ArrayBlockingQueue

    有界的阻塞队列,底层是由数组实现的,当队列满时,新元素将无法添加到队列中,直到队列中有空闲位置为止。当队列为空时,获取元素的操作将会阻塞,直到队列中有元素可用。

    LinkedBlockingQuene

    无界的阻塞队列,底层是由链表实现的,可以存储任意数量的元素。当队列满时,新元素将会一直阻塞等待,直到队列中有空闲位置为止。当队列为空时,获取元素的操作将会阻塞,直到队列中有元素可用。

    SynchronousQuene

    不保存任何元素,每次插入操作必须等待另一个线程的移除操作,每次移除操作必须等待另一个线程的插入操作,因此它可以用于两个线程之间进行数据交换。

    PriorityBlockingQueue

    支持优先级的阻塞队列,底层是由堆实现的,可以根据元素的优先级顺序进行排序。当添加元素时,会根据元素的优先级自动排序,获取元素时会返回当前队列中优先级最高的元素。当队列为空时,获取元素的操作将会阻塞,直到队列中有元素可用。

  6. 线程工厂(threadFactory):用于创建新的线程,可定制线程名字、线程组、优先级等。

  7. 拒绝策略(handler):当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理

AbortPolicy(默认)丢弃任务并抛出 RejectedExecutionException 异常。
CallerRunsPolicy由调用线程处理该任务。
DiscardPolicy丢弃任务,但是不抛出异常。可以配合这种模式进行自定义的处理方式。
DiscardOldestPolicy丢弃队列最早的未处理任务,然后重新尝试执行任务。

线程池状态

在这里插入图片描述

多线程注意事项

    线程安全:多个线程访问共享数据时需要考虑线程安全问题,如使用同步机制、原子类等。
    避免死锁:多个线程相互等待对方释放资源时,可能会导致死锁,需要避免。 线程生命周期的管理:线程应该在不需要时及时销毁,避免资源浪费。
    线程优先级的设置:设置线程优先级时,需要注意不要过分依赖优先级带来的性能提升。
    多线程的上下文切换开销:在多线程并发执行时,线程的切换会产生一定的开销,需要注意线程的数量和执行时间。

线程池注意事项

    线程池大小的选择:线程池的大小应该根据任务类型和系统配置进行调整,避免线程过多或过少。
    任务队列的选择:线程池的任务队列应该根据任务类型和负载情况进行选择,如使用有界队列或无界队列。
    关闭线程池:线程池在不需要时应该及时关闭,否则可能会产生资源浪费和内存泄漏。
    线程池的监控和调试:线程池在运行过程中可能会产生各种问题,如任务堆积、线程死锁等,需要进行监控和调试。

  • 10
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值