找回密码
 立即注册
首页 业界区 业界 Java并发编程(1)

Java并发编程(1)

崔竹 2025-9-23 16:12:40
 
基础

1、并行跟并发的区别


  •  并行:同一时刻,多个线程都在执行,这就要求有多个CPU分别执行多个线程。
  • 并发:在同一时刻,只有一个线程执行,但在一个时间段内,两个线程都执行了。其实现依赖于CPU切换线程,因为切换时间很短,所以基本对于用户是无感知的。
2、什么是进程和线程


  • 进程:程序运行起来后在内存中执行,并附带有运行所需的资源,是系统进行资源分配的基本单位。
  • 线程:CPU是被分配到线程的,所以线程是CPU分配的基本单位。在Java中,当我们启动一个main函数就相当于启动了一个JVM进程,而main函数的线程就是主线程。一个进程中有多个线程,多个线程共用进程的堆和方法区,但每个线程都有自己的程序计数器和栈。
3、线程有几种创建方式


  • 继承Thread类:重写run方法,调用start()方法启动线程。

    • 缺点:单继承,继承了Thread就不能继承别的类了

  1. public static class MyThread extends Thread {
  2.       @Override
  3.       public void run() {
  4.             System.out.println("this is child thread");
  5.       }      
  6. }  
  7. public static void main(String[] args){
  8.       MyThread thread = new MyThread();
  9.       thread.start();
  10. }
复制代码

  • 实现Runnable接口:重写run()方法,这是一个任务,要包装成Thread才能start。

    • 优点:只需要实现Runnable,业务逻辑和线程机制解耦。可以被多个线程复用。
    • 缺点:也是用完就销毁。

  1. public class RunnableTask extends Runnable {
  2.       public void run() {
  3.             System.out.println("Runnable!");
  4.       }      
  5. }  
  6. public static void main(String[] args){
  7.       RunnableTask task = new RunnableTask();
  8.       new Thread(task).start();
  9. }
复制代码

  • 实现Callable接口:重写call()方法,通过FutureTask获取任务执行的返回值。
  1. public class CallerTask implements Callable < String > {
  2.        public String call () throws Exception {
  3.              return "Hello,i am running!" ;
  4.        }
  5.        public static void main ( String [] args) {
  6.              //创建异步任务
  7.            FutureTask < String > task = new FutureTask < String > ( new CallerTask ());
  8.             //启动线程
  9.            new Thread ( task ) .start();
  10.            try {
  11.                  //等 待 执 ⾏ 完 成 ,并获取返回结果
  12.                 String result = task . get();
  13.                 System.out.println ( result) ;
  14.            } catch ( InterruptedException e ) {
  15.                  e.printStackTrace ();
  16.            } catch ( ExecutionException e ) {
  17.                  e.printStackTrace ();
  18.            }
  19.      }
  20. }
复制代码
  
4、为什么调用start()方法时会执行run()方法?那为什么不直接调用run()


  •  JVM执行start方法,会先创建一个线程,由创建出来的线程去执行run方法,才能起到多线程的效果。如果直接调用run方法,那么run还是运行在主线程中,相当于顺序执行。
5、线程有哪些常用的调度方法

1.png

 
6、线程有几种状态

状态说明
NEW初始状态:线程被创建,还没调用start方法
RUNNABLE运行状态:就绪 + 运行
BLOCKED阻塞状态:阻塞于锁
WAITING等待状态:等待其他线程做出一定动作(通知或中断)
TIME_WATING超时等待:指定时间自行返回
TERMINATED终止状态:已经执行完毕
2.png


  • 初始状态:用new Thread()创建线程对象,还没调用start()时,此时只是被实例化。
  • 就绪:线程已经具备运行条形,只差操作系统调度。
  • 运行:当CPU把时间片分配给线程时,进入Running,执行run()方法里的代码。可能会发生:

    • 执行完毕-->终止
    • 调用yield()-->放弃CPU,回到就绪
    • 被系统抢占调度-->回到就绪
    • 进入阻塞或等待-->转到响应状态

  • 阻塞:当线程尝试获取锁,但锁被其他线程占用,会进入阻塞状态。一旦获取锁,就进入running状态
  • 等待:线程调用wait()、join()等时,会进入等待状态。必须由notify()或notifyAll()才能唤醒,回到runnable状态
  • 超时等待:线程调用带超时参数的方法进入超时等待状态。到了超时时间或被notiry()唤醒后回到runnable
  • 终止:执行完run()或抛出未捕获异常结束时,进入终止状态。
7、什么是线程上下文切换

   CPU资源分配采用时间片轮转,也就是给每个线程分配一个时间片,线程在时间片内占用CPU执行任务。当线程使用完时间片后,就会处于就绪状态并让出CPU让其他线程用,这就是线程上下文切换。
8、守护线程了解吗

   Java中的线程分为两类:daemon线程(守护线程)和user线程(用户线程)
  在JVM启动时会调用main函数,其所在线程就是一个用户线程。JVM内部还启动了很多守护线程,如垃圾回收线程。
  守护线程和用户线程的区别是,当最后一个非守护线程结束时,JVM会正常退出(此时不管当前是否存在守护线程)。
9、线程间有哪些通信方式


  •  volatile和synchronized关键字:

    • volatile:保证变量在多线程间的可见性,不保存原子性。
    • synchronized:保证同一时刻只有一个线程执行被保护的代码块,提供原子性和可见性。常用于操作共享变量临界区的互斥访问。

  • 等待/通知机制:线程调用wait()进入等待队列,释放锁;另一个线程在条件满足后调用notify或notifyAll唤醒。
  • 管道输入/输出流:用于线程间直接传递数据。一个线程写入 PipedOutputStream,另一个线程从 PipedInputStream 读取。适合流式传输。
  • 使用Thread.join():一个线程等待另一个线程执行完毕后再继续。常用于 主线程等待子线程结果 的场景。
  • 使用ThreadLocal:为每个线程单独提供一个变量副本。线程间不共享数据,避免竞争和加锁。
 

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

4 小时前

举报

东西不错很实用谢谢分享
您需要登录后才可以回帖 登录 | 立即注册