当前位置: 首页> 科技> 数码 > 齐家网装修口碑怎么样_广告机免费投放_百度如何发布作品_如何网站推广

齐家网装修口碑怎么样_广告机免费投放_百度如何发布作品_如何网站推广

时间:2025/7/14 3:43:28来源:https://blog.csdn.net/lonelymanontheway/article/details/140840929 浏览次数:0次
齐家网装修口碑怎么样_广告机免费投放_百度如何发布作品_如何网站推广

概述

Java基础回顾,备忘录。

线程和进程

参考进程、线程和协程的区别与联系。

线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速。

线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。每个线程都拥有单独的栈内存用来存储本地数据。

线程调度、线程状态及转换、线程模型、线程通信方式、线程阻塞、线程饥饿、死锁、活锁等知识点,参考上面链接。

线程安全

代码所在的进程中有多个线程在同时运行,这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行结果一致,而且其他变量的值也和预期的一样,就是线程安全。

线程安全的API:

  • COW:Copy-On-Write,如:CopyOnWriteArrayList,
  • CAS:ConcurrentHashMap,
  • 读写分离:LinkedBlockingQueue

++操作符不是线程安全的。涉及到多个指令,读取,增加,存储回内存,这个过程可能会出现多个线程交差执行。

DateFormat的所有实现,包括SimpleDateFormat都不是线程安全的。

死锁

死锁就是两个或两个以上的线程被无限的阻塞,线程之间相互等待所需资源。这种情况可能发生在当两个线程尝试获取其它资源的锁,而每个线程又陷入无限等待其它资源锁的释放,除非一个用户进程被终止。

就Java而言,线程死锁可能发生在以下情况:

  • 当两个线程相互调用Thread.join
  • 当两个线程使用嵌套的同步块,一个线程占用了另外一个线程必需的锁,互相等待时被阻塞就有可能出现死锁。

避免死锁最简单的方法就是阻止循环等待条件,将系统中所有的资源设置标志位、排序,规定所有的进程申请资源必须以一定的顺序(升序或降序)做操作来避免死锁。

强制启动

强制启动线程就像是强制进行Java垃圾回收,目前还没有绝对方法,虽然可使用System.gc()来进行垃圾回收,但是不保证能成功。在Java里面没有办法强制启动一个线程,它是被线程调度器控制着且Java没有公布相关的API。

停止线程

JDK1.0本来有stop,suspend和resume方法,但是由于潜在的死锁威胁,在JDK1.2中被标记为废弃,之后JDK就没有提供一个兼容且线程安全的方法来停止一个线程。当run或call方法执行完时,线程会自动结束。

线程局部变量

即ThreadLocal。

堆和栈

栈是一块和线程紧密相关的内存区域。每个线程都有自己的栈内存,用于存储本地变量,方法参数和栈调用,一个线程中存储的变量对其它线程是不可见的。

堆是所有线程共享的一片公用内存区域。对象都在堆里创建,为了提升效率线程会从堆中弄一个缓存到自己的栈,如果多个线程使用该变量就可能引发问题,这时volatile变量就可以发挥作用,它要求线程从主存中读取变量的值。

获取线程堆栈

线程堆栈,即线程Dump,Thread Dump。Dump文件,也叫内存转储文件或内存快照文件。

获取Java进程的线程Dump文件的步骤:

# 获取线程PID
jps
# 或
ps –ef | grep java
# 获取Dump文件
kill -3 <pid>
# 或
jstack

Windows下,还可使用Ctrl + Break组合键。

虚拟线程

JDK19新特性,可简单理解为轻量级线程,协程。有待进一步学习。

start和run

先看源码:

