面试题:创建线程有几种方式

网上有说3或者4种,他们说的4种或者3种是把什么线程池也放进去了,今天我们就来看下oracle官网的描述,

thread is a thread of execution in a program. The Java Virtual Machine allows an application to have multiple threads of execution running concurrently.

Every thread has a priority. Threads with higher priority are executed in preference to threads with lower priority. Each thread may or may not also be marked as a daemon. When code running in some thread creates a new Thread object, the new thread has its priority initially set equal to the priority of the creating thread, and is a daemon thread if and only if the creating thread is a daemon.

When a Java Virtual Machine starts up, there is usually a single non-daemon thread (which typically calls the method named main of some designated class). The Java Virtual Machine continues to execute threads until either of the following occurs:

  • The exit method of class Runtime has been called and the security manager has permitted the exit operation to take place.
  • All threads that are not daemon threads have died, either by returning from the call to the run method or by throwing an exception that propagates beyond the run method.

 

There are two ways to create a new thread of execution. One is to declare a class to be a subclass of Thread. This subclass should override the run method of class Thread. An instance of the subclass can then be allocated and started. For example, a thread that computes primes larger than a stated value could be written as follows:

这是oracle官网对Thread类的描述:https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/Thread.html

我看到的是jdk14 但是不会影响对我们今天面试题的答案

在上面英文描述最后的一段话中我们可以找到答案:

There are two ways to create a new thread of execution

就是创建线程有二种方式,一个是继承Thread,还有一种是实现Runnable接口,这是官网说明的,够有权威的吧

至于说什么3或者4种,比如说Callable或者线程池只是基于线程做了一些封装操作而已,底层实现还是线程,它的本质没有变,

二种创建线程方式的对比

肯定是推荐使用实现Runnable方式创建线程的,理由支持如下:

1:如果是继承Thread,因为Java只支持单继承,这就限制了继承其他类,也就限制了它的扩展

2:我们在平时开发中很少说单独去开启一个线程,大部分都是使用线程池去管理线程,那么线程池需要的是Runnable,

3:如果使用继承Thread类开启线程的话,每次都是去开启一个线程,这样内存消耗比较大,如果是使用Runnable结合线程池可以降低消耗

 

### Java 中 ConcurrentHashMap 实现线程安全的底层原理 #### 什么是 ConcurrentHashMap? `ConcurrentHashMap` 是一种支持高并发场景下的线程安全 Map 结构,它允许多个线程同时对其进行读写操作而不会发生数据不一致的问题[^2]。相比传统的 `Hashtable` 和带有 `synchronized` 锁的 `HashMap`,`ConcurrentHashMap` 提供了更高的吞吐量和更低的竞争开销。 --- #### ConcurrentHashMap 的核心设计思想 为了提高性能,`ConcurrentHashMap` 并没有像 `Hashtable` 或者 `Collections.synchronizedMap()` 那样对整个表加锁,而是采用了更细粒度的锁分离策略——**分段锁(Segment Lock)**。具体来说: - 在 JDK 7 中,`ConcurrentHashMap` 将内部的数据分为若干个 Segment 对象,每个 Segment 类似于一个小的 HashTable,并且有自己的锁。这样可以使得不同的线程可以在不同的 Segment 上进行操作而不互相干扰。 - 在 JDK 8 中,`ConcurrentHashMap` 改进了其底层结构,去掉了 Segment 的概念,转而使用了一种更加高效的实现方式:**数组 + 链表 + 红黑树**[^1]。 --- #### JDK 8 中 ConcurrentHashMap 的底层实现细节 ##### 数据结构概述 在 JDK 8 中,`ConcurrentHashMap` 的底层由以下几个部分组成: 1. **Node 数组**:类似于普通的 HashMap,`ConcurrentHashMap` 使用了一个 Node 数组作为主要存储容器。 2. **链表**:当哈希冲突时,节点会被链接成一条单向链表。 3. **红黑树**:如果链表长度超过一定阈值(默认为 8),则会将链表转换为红黑树以减少查找的时间复杂度[^1]。 ##### 关键特性 1. **CAS 操作**:`ConcurrentHashMap` 大量依赖于无锁算法中的 CAS(Compare And Swap)技术来进行高效的状态更新。例如,在插入新元素时,先尝试通过 CAS 更新指定位置上的引用;只有当 CAS 失败时才会考虑升级到更高层次的同步手段。 2. **分段锁机制**:虽然 JDK 8 移除了显式的 Segments,但实际上仍然保留了类似的思路。对于每一次修改操作(如 put、remove),只会针对涉及的具体桶位施加重入锁(ReentrantLock),而不是全局锁定整个表[^2]。 3. **帮助式传播(Helpful Propagation)**:为了避免长时间持有锁带来的性能瓶颈,`ConcurrentHashMap` 设计了一些辅助函数让其他线程也能参与到一些耗时任务当中,比如扩容过程中的迁移工作就可以被任何发现正在进行 resize 的线程主动接手完成一部分[^1]。 --- #### 如何保证线程安全性? 1. **读操作无需加锁** - 由于引入了 JMM (Java Memory Model)的支持,`ConcurrentHashMap` 能够确保即使是在多线程环境下,简单的 get 请求也可以安全地返回最新版本的结果,而不需要额外付出同步成本[^3]。 2. **写操作局部化保护** - 插入或删除等变更型 API 主要集中在特定索引处执行,因此只需对该区域实施短暂封锁即可满足需求。例如,在调用 `putVal(K,V)` 方法期间,仅需占有对应槽位所属的 ReentrantLock 实例便可顺利推进后续逻辑[^2]。 3. **动态调整容量大小** - 当现有空间不足以容纳新增项时,`ConcurrentHashMap` 会自动触发扩展流程。值得注意的是,这一阶段同样遵循最小影响原则,即尽量不影响正常运转的服务请求。此外,得益于前面提到的帮助式传播理念,即便原作者暂时离开现场,后来者依旧有机会接力承担起剩余职责直至彻底结束整个重构周期。 --- #### 示例代码分析 以下是一个简化版的 `putIfAbsent` 方法演示如何利用 CAS 来保障原子性和一致性: ```java final V putVal(int hash, K key, V value) { Node<K,V>[] tab; Node<K,V> p; int n, i; if ((tab = table) != null && (n = tab.length) > 0) { // 计算定位索引 if ((p = tab[i = (n - 1) & hash]) == null) casTabAt(tab, i, null, new Node<K,V>(hash, key, value)); // 使用 CAS 创建首节点 else { lockForPut(p); // 加锁后再继续深入判断 try { Object k; if (valueMatch(p.value)) // 已存在相同key,则跳过赋值 return p.value; else // 新增情况走常规路径 addBinTreeOrList(hash, key, value); } finally { unlock(); // 不管怎样都要记得解锁哦~ } } } return null; } ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值