并行是同时执行多个任务,而并发是多个任务在一段时间内交替执行。
并行(Parallel)是指同时执行多个任务或操作,通过同时利用多个计算资源来提高系统的处理能力。在并行处理中,任务被划分为多个子任务,并且这些子任务可以同时执行,每个子任务分配给不同的处理单元(如多核处理器或分布式系统中的多个计算节点)。通过并行执行,可以加快任务的完成速度,提高系统的吞吐量。
并发(Concurrent)是指多个任务在一段时间内交替执行。这并不意味着同时执行多个任务,而是任务在时间上重叠执行。在并发处理中,系统可能只有一个处理单元,但任务通过时间片轮转或其他调度算法进行切换,以实现多个任务的看似同时执行。并发可以提高系统的响应能力和资源利用率,特别是在涉及输入/输出等等待时间的情况下。
总结
并行:同时执行多个任务,通过利用多个计算资源提高系统处理能力。
并发:多个任务在一段时间内交替执行,提高系统的响应能力和资源利用率。
并行可以在多个计算资源上同时执行多个任务,而并发是在一个计算资源上交替执行多个任务。
定义:进程是程序的执行实例,它具有独立的内存空间和系统资源。线程是进程中的一个执行单元,多个线程可以共享进程的内存空间和系统资源。
线程切换开销相对小,但需要进行同步操作;进程是具有独立地址空间和资源的执行实例,切换开销大,但数据隔离性好。
守护线程(Daemon /ˈdiːmən/ Thread)是在程序运行过程中在后台提供服务的线程。当所有的非守护线程结束时,守护线程也会随之自动结束,无论它是否执行完任务。 守护线程在后台默默地运行,为其他线程提供支持和服务,如垃圾回收、监控、自动保存等 与非守护线程相比,守护线程有以下特点: 生命周期:守护线程的生命周期与程序的生命周期相同,即当所有的非守护线程结束时,守护线程也会被终止。 任务执行:守护线程通常用于执行一些支持性任务,不负责执行核心业务逻辑。 程序退出:如果只剩下守护线程在运行,程序会自动退出而不等待守护线程执行完任务。
Thread daemonThread = new Thread(new Runnable() {
public void run() {
// 守护线程的任务逻辑
}
});
daemonThread.setDaemon(true); // 设置为守护线程
daemonThread.start(); // 启动线程
class MyThread extends Thread {
public void run() {
// 线程的执行逻辑
}
}
// 创建并启动线程
MyThread myThread = new MyThread();
myThread.start();
创建Thread,传入一个Runnable对象(有参构造)
class MyRunnable implements Runnable {
public void run() {
// 线程的执行逻辑
}
}
// 创建并启动线程
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
创建Runnable的方法
class MyRunnable implements Runnable {
public void run() {
// 线程的执行逻辑
}
}
Thread thread = new Thread(new Runnable() {
public void run() {
// 线程的执行逻辑
}
});
thread.start();
Thread thread = new Thread(() -# {
// 线程的执行逻辑
});
thread.start();
通常情况下,推荐使用实现Runnable接口或使用Lambda表达式的方式,因为它们能更好地支持代码的组织和重用,同时避免了单继承的限制
Runnable和Callable接口都用于创建可并发执行的任务,但它们有以下区别:
返回值:Runnable接口的run()方法没有返回值,它表示一个没有返回结果的任务。而Callable接口的call()方法可以返回任务的执行结果,它是一个带有泛型参数的接口,用于定义具有返回值的任务。
异常处理:Runnable接口的run()方法不能抛出检查异常,只能捕获并处理。而Callable接口的call()方法可以抛出检查异常,调用者需要显式处理这些异常或将其向上抛出。
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
@FunctionalInterface
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
使用方式:Runnable接口通常作为线程的执行体,将任务逻辑放在run()方法中实现。Callable接口通常与ExecutorService一起使用,通过submit()方法提交Callable任务给线程池执行,并通过返回的Future对象获取任务的执行结果。
返回结果的获取:Runnable任务执行完毕后,无法直接获取任务的执行结果。而Callable任务执行完毕后,可以通过Future对象的get()方法获取任务的执行结果,该方法会阻塞调用线程直到任务执行完毕并返回结果。
// 使用Runnable接口创建任务
Runnable myRunnable = new Runnable() {
public void run() {
// 任务逻辑
}
};
// 使用Callable接口创建任务
Callable<Integer> myCallable = new Callable<Integer>() {
public Integer call() throws Exception {
// 任务逻辑
return 42; // 返回结果
}
};
在Java中,通常使用ExecutorService来执行Runnable和Callable任务。ExecutorService提供了submit()方法用于提交任务,并返回代表任务执行结果的Future对象。使用Future对象可以判断任务是否完成,获取任务的执行结果,或取消任务的执行。
操作系统中线程的5种状态
java中线程的6种状态
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,
/**
* /ˈtɜːmɪneɪtɪd/
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
https://blog.csdn.net/acc__essing/article/details/127470780?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168404937816782425125388%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=168404937816782425125388&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-4-127470780-null-null.142^v87^control_2,239^v2^insert_chatgpt&utm_term=java%20%E7%BA%BF%E7%A8%8B%E7%8A%B6%E6%80%81%E4%BB%A5%E5%8F%8A%E5%88%87%E6%8D%A2&spm=1018.2226.3001.4187
https://my.oschina.net/goldenshaw?tab=newest&catalogId=3277710
run() 方法只是在当前线程中同步执行线程代码(run方法的逻辑),而 start() 方法会创建一个新线程,并在新线程中异步执行线程代码。通常情况下,我们使用 start() 方法来启动线程,以便实现多线程并发执行的效果。
简单说就是一个在当前线程中执行run(),一个是新建一个线程执行run()
synchronized (sharedObject) {
// 需要同步的代码块
}
public synchronized void myMethod() {
// 需要同步的代码
}
Lock lock = new ReentrantLock();
lock.lock(); // 获取锁
try {
// 需要同步的代码
} finally {
lock.unlock(); // 释放锁
}
AtomicInteger counter = new AtomicInteger();
counter.incrementAndGet(); // 原子递增操作
Thread.interrupt() 向目标线程发送中断信号(即将目标线程的中断状态设置为 true,仅此而已)。Thread.interrupt() 方法并不能直接终止目标线程的执行。它只是改变了目标线程的中断状态,需要目标线程自行检查中断状态并作出相应的响应(一个线程不应该由其他线程强行终止)。
具体工作原理如下:
如果目标线程处于阻塞状态(如调用了 Object.wait()、Thread.sleep()、BlockingQueue.take() 等方法),它将立即抛出 InterruptedException 异常(并且唤醒目标线程),并且中断状态会被清除(重置为 false)。
如果目标线程在执行过程中检查了中断状态(通过 Thread.interrupted() 或 Thread.isInterrupted() 方法),则中断状态为 true。线程可以根据中断状态采取适当的操作,例如终止线程的执行或进行清理工作。
需要注意的是,Thread.interrupt() 方法仅仅是改变了目标线程的中断状态,具体的中断响应逻辑由目标线程自行决定。通常情况下,目标线程在合适的时机检查中断状态并采取相应的处理措施,例如退出执行循环或释放资源。 总结而言,Thread.interrupt() 方法通过改变目标线程的中断状态来请求目标线程中断,但具体的中断响应逻辑由目标线程自行处理。
Thread thread = new Thread(() -> {
System.out.println("线程被启动了");
while (!Thread.interrupted()) {
System.out.println(System.currentTimeMillis());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (Thread.interrupted()) {
System.out.println("线程被中断了");
break;
}
}
});
thread.start();
// 确保thread已经进入等待状态
Thread.sleep(500);
thread.interrupt();
线程被启动了
1684204424982
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at Xxx.lambda$main$0(Xxx.java:10)
at java.lang.Thread.run(Thread.java:748)
1684204425483
1684204426483
1684204427484
...
Thread中有个成员变量threadLocals
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocalMap中有一个数组
private Entry[] table;
Entry继承自WeakReference<ThreadLocal<?>>,key为ThreadLocal,value为Object(也就是我们存放的值)
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
ThreadLocal 是 Java 中的一个线程封闭机制,它允许将某个对象与当前线程关联起来,使得每个线程都拥有自己独立的对象副本。简单来说,ThreadLocal 提供了一种在多线程环境下,每个线程都可以独立访问自己的对象副本的机制。
ThreadLocal 的主要特点和应用如下:
ThreadLocal 在以下场景下常被使用:
总的来说,ThreadLocal 在需要实现线程隔离、线程上下文传递或线程状态保存的场景下非常有用。它提供了一种方便的机制,使得每个线程都可以独立地操作自己的对象副本,而不会受其他线程的干扰。
synchronized
是 Java 中的关键字,用于实现线程的同步和互斥。它可以用于修饰方法、代码块和静态方法,用于控制对共享资源的访问。
synchronized
关键字的特性包括:
synchronized
保证了同一时间只有一个线程可以获得锁并执行同步代码块或方法,防止多线程并发访问共享资源导致的数据不一致性和冲突问题。synchronized
释放锁时会将修改的共享变量的值刷新到主内存,使得其他线程可以看到最新的值,保证了多线程间的数据一致性。synchronized
使用的是内置锁(也称为监视器锁或互斥锁),每个对象都有一个与之关联的内置锁。当线程进入同步代码块时,它会尝试获取对象的内置锁,如果锁被其他线程持有,则线程进入阻塞状态,直到锁可用。同步代码块
同步方法
静态同步方法
Object lock = new Object();
synchronized (lock){
// 操作1
}
synchronized (lock){
// 操作2
}
synchronized (lock){
// 操作3
}
优化后代码
Object lock = new Object();
synchronized (lock){
// 操作1
// 操作2
// 操作3
}
public class LockEliminationExample {
public void performOperation() {
StringBuilder sb = new StringBuilder();
// 不会被多线程共享的局部变量
String localVariable = "Local";
// 锁对象只在当前方法内使用
synchronized (sb) {
// 执行需要同步的操作
sb.append("Hello");
sb.append("World");
sb.append(localVariable);
}
System.out.println(sb.toString());
}
}
优化后
public class LockEliminationExample {
public void performOperation() {
StringBuilder sb = new StringBuilder();
// 不会被多线程共享的局部变量
String localVariable = "Local";
// 执行需要同步的操作,锁消除
sb.append("Hello");
sb.append("World");
sb.append(localVariable);
System.out.println(sb.toString());
}
}
synchronized和ReentrantLock都是Java中用于实现线程同步的机制,它们有以下区别:
lock()
方法获取锁,以及调用unlock()
方法释放锁。
lockInterruptibly()
方法实现在等待期间响应中断。而synchronized关键字的锁获取过程是不可中断的,一旦获取不到锁,线程将一直处于阻塞状态,直到获取到锁或者抛出异常。
unlock()
方法相同次数的释放锁。而synchronized关键字则会自动进行锁的释放。
需要注意的是,synchronized是Java语言内置的关键字,使用简单方便,适用于大多数的同步场景。而ReentrantLock是一个类,提供了更多灵活性和扩展功能,适用于需要更精细控制的同步场景。在选择使用synchronized还是ReentrantLock时,可以根据具体需求和场景来进行选择。
synchronized和volatile是Java中用于实现线程安全的关键字,它们有以下区别:
需要注意的是,synchronized提供了更强的线程安全性和数据一致性,但在使用时需要考虑锁的开销和可能的死锁情况。volatile关键字的使用更轻量,适用于简单的变量读写操作,但无法解决复合操作的原子性问题。在选择使用synchronized还是volatile时,需要根据具体的场景和需求来进行选择。
当多个线程访问共享变量时,为了保证可见性和避免指令重排序带来的问题,可以使用volatile关键字来修饰变量。
volatile关键字具有以下特性:
需要注意的是,volatile关键字仅适用于单个变量的读写操作,并不能保证复合操作的原子性。例如,volatile关键字无法保证i++这种操作的原子性,因为该操作包括读取、自增和写回三个步骤,而volatile关键字只能保证单个变量的读写操作的原子性。
另外,volatile关键字的使用相对于synchronized来说更轻量,因为它不需要获取锁来实现线程间的同步,但在某些情况下可能需要额外的控制机制来保证一致性。
总结起来,volatile关键字主要用于保证共享变量的可见性,确保对变量的修改能够及时被其他线程看到,并且禁止编译器和处理器对该变量进行重排序。它适用于对变量的简单读写操作,但不能解决复合操作的原子性问题。
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class SharedResource {
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private int value;
public int getValue() {
lock.readLock().lock();
try {
return value;
} finally {
lock.readLock().unlock();
}
}
public void setValue(int newValue) {
lock.writeLock().lock();
try {
value = newValue;
} finally {
lock.writeLock().unlock();
}
}
}
乐观锁常见的两种实现方式是版本号(Versioning)和比较并交换(Compare and Swap,CAS)。
版本号的实现可以是一个整数字段,例如使用 Java 中的 AtomicInteger
,或者是一个时间戳字段,记录数据的修改时间。这样,每次更新操作都会更新版本号,从而标识数据的变化。
CAS 操作可以通过硬件的原子指令来实现,是一种非阻塞的操作。在多线程环境下,多个线程可以同时尝试执行 CAS 操作来更新数据,但只有一个线程的 CAS 操作会成功,其他线程需要重试或进行相应的处理。
Java 提供了 java.util.concurrent.atomic
包下的原子类,例如 AtomicInteger
、AtomicLong
等,它们使用 CAS 操作来实现线程安全的原子操作,可以作为乐观锁的一种实现方式。
这两种乐观锁的实现方式都是基于无锁的思想,避免了线程阻塞和上下文切换的开销,适用于读多写少的场景。选择哪种方式取决于具体的应用需求和环境。
乐观锁虽然在某些场景下可以提供高性能和并发量,但也存在一些缺点:
综上所述,乐观锁的主要缺点是需要处理冲突、无法保证一致性、自旋开销和难以应对长事务。在选择使用乐观锁时,需要综合考虑应用场景、并发访问模式和数据一致性要求,权衡其优点和缺点。
cas适用于冲突少的场景
synchronized 适用于冲突多的场景
Java中的原子类是一组线程安全的类,它们提供了原子操作的功能,可以在多线程环境下进行线程安全的操作。这些原子类位于java.util.concurrent.atomic
包中,常见的原子类包括AtomicInteger
、AtomicLong
、AtomicBoolean
等。
原子类的主要特点如下:
原子类在多线程编程中非常有用,特别适用于高并发环境下对共享变量进行原子操作的场景。它们提供了一种高效、简洁和线程安全的方式来操作共享变量,避免了使用传统的锁机制所带来的线程阻塞和上下文切换的开销。通过使用原子类,可以编写出更高效、可靠和可维护的多线程代码。
java.util.concurrent.locks.AbstractQueuedSynchronizer#tryAcquire
AbortPolicy
拒绝执行,并抛出异常RejectedExecutionException
.为默认拒绝策略CallerRunsPolicy
让调用者自己执行DiscardPolicy
丢弃DiscardOldestPolicy
丢弃最早的// 七大参数
int corePoolSize = 3;
int maximumPoolSize = 9;
long keepAliveTime = 30;
TimeUnit unit = TimeUnit.SECONDS;
BlockingQueue<Runnable# workQueue = new LinkedBlockingQueue<>(9);
ThreadFactory threadFactory = Executors.defaultThreadFactory();
RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
ThreadPoolExecutor pool = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
返回值:execute()没有返回值,submit() 有返回值
异常处理:execute()
方法没有提供任何异常处理机制。如果提交的任务在执行过程中抛出异常,它将由内部的 UncaughtExceptionHandler
处理。而 submit()
在获取结果的时候可以捕获到异常,然后自己处理。
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(1);
Runnable runnable = () -# {
int i = 10 / 0;
};
pool.execute(runnable);
pool.shutdown();
}
Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero
at Xxx.lambda$main$0(Xxx.java:14)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(1);
Runnable runnable = () -# {
int i = 10 / 0;
};
Future<?# submit = pool.submit(runnable);
try {
submit.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
pool.shutdown();
}
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(1);
Runnable runnable = () -# {
int i = 10 / 0;
};
Future<?# submit = pool.submit(runnable);
try {
submit.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
pool.shutdown();
}
java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at Xxx.main(Xxx.java:14)
Caused by: java.lang.ArithmeticException: / by zero
at Xxx.lambda$main$0(Xxx.java:10)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
任务的包装:execute()
方法接受一个 Runnable
接口或其子类的任务对象作为参数。而 submit()
方法既可以接受 Runnable
接口的任务对象,也可以接受 Callable
接口的任务对象。
java.util.concurrent.Executor#execute
void execute(Runnable command);
java.util.concurrent.ExecutorService#submit
<T> Future<T> submit(Callable<T> task);
Future<?> submit(Runnable task);
Future 对象:submit()
方法返回一个 Future
对象,可以通过该对象来管理和获取任务的执行状态和结果。
execute()
方法用于提交不需要返回结果的任务,而 submit()
方法用于提交需要返回结果的任务,并提供更多的控制和异常处理机制。如果你关心任务的返回结果或需要更精细的异常处理,那么使用 submit()
方法会更加灵活和方便。如果你只需要提交简单的任务而不关心结果,execute()
方法也是一个简单的选择。