java整个程序全局变量-java全局对象
作者|阿木夏
编辑| 卡维恩
原创 |
1、什么是线程?
线程是操作系统能够进行操作调度的最小单位。 它包含在一个流程中,是流程中的实际操作单元。 程序员可以将它用于多处理器编程,您可以使用多线程来加速计算密集型任务。 例如,如果一个线程完成一项任务需要 100 毫秒,那么十个线程完成任务只需要 10 毫秒。
2、线程和进程有什么区别?
线程是进程的子集。 一个进程可以有多个线程,每个线程并行执行不同的任务。 不同的进程使用不同的内存空间,所有的线程共享同一个内存空间。 每个线程都有一个单独的堆栈内存,用于存储本地数据。
3、Java中如何实现线程?
两种方式: java.lang.Thread类的实例是一个线程但是需要调用java.lang.Runnable接口来执行。 由于thread类本身就是被调用的Runnable接口,所以可以继承java.lang.Thread类或者直接调用Runnable接口重写run()方法实现线程。
4、Java关键字volatile和synchronized的作用和区别是什么?
1、挥发性
它修改的变量不保留副本,直接访问主存中的变量。
在Java内存模型中,有主内存,每个线程也有自己的内存(比如寄存器)。 为了提高性能,线程会在自己的内存中保留一份它访问的变量的副本。 这样,同一个变量在某一时刻内存中的值可能与另一个线程内存中的值或主存中的值不一致。 声明为volatile的变量意味着这个变量随时会被其他线程修改,所以不能缓存在线程内存中。
2、同步
当用于修改方法或代码块时,可以保证最多有一个线程同时执行代码。
1、两个并发线程访问同一个对象中的synchronized(this)同步代码块时,一次只能执行一个线程。 另一个线程必须等待当前线程执行完代码块才能执行代码块。
2. 但是,当一个线程访问对象的synchronized(this)同步代码块时,另一个线程仍然可以访问对象中的non-synchronized(this)同步代码块。
3、特别关键的是,当一个线程访问一个对象的synchronized(this)同步代码块时,其他线程访问该对象中所有其他synchronized(this)同步代码块都会被阻塞。
4.当一个线程访问一个对象的synchronized(this)同步代码块时,它获得了该对象的对象锁。 结果,其他线程暂时被阻止访问对象同步代码的所有部分。
5、以上规则同样适用于其他对象锁。
5. 线程生命周期有哪些不同?
当我们在Java程序中创建一个新的线程时,它的状态是New。 当我们调用线程的start()方法时,状态变为Runnable。 线程调度器会为Runnable线程池中的线程分配CPU时间,并将它们的状态变为Running。 其他线程状态是 Waiting、Blocked 和 Dead。
6. 你对线程优先级的理解是什么?
每个线程都有一个优先级。 一般来说,高优先级的线程在运行时会有优先级,但这取决于线程调度的实现,这与操作系统有关(OS dependent)。 我们可以定义线程的优先级,但这并不能保证高优先级线程会先于低优先级线程执行。 线程优先级是一个int变量(从1-10),1代表最低优先级,10代表最高优先级。
7.什么是死锁? 如何分析和避免死锁?
死锁是指两个或多个线程永远阻塞的情况。 这种情况至少需要两个以上的线程和两个以上的资源。
要分析死锁,我们需要查看 Java 应用程序的线程转储。 我们需要找出哪些线程处于 BLOCKED 状态以及它们正在等待的资源。 每个资源都有一个唯一的 id,通过它我们可以找出哪些线程已经拥有它的对象锁。
避免嵌套锁、只在需要的地方使用锁和避免无限期等待是避免死锁的常用方法。
8.什么是线程安全? Vector 是线程安全类吗?
如果您的代码处于多个线程同时运行的进程中,则这些线程可能同时运行此代码。 如果每次运行的结果都和单线程运行的结果一样,并且其他变量的值都和预期的一样,就是线程安全的。 一个线程安全的计数器类的同一个实例对象即使被多个线程使用也不会被误算。 显然,您可以将集合类分为两组,线程安全的和非线程安全的。 Vector使用同步的方式来实现线程安全,而与之类似的ArrayList并不是线程安全的。
9. 如何在 Java 中停止一个线程?
Java 提供了丰富的 API 但没有提供停止线程的 API。 JDK 1.0 最初有一些控制方法,如 stop()、suspend() 和 resume(),但由于潜在的死锁威胁,它们在后续的 JDK 版本中被弃用,Java API 的设计者也没有提供兼容和线程安全的方法停止一个线程。当run()或call()方法执行时,线程会自动结束。 如果想手动结束一个线程,可以使用volatile布尔变量退出run()方法的循环或者取消任务来中断线程。
10. 什么是ThreadLocal?
ThreadLocal 用于创建线程局部变量。 我们知道一个对象的所有线程都会共享它的全局变量,所以这些变量不是线程安全的,我们可以使用同步技术。 但是当我们不想使用同步时,我们可以选择ThreadLocal变量。
每个线程都会有自己的Thread变量,它们可以使用get()set()方法来获取它们的默认值或者在线程内部改变它们的值。 ThreadLocal 实例通常希望它们是与线程状态关联的私有静态属性。
11. Sleep()、suspend()和wait()有什么区别?
Thread.sleep() 使当前线程在指定时间处于“Not Runnable”状态。 线程一直持有对象的监视器。 例如,一个线程当前在一个synchronized块或方法中,其他线程不能进入该块或方法。 如果另一个线程调用 interrupt() 方法,它将唤醒那个“睡眠”线程。
注意:sleep() 是一个静态方法。 这意味着它只对当前线程有效。 一个常见的错误是调用 t.sleep(),(其中 t 是不同于当前线程的线程)。 即使执行了 t.sleep() ,当前线程也会进入睡眠状态,而不是 t 线程。 t.suspend() 是一种过时的方法。 使用 suspend() 会导致线程进入停滞状态。 线程会一直持有对象的监视器,suspend()很容易造成死锁问题。
object.wait() 使当前线程处于“不可运行”状态,与 sleep() 的区别在于 wait 是对象方法而不是线程。 调用object.wait()时,线程首先需要获取这个对象的对象锁,当前线程必须在锁对象上同步,将当前线程加入等待队列,然后另一个线程才能同步同一个对象锁定调用对象。 notify(),会唤醒原来等待的线程,然后释放锁。 基本上wait()/notify()和sleep()/interrupt()类似,只是前者需要获取对象锁。
12.什么是线程饥饿,什么是活锁?
当所有线程都被阻塞,或者由于所需资源不可用而无法处理时,没有非阻塞线程使资源可用。 Java API 中的线程活锁可能发生在以下几种情况:
1.当程序中所有线程都执行Object.wait(0)时,参数为0的wait方法。程序会活锁,直到有线程调用对应对象的Object.notify()或Object.notifyAll()。
2、当所有线程都陷入死循环。
13.什么是Java Timer类? 如何创建具有特定时间间隔的任务?
java.util.Timer 是一个实用类,可用于安排线程在未来的特定时间执行。 Timer 类可用于安排一次性任务或周期性任务。
java.util.TimerTask 是实现 Runnable 接口的抽象类。 我们需要继承这个类来创建自己的定时任务,并使用Timer来定时执行。
14. Java 中的同步集合和并发集合有什么区别?
同步集合和并发集合都为多线程和并发提供了合适的线程安全集合,但并发集合更具可扩展性。
在Java 1.5之前,程序员只能使用synchronized集合,这会在多个线程并发时造成争用,阻碍了系统的可扩展性。
Java 5 引入了像 ConcurrentHashMap 这样的并发集合,它不仅提供了线程安全性,还通过锁分离和内部分区等现代技术提高了可扩展性。
15. 哪个是更好的选择,同步方法还是同步块?
synchronized 块是更好的选择,因为它不会锁定整个对象(当然你也可以让它锁定整个对象)。 同步方法会锁定整个对象,即使这个类中有多个不相关的同步块java整个程序全局变量,这通常会导致它们停止执行,需要等待获取这个对象上的锁。
16.什么是线程池? 为什么要使用它?
创建线程需要昂贵的资源和时间。 如果在任务来的时候创建线程,响应时间会比较长,而且一个进程可以创建的线程数是有限的。
为了避免这些问题,在程序启动时会创建几个线程来响应处理。 它们被称为线程池,里面的线程被称为工作线程。
从JDK1.5开始,Java API提供了Executor框架,可以创建不同的线程池。 比如单线程池,一次处理一个任务; 固定数量的线程池或缓存线程池(一种可扩展的线程池,适用于具有许多短期任务的程序)。
17. Java中的invokeAndWait和invokeLater有什么区别?
这两个方法由 Swing API 提供给 Java 开发人员从当前线程而不是事件派发线程更新 GUI 组件。 InvokeAndWait() 同步更新 GUI 组件,例如进度条。 一旦更新了进度,进度条也应该相应地改变。 如果进度被多个线程跟踪,则调用 invokeAndWait() 方法请求事件派发线程相应地更新组件。 invokeLater() 方法异步调用更新组件。
18.什么是多线程中的忙循环?
忙循环是当程序员使用循环使线程等待时,不像传统方法如 wait()、sleep() 或 yield() 放弃 CPU 控制,但忙循环不放弃 CPU,它只是运行一个空循环。 这样做的目的是为了保留 CPU 缓存。
在多核系统上,等待线程醒来时可能正在另一个核上运行,重建缓存。 它可以用于避免重建缓存并减少等待重建的时间。
19.什么是Java内存模型?
Java 内存模型指定并指导 Java 程序在不同的内存体系结构、CPU 和操作系统之间确定性地运行。 在多线程的情况下尤为重要。 Java 内存模型保证一个线程所做的更改可以被其他线程看到,并且它们之间存在先行关系。 这种关系定义了一些规则,让程序员在并发编程时可以更清晰地思考。 例如,happens-before 关系确保:
线程内的代码可以按顺序执行,这称为程序顺序规则。
对于同一个锁,一个解锁操作必须发生在另一个时间上更晚发生的锁操作之前,这也称为监视器锁规则。
前面对volatile的写操作先于后面的volatile的读操作,也叫volatile变量规则。
线程中的任何操作都必须在线程的 start() 调用之后调用,也称为线程启动规则。
根据线程终止规则,线程的所有操作都将在线程终止之前执行。
对象的终结操作必须在对象构造完成后完成,也称为对象终结规则。
可转移性
更多介绍可以移步并发编程网:
(深入理解java内存模型系列文章:)
20. Java 中的 interrupted 和 isInterruptedd 方法有什么区别?
interrupted() 和 isInterrupted() 的主要区别是前者会清除中断状态而后者不会。 Java多线程的中断机制是通过内部标志实现的。 调用 Thread.interrupt() 来中断线程会将中断标志设置为 true。 当被中断的线程调用静态方法Thread.interrupted()检查中断状态时,中断状态被清除。
非静态方法isInterrupted()用于在不改变中断状态标志的情况下查询其他线程的中断状态。 简单地说,任何抛出 InterruptedException 的方法都会清除中断状态。 在任何情况下,一个线程的中断状态都可能被其他调用中断的线程改变。
21. Java 中的同步集合和并发集合有什么区别?
同步集合和并发集合都为多线程和并发提供了合适的线程安全集合,但并发集合更具可扩展性。 在Java 1.5之前,程序员只能使用synchronized集合,这会在多个线程并发时造成争用,阻碍了系统的可扩展性。 Java 5 引入了像 ConcurrentHashMap 这样的并发集合,它不仅提供了线程安全性,还通过锁分离和内部分区等现代技术提高了可伸缩性。
无论是同步集合还是并发集合,都支持线程安全。 它们之间的主要区别体现在性能和可扩展性,以及它们如何实现线程安全。
同步的HashMap、Hashtable、HashSet、Vector、ArrayList比它们的并发实现(ConcurrentHashMap、CopyOnWriteArrayList、CopyOnWriteHashSet)慢很多。 这种缓慢的主要原因是锁。 同步集合会锁定整个 Map 或 List,而并发集合不会。 并发集合通过使用锁定剥离等先进且经过验证的技术是线程安全的。
比如ConcurrentHashMap会将整个Map分成若干个分片,只对相关的分片加锁,其他未加锁的分片允许多个线程访问。
同样,CopyOnWriteArrayList 允许多个线程以异步方式读取。 当一个线程写入时,它会将整个List复制给它。
如果在通过多读少写有利于并发集合的条件下使用并发集合,则它们比同步集合更具可扩展性。
22.什么是线程池? 为什么要使用它?
创建线程需要昂贵的资源和时间。 如果在任务来的时候创建线程,响应时间会比较长,而且一个进程可以创建的线程数是有限的。 为了避免这些问题,在程序启动时会创建几个线程来响应处理。 它们被称为线程池,里面的线程被称为工作线程。 从JDK1.5开始,Java API提供了Executor框架,可以创建不同的线程池。 比如单线程池,一次处理一个任务; 固定数量的线程池或缓存线程池(可扩展的线程池,适用于具有许多短期任务的程序)
线程池的作用是在一个线程被调用时初始化一定数量的线程。 当一个线程来的时候,首先检查初始化的线程是否还空闲。 如果没有,则检查当前运行的线程数是否达到最大值。 如果没有编号,则分配一个新的线程来处理它。
就像在餐厅吃饭一样,从里面叫服务员; 但是如果已经达到了最大数量,那就意味着waiters已经用完了,那就没办法了java整个程序全局变量,其他线程只好等到有新的“waiter”为止。
线程池的好处是可以管理线程,有一个高层中心,这样程序就不会乱,不会因为大量的并发导致资源不足导致系统挂掉。
23. Java 中的活锁和死锁有什么区别?
活锁:一个线程通常有响应其他线程的活动。 如果其他线程也在响应另一个线程的活动,则可能会出现活锁。 与死锁一样,活锁线程无法继续执行。 然而,线程并没有被阻塞——它们正忙于相互响应,无法恢复工作。 这相当于两个人在走廊相遇:A 向左倾斜让 B 通过,B 向右倾斜让 A 通过。 看得出来是互相挡住了。 A向右倾,B向左倾,他们仍然互相挡住。
死锁:两个或多个线程被阻塞,等待其他死锁线程持有的锁。 当多个线程同时请求同一组锁但顺序不同时,通常会发生死锁。 死锁会让你的程序挂起,无法完成任务。
24.如何避免死锁?
要发生死锁,必须满足以下四个条件:
互斥:一种资源一次只能被一个进程使用。
请求和持有条件:当一个进程因请求资源而阻塞时,它会持有获得的资源。
非剥夺条件:进程获得的资源,在用完之前不能强行剥夺。
循环等待条件:几个进程之间形成头尾循环等待资源关系。
三种技术用于避免死锁:
加锁顺序(线程按一定顺序加锁)
加锁时限(在线程尝试获取锁的时候加上一定的时限,超过时限则放弃对锁的请求并释放其拥有的锁)
死锁检测
(死锁产生的原因以及如何避免更深入的理解:)
25.notify()和notifyAll()有什么区别?
1、notify()和notifyAll()是Object对象用来通知等待该对象的线程的方法。
2. void notify():唤醒一个正在等待对象的线程。
3. void notifyAll():唤醒所有等待该对象的线程。
两者最大的区别在于:
notifyAll 使所有等待对象通知的线程退出等待状态,并等待对象上的锁。 一旦对象被解锁,他们就会竞争。
notify 他只是选择一个等待状态的线程进行通知,并使其获取对象上的锁,但不打扰其他也在等待该对象通知的线程,并在第一个线程完成后释放该对象上的锁跑步。 此时,如果该对象没有再次使用notify语句,即使该对象已经空闲,其他在wait状态等待的线程也会继续处于wait状态,因为直到该对象没有得到该对象的通知发送通知或通知全部。 被通知或notifyAll,而不是锁定。
26.什么是可重入锁(ReentrantLock)?
java.util.concurrent.lock 中的 Lock 框架是对锁定的抽象,允许将锁定实现为 Java 类而不是语言功能。 这为 Lock 的多种实现留下了空间,它们可能具有不同的调度算法、性能特征或锁定语义。 ReentrantLock 类实现了 Lock,它具有与 synchronized 相同的并发性和内存语义,但增加了一些功能,如锁投票、定时锁等待和可中断锁等待。 此外,它在激烈的争用情况下提供更好的性能。 (换句话说,当许多线程想要访问共享资源时,JVM 可以花更少的时间调度线程,而花更多的时间执行线程。)
可重入锁是什么意思? 简单来说,它有一个与锁相关的获取计数器。 如果拥有锁的线程再次获取锁,则获取计数器加1,然后需要释放两次锁才能真正释放。 这模仿了同步的语义; 如果线程进入一个由线程已经拥有的监视器保护的同步块,则允许线程继续,当线程退出第二个(或后续)同步块时,锁不会被释放,只有线程退出锁才有效当它进入受监视器保护的第一个同步块时释放。
27、读写锁可以用于哪些应用场景?
读写锁可以用在“读多写少”的场景。 读写锁支持多个读操作并发,写操作只能由一个线程执行。
ReadWriteLock 针对相对不频繁写入数据结构,但经常读取数据结构的多个任务进行了优化。 ReadWriteLock 允许您同时拥有多个读取器,只要它们都没有尝试写入。 如果写锁已经被其他任务持有,则在释放写锁之前,任何读者都无法访问它。
ReadWriteLock对程序性能的提升主要受限于以下因素:
1. 读取数据的频率与修改数据的频率比较的结果。
2.读写时间
3.有多少线程竞争
4.是否在多进程机器上运行
Java我是最强的。 是一个专注于Java技术的垂直社区。 加入优质技术群,请在公众号后台留言“加群”。 投稿合作请发邮件至:javawozuiqiang@qq.com,并注明“Java我是最强贡献者”。
Java我是最强的
一个关心Java人成长的技术内容社区
快速关注