public void start() {synchronized (this) {// zero status corresponds to state "NEW".if (holder.threadStatus != 0)throw new IllegalThreadStateException();// native方法,JVM来负责调用start0();}
}@Override
public void run() {Runnable task = holder.task;if (task != null) {Object bindings = scopedValueBindings();runWith(bindings, task);}
}

区别:

  • start方法用于真正启动一个新线程,操作系统会分配一个新线程,使之进入就绪状态,当CPU分配时间给该线程时,JVM会调用该线程的run方法;
  • run方法相当于一个普通方法调用,不会创建新线程;run用于定义线程在启动后要执行的任务。直接调用run时,代码将在当前线程中执行,不会有多线程效果。

JVM启动一个单独的新线程不同于普通方法的调用,这项工作由start方法来完成,start方法由本地方法实现,需要显示地被调用。

Runnable和Callable

Runnable和Callable都代表那些要在不同的线程中执行的任务。Runnable从JDK1.0开始就有,Callable是在JDK1.5增加的。主要区别是Callable的 call方法可以返回值和抛出异常,而Runnable的run方法没有这些功能。Callable可以返回装载有计算结果的Future对象。

FutureTask

参考多线程与并发系列之FutureTask。

FutureTask表示异步运算,有启动、取消、查询是否完成、取回结果等方法。只有当任务完成时结果才能取回,如果任务尚未完成get方法将会阻塞。FutureTask对象可对调用Callable和Runnable的对象进行包装,可提交给Executor来执行。

ForkJoinTask

参考Java Fork-Join框架学习。

JDK7引入,Map-Reduce,分治思想,可用于递归地将一个大的计算任务划分成许多小的计算任务,目的是将所有可用的处理能力用来提升程序的性能。使用工作窃取算法,可以完成更多任务的工作线程可以从其它线程中窃取任务来执行。

interrupted和isInterrupted

Thread有interrupt、interrupted、isInterrupted三个方法,先看源码:

public void interrupt() {if (this != Thread.currentThread()) {checkAccess();// thread may be blocked in an I/O operationsynchronized (interruptLock) {Interruptible b = nioBlocker;if (b != null) {interrupted = true;interrupt0();  // inform VM of interruptb.interrupt(this);return;}}}// 设置中断标识为trueinterrupted = true;interrupt0();
}public static boolean interrupted() {return currentThread().getAndClearInterrupt();
}public boolean isInterrupted() {// 返回中断标识return interrupted;
}

interrupted会将中断状态清除。Java多线程的中断机制是用内部标识来实现的,调用Thread.interrupt()来中断一个线程就会设置中断标识为true。当中断线程调用静态方法Thread.interrupted()来检查中断状态时,中断状态会被清零。而非静态方法isInterrupted用来查询其它线程的中断状态,且不会改变中断状态标识。

任何抛出InterruptedException异常的方法都会将中断状态清零。无论如何,一个线程的中断状态有有可能被其它线程调用中断来改变。

wait与sleep

wait是Object类里的方法,即Object.wait(),会释放锁资源以及CPU资源;
sleep是Thread类里的方法,即Thread.sleep(),不会释放锁资源,会释放CPU资源。

wait方法是让一个线程进入到阻塞状态,必须写在一个synchronized同步代码块里面。wait/notify是基于共享内存来实现线程通信的工具,这个通信涉及条件的竞争,所以在调用这两个方法之前必须竞争锁资源。
当线程调用wait方法时,表示当前线程的工作处理完,意味着让其他竞争同一个共享资源的线程有机会去执行。但前提是其他线程需要竞争到锁资源,所以wait方法必须释放锁,否则就会导致死锁。

Thread.sleep()方法,只是让一个线程单纯进入睡眠状态,这个方法并没有强制要求加synchronized同步锁。而且从它的功能和语义来说,也没有这个必要。当然,如果是在一个Synchronized同步代码块里面调用这个Thread.sleep,也并不会触发锁的释放。

凡是让线程进入阻塞状态的方法,操作系统都会重新调度实现CPU时间片切换,目的是提升CPU的利用率。

wait和sleep都会造成某种形式的暂停,它们可以满足不同的需要。wait方法用于线程间通信,如果等待条件为真且其它线程被唤醒时它会释放锁,而sleep方法仅仅释放CPU资源或者让当前线程停止执行一段时间,但不会释放锁。

wait-notify

为什么wait和notify方法要在同步块中调用?
主要是因为Java API强制要求这样做,如果你不这么做,你的代码会抛出IllegalMonitorStateException异常。为了避免wait和notify之间产生竞态条件。

生产者-消费者

实际上就是一个线程间通信问题。

在同步块中调用wait和notify方法,阻塞通过循环来测试等待条件。

或者使用Semaphore、BlockingQueue来实现生产者消费者模型。

示例代码:

notify和notifyAll

多线程可以等待单监控锁,JDK提供一些方法当等待条件改变时通知它们,但是这些方法没有完全实现。notify方法不能唤醒某个具体的线程,所以只有一个线程在等待的时候它才有用武之地。而notifyAll唤醒所有线程,并允许他们争夺锁确保至少有一个线程能继续运行。

yield

yield方法可以暂停当前正在执行的线程对象,让其它有相同优先级的线程执行。静态方法,而且只保证当前线程放弃CPU占用而不能保证使其它线程一定能占用CPU,执行yield的线程有可能在进入到暂停状态后马上又被执行。

sleep、join、yield

检测线程是否拥有锁?

Thread中有个方法叫holdsLock,当且仅当前线程拥有某个具体对象的锁时返回true。源码如下:

// Returns true if and only if the current thread holds the monitor lock on the specified object.
public static native boolean holdsLock(Object obj);

wait,notify和notifyAll等方法为什么不在Thread类

JAVA提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程获得。如果线程需要等待某些锁那么调用对象中的wait方法才有意义。如果wait方法定义在Thread类中,线程正在等待的是哪个锁就不明显。简单的说,由于wait,notify和notifyAll都是锁级别的操作,所以把他们定义在Object类中因为锁属于对象。

竞态条件

多线程对一些资源的竞争的时候就会产生竞态条件。如果首先要执行的程序竞争失败排到后面执行,那么整个程序就会出现一些不确定的bug。

ReadWriteLock

读写锁是用来提升并发程序性能的锁分离技术的成果。ReadWriteLock是Java5中新增的一个接口,一个ReadWriteLock维护一对关联的锁,一个用于只读操作一个用于写。在没有写线程的情况下一个读锁可能会同时被多个读线程持有。写锁是独占的,你可以使用JDK中的ReentrantReadWriteLock来实现这个规则,它最多支持65535个写锁和65535个读锁。

public interface ReadWriteLock {Lock readLock();Lock writeLock();
}

Semaphore

同步类,它是一个计数信号。信号量维护一个许可集合。如有必要,在许可可用前会阻塞每一个acquire,然后再获取该许可。每个release添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore只对可用许可的号码进行计数,并采取相应的行动。信号量常常用于多线程的代码中,比如数据库连接池。

CyclicBarrier和CountDownLatch

都可用来让一组线程等待其它线程。与CyclicBarrier不同的是,CountdownLatch不能重新使用。

同步

即同步锁,即synchronized锁,参考面试必备之synchronized。

同步方法和同步代码块
在Java语言中,每一个对象有一把锁。线程可以使用synchronized关键字来获取对象上的锁。synchronized关键字可应用在方法级别(粗粒度锁)或者是代码块级别(细粒度锁)。

同步静态方法时会获取该类的“Class”对象,所以当一个线程进入同步的静态方法中时,线程监视器获取类本身的对象锁,其它线程不能进入这个类的任何静态同步方法。它不像实例方法,因为多个线程可以同时访问不同实例同步实例方法。

同步机制应遵循的基本准则

  • 空闲让进:当无进程处于临界区时,表明临界资源处于空闲状态,允许一个请求进入临界区的进程立即进入临界区,以有效利用临界资源
  • 忙则等待:当已有进程处于临界区时,表明临界资源正在被访问,因而其他试图进入临界区的进程必须等待,以保证对临界资源的互斥访问
  • 有限等待:对要求访问临界资源的进程,应保证在有限时间内能进入自己的临界区,以免陷入死等状态
  • 让权等待:当进程不能进入自己的临界区时,应立即释放处理机,以免进程陷入忙等状态

volatile

参考面试必备之volatile。

volatile和atomic
volatile变量可以确保先行关系,即写操作会发生在后续的读操作之前,但不能保证原子性,如++操作就不是原子性的。

而AtomicInteger类提供的atomic方法可以让这种操作具有原子性,如getAndIncrement方法会原子性的进行增量操作,把当前值加一,其它数据类型和引用变量也可以进行相似操作。

DCL

一个用来创建线程安全的单例的方法,当单例实例第一次被创建时它试图用单个锁进行性能优化 。

懒加载和静态变量初始化,枚举。创建线程安全的Singleton,参考单例模式。

忙循环

忙循环就是程序员用循环让一个线程等待,不像传统方法wait,sleep或 yield它们都放弃CPU控制,而忙循环不会放弃CPU,它就是在运行一个空循环。目的是为了保留CPU缓存,在多核系统中,一个等待线程醒来的时候可能会在另一个内核运行,这样会重建缓存。为了避免重建缓存和减少等待重建的时间就可以使用它了。

False Sharing

伪共享是

线程池

参考线程池。

线程运行时异常

线程运行时发生异常会怎样?

如果异常没有被捕获该线程将会停止执行。Thread.UncaughtExceptionHandler是用于处理未捕获异常造成线程突然中断情况的一个内嵌接口。当一个未捕获异常将造成线程中断时,JVM会使用Thread.getUncaughtExceptionHandler()来查询线程的UncaughtExceptionHandler,并将线程和异常作为参数传递给handler的uncaughtException方法进行处理。

异常

(多)线程编码时可能会遇到的异常。

InterruptedException

当一个线程在处于阻塞状态(如通过wait、sleep或join方法)时,如果另外一个线程调用这个线程的interrupt方法,便会抛出InterruptedException。

IllegalMonitorStateException

当一个线程尝试调用wait、notify、notifyAll,但没有持有该对象的监视器锁时,会抛出InvalidMonitorStateException。即程序在没有执行对象的任何同步块或同步方法(synchronized)时,仍然尝试调用wait,notify,notifyAll时。该异常是RuntimeExcpetion子类,所以不一定要捕获,不会在wait,notify,notifyAll的方法签名提及。

IllegalThreadStateException

如类名所示,在不合法的线程状态下被触发,如当试图在一个已经启动的线程上再次调用start方法时,会抛出此异常。

RejectedExecutionException

当向线程池提交任务时,线程池无法接受新任务(线程池已关闭或已达到最大容量),会抛出此异常。

TimeoutException

当一个线程在调用等待操作时,如Future.get,指定的超时时间已经过去,但任务还没有完成时抛出TimeoutException。

ExecutionException

当执行的任务抛出异常,该异常会被封装为ExecutionException并抛出,如Future.get()

BrokenBarrierException

在使用CyclicBarrier时,如果其中一个参与者线程中断或超时,会抛出BrokenBarrierException,表示屏障已经被破坏。

InterruptedIOException

当一个线程在进行IO操作时被中断,抛出InterruptedIOException。

ConcurrentModificationException

经典的CME,当多个线程同时修改某个集合时,可能会抛出此异常。

同步集合与并发集合

都为多线程和并发提供合适的线程安全的集合,并发集合的可扩展性更高。在Java1.5之前程序员们只有同步集合来用且在多线程并发的时候会导致争用,阻碍了系统的扩展性。Java5引入并发集合像ConcurrentHashMap,不仅提供线程安全还用锁分离和内部分区等现代技术提高可扩展性。

ConcurrentHashMap并发度

ConcurrentHashMap把实际map划分成若干部分来实现它的可扩展性和线程安全。这种划分是使用并发度获得的,它是ConcurrentHashMap类构造函数的一个可选参数,默认值为16,这样在多线程情况下就能避免争用。

最佳实践

包括:

  • 对线程命名,方便日志记录、错误调试;
  • 避免锁定,最小化同步范围,缩小临界区。不是将整个方法同步,只对关键部分做同步,即同步块;锁花费的代价高昂且上下文切换更耗费时间空间
  • 使用并发API,如BlockingQueue,CountDownLatch及Semeaphore,而不是使用wait和notify来实现线程间通信。前者可简化编码,而用wait和notify很难实现对复杂控制流的控制;
  • 优先使用并发集合,而不是同步集合、或对集合进行同步。前者可扩展性更好;
  • 使用线程池;
  • 将线程和任务分离,使用线程池执行器来执行Runnable或Callable;

参考

关键字:齐家网装修口碑怎么样_广告机免费投放_百度如何发布作品_如何网站推广

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: