Java 锁机制

在多线程编程中,锁是一种用于确保线程安全的重要机制。Java 作为一种广泛使用的编程语言,提供了多种类型的锁来满足不同的并发编程需求。本文将深入探讨 Java 锁机制的概念、类型、应用场景以及最佳实践,帮助读者更好地理解和应用 Java 锁。

一、引言

在现代软件开发中,多线程编程已经成为了一种常见的技术手段。多线程编程可以提高程序的并发性和响应性,但是也带来了一些挑战,如线程安全问题。为了解决线程安全问题,Java 提供了多种类型的锁,如 synchronized 关键字、ReentrantLock、ReadWriteLock 等。这些锁可以确保在多线程环境下,对共享资源的访问是线程安全的。

二、Java 锁的概念与作用

(一)锁的定义

在 Java 中,锁是一种用于控制对共享资源的访问的机制。当一个线程获取到锁时,它可以独占访问共享资源,其他线程必须等待该线程释放锁后才能访问共享资源。

(二)锁的作用

  1. 确保线程安全
    • 在多线程环境下,多个线程可能同时访问共享资源,这可能导致数据不一致、丢失更新等问题。锁可以确保在同一时间只有一个线程可以访问共享资源,从而避免这些问题的发生。
  2. 实现同步
    • 锁可以用于实现线程之间的同步。例如,一个线程在等待另一个线程完成某个任务后才能继续执行,这时可以使用锁来实现线程之间的同步。
  3. 提高并发性能
    • 虽然锁会在一定程度上降低并发性能,但是合理地使用锁可以避免数据冲突,从而提高系统的整体并发性能。例如,通过使用读写锁,可以允许多个线程同时读取共享资源,而只有一个线程可以写入共享资源,从而提高系统的并发度。

三、Java 锁的类型

(一)内置锁(synchronized)

  1. 简介
    • synchronized 是 Java 中的内置锁,也称为监视器锁。它可以用于修饰方法或代码块,确保在同一时间只有一个线程可以访问被修饰的方法或代码块。
  2. 特点
    • 简单易用:synchronized 是 Java 语言的内置特性,使用起来非常简单。只需要在方法或代码块上加上 synchronized 关键字即可实现锁的功能。
    • 自动释放锁:当一个线程进入 synchronized 修饰的方法或代码块时,会自动获取锁。当线程退出方法或代码块时,会自动释放锁。
    • 可重入性:synchronized 具有可重入性,即一个线程可以多次获取同一个锁。这在一些复杂的同步场景中非常有用,例如一个方法调用另一个方法,而这两个方法都使用了 synchronized 修饰。
  3. 示例代码
    • 以下是一个使用 synchronized 修饰方法的示例:

public class SynchronizedExample {private int count = 0;public synchronized void increment() {count++;}public static void main(String[] args) throws InterruptedException {SynchronizedExample example = new SynchronizedExample();Thread thread1 = new Thread(example::increment);Thread thread2 = new Thread(example::increment);thread1.start();thread2.start();thread1.join();thread2.join();System.out.println("Count: " + example.count);}
}

  • 在这个示例中,increment方法使用了 synchronized 修饰,确保在同一时间只有一个线程可以执行该方法,从而保证了对count变量的线程安全访问。

(二)显式锁(ReentrantLock)

  1. 简介
    • ReentrantLock 是 Java 中的一种显式锁,它提供了与 synchronized 类似的功能,但具有更多的灵活性和高级特性。
  2. 特点
    • 可中断的锁获取:ReentrantLock 支持可中断的锁获取,即当一个线程在等待获取锁时,可以被其他线程中断。这在一些需要响应中断的场景中非常有用,例如在等待用户输入时。
    • 尝试获取锁:ReentrantLock 支持尝试获取锁的操作,可以在不阻塞的情况下尝试获取锁。如果锁不可用,立即返回 false,而不会阻塞线程。
    • 公平锁:ReentrantLock 可以设置为公平锁,即按照线程等待的时间顺序来分配锁。这在一些对公平性要求较高的场景中非常有用,例如在多线程任务调度中。
  3. 示例代码
    • 以下是一个使用 ReentrantLock 的示例:

import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockExample {private int count = 0;private final ReentrantLock lock = new ReentrantLock();public void increment() {lock.lock();try {count++;} finally {lock.unlock();}}public static void main(String[] args) throws InterruptedException {ReentrantLockExample example = new ReentrantLockExample();Thread thread1 = new Thread(example::increment);Thread thread2 = new Thread(example::increment);thread1.start();thread2.start();thread1.join();thread2.join();System.out.println("Count: " + example.count);}
}

  • 在这个示例中,使用了 ReentrantLock 来实现对count变量的线程安全访问。在increment方法中,通过lock.lock()获取锁,在操作完成后通过lock.unlock()释放锁。

(三)读写锁(ReadWriteLock)

  1. 简介
    • ReadWriteLock 是 Java 中的一种读写锁,它允许多个线程同时读取共享资源,但在写入共享资源时必须独占访问。
  2. 特点
    • 提高并发性能:读写锁可以允许多个线程同时读取共享资源,从而提高系统的并发性能。在读取操作频繁的场景中,读写锁可以显著提高系统的性能。
    • 互斥的写操作:当一个线程在写入共享资源时,其他线程必须等待该线程完成写入操作后才能读取或写入共享资源。这确保了在写入操作期间,共享资源的一致性。
  3. 示例代码
    • 以下是一个使用 ReadWriteLock 的示例:

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;public class ReadWriteLockExample {private int value = 0;private final ReadWriteLock lock = new ReentrantReadWriteLock();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();}}public static void main(String[] args) throws InterruptedException {ReadWriteLockExample example = new ReadWriteLockExample();Thread readerThread1 = new Thread(() -> {System.out.println("Reader 1: " + example.getValue());});Thread readerThread2 = new Thread(() -> {System.out.println("Reader 2: " + example.getValue());});Thread writerThread = new Thread(() -> {example.setValue(42);});readerThread1.start();readerThread2.start();writerThread.start();readerThread1.join();readerThread2.join();writerThread.join();}
}

  • 在这个示例中,使用了 ReadWriteLock 来实现对value变量的读写操作。在读取操作中,使用lock.readLock().lock()获取读锁,在操作完成后使用lock.readLock().unlock()释放读锁。在写入操作中,使用lock.writeLock().lock()获取写锁,在操作完成后使用lock.writeLock().unlock()释放写锁。

(四)自旋锁(SpinLock)

  1. 简介
    • 自旋锁是一种特殊的锁,它在获取锁时不会立即进入阻塞状态,而是在一个循环中不断尝试获取锁,直到获取成功为止。
  2. 特点
    • 适用于短时间的等待:自旋锁适用于等待时间较短的场景,因为在等待期间线程不会被阻塞,而是在一个循环中不断尝试获取锁。如果等待时间较长,自旋锁会浪费 CPU 资源,因为线程一直在循环中等待,而不是进入阻塞状态等待唤醒。
    • 避免上下文切换:自旋锁可以避免线程的上下文切换,因为线程在等待期间不会被阻塞,而是在一个循环中不断尝试获取锁。上下文切换是一个比较耗时的操作,因此在等待时间较短的场景中,自旋锁可以提高系统的性能。
  3. 示例代码
    • 以下是一个使用自旋锁的示例:

import java.util.concurrent.atomic.AtomicReference;public class SpinLockExample {private AtomicReference<Thread> owner = new AtomicReference<>();public void lock() {Thread currentThread = Thread.currentThread();while (!owner.compareAndSet(null, currentThread)) {// 自旋等待}}public void unlock() {Thread currentThread = Thread.currentThread();owner.compareAndSet(currentThread, null);}public static void main(String[] args) throws InterruptedException {SpinLockExample spinLock = new SpinLockExample();Thread thread1 = new Thread(() -> {spinLock.lock();try {System.out.println("Thread 1 acquired the lock.");Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();} finally {spinLock.unlock();}});Thread thread2 = new Thread(() -> {spinLock.lock();try {System.out.println("Thread 2 acquired the lock.");} finally {spinLock.unlock();}});thread1.start();thread2.start();thread1.join();thread2.join();}
}

  • 在这个示例中,使用了自旋锁来实现对共享资源的访问控制。在lock方法中,通过一个循环不断尝试使用compareAndSet方法将owner原子引用设置为当前线程,如果设置成功,则表示获取到了锁。在unlock方法中,将owner原子引用设置为 null,表示释放锁。

四、Java 锁的应用场景

(一)多线程同步

  1. 问题描述
    • 在多线程环境下,多个线程可能同时访问共享资源,这可能导致数据不一致、丢失更新等问题。需要使用锁来确保在同一时间只有一个线程可以访问共享资源,从而保证数据的一致性。
  2. 解决方案
    • 使用 synchronized 关键字、ReentrantLock 或 ReadWriteLock 等锁机制来实现多线程同步。例如,在一个多线程的计数器程序中,可以使用锁来确保在同一时间只有一个线程可以对计数器进行递增操作,从而保证计数器的值是正确的。

(二)资源竞争

  1. 问题描述
    • 在多线程环境下,多个线程可能同时竞争有限的资源,如数据库连接、文件句柄等。如果没有适当的锁机制,可能会导致资源竞争问题,如死锁、资源耗尽等。
  2. 解决方案
    • 使用锁来控制对资源的访问,确保在同一时间只有一个线程可以访问资源。例如,可以使用 ReentrantLock 来实现对数据库连接的独占访问,避免多个线程同时竞争数据库连接导致的资源耗尽问题。

(三)线程间通信

  1. 问题描述
    • 在多线程环境下,线程之间需要进行通信和协调。例如,一个线程需要等待另一个线程完成某个任务后才能继续执行。需要使用锁来实现线程之间的通信和协调。
  2. 解决方案
    • 使用锁和条件变量来实现线程之间的通信和协调。例如,可以使用 ReentrantLock 和 Condition 对象来实现一个生产者 - 消费者模型,其中生产者线程在生产完一个产品后,通过条件变量通知消费者线程进行消费。

五、Java 锁的性能优化

(一)选择合适的锁类型

  1. 不同锁类型的性能特点
    • synchronized 是 Java 中的内置锁,使用简单方便,但是在一些高并发场景下可能会导致性能问题。ReentrantLock 提供了更多的灵活性和高级特性,如可中断的锁获取、尝试获取锁等,但是在使用上相对复杂一些。ReadWriteLock 适用于读取操作频繁的场景,可以提高系统的并发性能。自旋锁适用于等待时间较短的场景,可以避免线程的上下文切换,提高系统的性能。
  2. 根据应用场景选择锁类型
    • 在选择锁类型时,需要根据应用场景的特点来选择合适的锁类型。如果应用场景对性能要求较高,可以考虑使用 ReentrantLock 或 ReadWriteLock 等显式锁,以获得更多的灵活性和性能优化空间。如果应用场景对等待时间要求较高,可以考虑使用自旋锁,以避免线程的上下文切换。

(二)减少锁的粒度

  1. 锁粒度的概念
    • 锁粒度是指锁所保护的资源的大小。锁粒度越小,并发度越高,但是锁的管理成本也越高。锁粒度越大,并发度越低,但是锁的管理成本也越低。
  2. 如何减少锁的粒度
    • 可以通过将大的锁分解为小的锁来减少锁的粒度,从而提高系统的并发性能。例如,在一个哈希表中,可以使用分段锁来代替全局锁,每个分段锁只保护哈希表中的一部分数据,从而提高哈希表的并发性能。

(三)避免死锁

  1. 死锁的产生原因
    • 死锁是指两个或多个线程互相等待对方释放锁,从而导致所有线程都无法继续执行的情况。死锁的产生原因主要有以下几个方面:
      • 互斥条件:一个资源在同一时间只能被一个线程占用。
      • 请求与保持条件:一个线程在持有一个资源的同时,又请求另一个资源。
      • 不剥夺条件:一个线程在持有一个资源后,不能被其他线程强行剥夺。
      • 循环等待条件:多个线程之间形成了一个循环等待的关系。
  2. 如何避免死锁
    • 可以通过以下几种方式来避免死锁的产生:
      • 破坏互斥条件:如果资源可以被多个线程同时访问,就可以破坏互斥条件。但是在实际应用中,这种方式通常不太可行,因为很多资源本身就是互斥的。
      • 破坏请求与保持条件:可以通过一次性获取所有需要的资源,或者在获取资源之前先释放已经持有的资源,来破坏请求与保持条件。
      • 破坏不剥夺条件:可以通过设置超时时间或者使用中断机制来破坏不剥夺条件。当一个线程在等待资源时,如果超过了超时时间或者被其他线程中断,就可以释放已经持有的资源,从而避免死锁的产生。
      • 破坏循环等待条件:可以通过对资源进行编号,然后按照编号顺序获取资源,来破坏循环等待条件。

六、Java 锁的示例代码分析

(一)多线程同步示例

  1. 使用 synchronized 关键字实现多线程同步
    • 在这个示例中,使用了 synchronized 关键字来修饰incrementgetCount方法,确保在同一时间只有一个线程可以访问count变量,从而实现了多线程同步。
  2. 使用 ReentrantLock 实现多线程同步
    • 在这个示例中,使用了 ReentrantLock 来实现对count变量的线程安全访问。在incrementgetCount方法中,通过lock.lock()获取锁,在操作完成后通过lock.unlock()释放锁。

(二)资源竞争示例

  1. 使用 ReentrantLock 解决资源竞争问题
    • 假设我们有一个共享资源,多个线程需要竞争访问这个资源。以下是使用 ReentrantLock 来解决资源竞争问题的示例:
import java.util.concurrent.locks.ReentrantLock;class SharedResource {private int value;private final ReentrantLock lock = new ReentrantLock();public void setValue(int newValue) {lock.lock();try {value = newValue;} finally {lock.unlock();}}public int getValue() {lock.lock();try {return value;} finally {lock.unlock();}}
}public class ResourceCompetitionExample {public static void main(String[] args) throws InterruptedException {SharedResource resource = new SharedResource();Thread thread1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {resource.setValue(resource.getValue() + 1);}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {resource.setValue(resource.getValue() - 1);}});thread1.start();thread2.start();thread1.join();thread2.join();System.out.println("Final value: " + resource.getValue());}
}

  • 在这个示例中,SharedResource类表示共享资源,使用 ReentrantLock 来确保对value变量的线程安全访问。两个线程分别对value进行加和减操作,通过锁的机制保证了资源的正确访问。

  1. 使用 ReadWriteLock 提高资源访问效率
    • 当有大量的读操作和较少的写操作时,可以使用 ReadWriteLock 来提高资源的访问效率。以下是一个示例:
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;class ReadWriteResource {private int value;private final ReadWriteLock lock = new ReentrantReadWriteLock();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();}}
}public class ReadWriteLockExample {public static void main(String[] args) throws InterruptedException {ReadWriteResource resource = new ReadWriteResource();Thread[] readerThreads = new Thread[10];Thread writerThread = new Thread(() -> {resource.setValue(42);});for (int i = 0; i < readerThreads.length; i++) {readerThreads[i] = new Thread(() -> {System.out.println("Reader: " + resource.getValue());});}writerThread.start();for (Thread readerThread : readerThreads) {readerThread.start();}writerThread.join();for (Thread readerThread : readerThreads) {readerThread.join();}}
}

  • 在这个示例中,ReadWriteResource类使用 ReadWriteLock 来管理对value变量的访问。多个读线程可以同时获取读锁进行读取操作,而写线程在写入时需要获取写锁,独占访问资源。

(三)线程间通信示例

  1. 使用 ReentrantLock 和 Condition 实现生产者 - 消费者模型
    • 以下是使用 ReentrantLock 和 Condition 实现生产者 - 消费者模型的示例:
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;class ProducerConsumer {private final Queue<Integer> buffer = new LinkedList<>();private final int bufferSize = 5;private final ReentrantLock lock = new ReentrantLock();private final Condition notFull = lock.newCondition();private final Condition notEmpty = lock.newCondition();public void produce(int value) throws InterruptedException {lock.lock();try {while (buffer.size() == bufferSize) {notFull.await();}buffer.add(value);notEmpty.signalAll();} finally {lock.unlock();}}public int consume() throws InterruptedException {lock.lock();try {while (buffer.isEmpty()) {notEmpty.await();}int value = buffer.poll();notFull.signalAll();return value;} finally {lock.unlock();}}
}public class ProducerConsumerExample {public static void main(String[] args) {ProducerConsumer pc = new ProducerConsumer();Thread producerThread = new Thread(() -> {for (int i = 0; i < 10; i++) {try {pc.produce(i);System.out.println("Produced: " + i);} catch (InterruptedException e) {Thread.currentThread().interrupt();}}});Thread consumerThread = new Thread(() -> {for (int i = 0; i < 10; i++) {try {int value = pc.consume();System.out.println("Consumed: " + value);} catch (InterruptedException e) {Thread.currentThread().interrupt();}}});producerThread.start();consumerThread.start();try {producerThread.join();consumerThread.join();} catch (InterruptedException e) {Thread.currentThread().interrupt();}}
}

  • 在这个示例中,ProducerConsumer类使用 ReentrantLock 和 Condition 来实现生产者 - 消费者模型。生产者线程在缓冲区满时等待,消费者线程在缓冲区空时等待,通过条件变量进行线程间的通信和协调。

  1. 使用 Semaphore 实现资源的有限访问
    • Semaphore 可以用来控制同时访问某个特定资源的线程数量。以下是一个示例:
import java.util.concurrent.Semaphore;class LimitedResource {private final Semaphore semaphore;public LimitedResource(int permits) {semaphore = new Semaphore(permits);}public void useResource() throws InterruptedException {semaphore.acquire();try {System.out.println("Using resource. Thread: " + Thread.currentThread().getName());Thread.sleep(1000);} finally {semaphore.release();}}
}public class SemaphoreExample {public static void main(String[] args) {LimitedResource resource = new LimitedResource(3);Thread[] threads = new Thread[5];for (int i = 0; i < threads.length; i++) {threads[i] = new Thread(resource::useResource);threads[i].start();}for (Thread thread : threads) {try {thread.join();} catch (InterruptedException e) {Thread.currentThread().interrupt();}}}
}

  • 在这个示例中,LimitedResource类使用 Semaphore 来控制对资源的有限访问。创建了一个最多允许三个线程同时访问资源的信号量,五个线程尝试获取资源并使用,通过信号量的机制确保了资源的有限访问。

七、Java 锁的注意事项

(一)锁的范围

  1. 确定合适的锁范围
    • 在使用锁时,要确保锁的范围尽可能小,只锁定必要的代码块或对象。避免不必要的大范围锁定,以免影响并发性能。
    • 例如,如果只需要保护一个变量的更新操作,就只在更新这个变量的代码块上使用锁,而不是在整个方法上使用锁。
  2. 避免死锁
    • 死锁是指两个或多个线程互相等待对方释放锁,从而导致所有线程都无法继续执行的情况。在使用锁时,要注意避免死锁的发生。
    • 例如,在获取多个锁时,要确保以相同的顺序获取锁,避免出现循环等待的情况。

(二)锁的性能

  1. 考虑锁的开销
    • 锁的获取和释放会带来一定的开销,尤其是在高并发的情况下。要考虑锁的开销对系统性能的影响。
    • 可以通过性能测试和分析来确定锁的使用是否对系统性能造成了较大的影响,如果是,可以考虑使用更高效的锁机制或优化锁的使用方式。
  2. 选择合适的锁类型
    • 不同的锁类型在性能和功能上有所不同。要根据具体的应用场景选择合适的锁类型。
    • 例如,如果需要更高的灵活性和可中断性,可以选择 ReentrantLock;如果读取操作远远多于写入操作,可以选择 ReadWriteLock。

(三)锁的可维护性

  1. 清晰的锁命名和注释
    • 在使用锁时,要为锁取一个有意义的名称,并添加清晰的注释,说明锁的用途和作用范围。
    • 这有助于提高代码的可维护性,让其他开发人员更容易理解代码的逻辑和锁的使用方式。
  2. 避免复杂的锁嵌套
    • 复杂的锁嵌套会增加代码的复杂性和理解难度,也容易导致死锁等问题。要尽量避免复杂的锁嵌套。
    • 如果确实需要使用多个锁,可以考虑使用更高级的锁机制,如读写锁或分段锁,来简化锁的管理。

八、结论

Java 锁机制是多线程编程中确保线程安全的重要手段。通过合理地使用不同类型的锁,如 synchronized、ReentrantLock、ReadWriteLock 等,可以有效地解决多线程环境下的资源竞争和同步问题。在使用锁时,要根据具体的应用场景选择合适的锁类型,注意锁的范围、性能和可维护性,以避免出现死锁、性能问题和代码难以理解等情况。同时,要不断学习和掌握新的锁机制和多线程编程技术,以提高自己的编程能力和系统的性能。希望本文对读者理解和应用 Java 锁机制有所帮助。

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/460036.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

音视频入门基础:AAC专题(11)——AudioSpecificConfig简介

音视频入门基础&#xff1a;AAC专题系列文章&#xff1a; 音视频入门基础&#xff1a;AAC专题&#xff08;1&#xff09;——AAC官方文档下载 音视频入门基础&#xff1a;AAC专题&#xff08;2&#xff09;——使用FFmpeg命令生成AAC裸流文件 音视频入门基础&#xff1a;AAC…

java-web-day5

1.spring-boot-web入门 目标: 开始最基本的web应用的构建 使用浏览器访问后端, 后端给浏览器返回HelloController 流程: 1.创建springboot工程, 填写模块信息, 并勾选web开发的相关依赖 注意: 在新版idea中模块创建时java下拉框只能选17, 21, 23 这里选17, maven版本是3.6.3, 很…

基于SSM的智能台球厅系统

基于SSM的智能台球厅系统设计与实现 摘要 智能台球厅系统是一个以用户便捷体验为核心的管理系统&#xff0c;结合SSM&#xff08;Spring、Spring MVC、MyBatis&#xff09;框架来实现台球厅日常业务流程的自动化和智能化管理。系统主要包含用户预约、场地管理、设备状态监控、支…

String的长度有限,而我对你的思念却无限延伸

公主请阅 1. 为什么学习string类&#xff1f;2. string类的常用接口2.1 string类对象的常见构造2.1.1 string 2.2 operator[]2.3 迭代器2.4 auto自动推导数据类型2.5 范围for2.6 迭代器第二层2.7 size和length获取字符串的长度2.8 max_size 获取这个字符串能设置的最大长度2.9 …

spring-第十一章 注解开发

spring 文章目录 spring前言1.注解回顾1.1原理1.2springIOC注解扫描原理1.2.1解释1.2.2案例 2.声明bean的注解补充&#xff1a;Bean注解&#xff0c;管理三方包对象 3.spring注解的使用3.1加入aop依赖3.2配置文件中添加context命名空间3.3配置文件中指定要扫描的包3.4在Bean上使…

Linux 之 文件属性与目录、字符串处理、系统信息获取

学习任务&#xff1a; 1、 文件属性与目录&#xff1a;Linux 文件类型、stat、chmod、链接文件、目录文件 2、 字符串处理&#xff1a;字符串输入/输出、strlen、strcat、strcpy、memset、atoi()、atol()、atoll() 3、 系统信息&#xff1a;proc 虚拟文件系统&#xff08;重点&…

搜索引擎算法更新对网站优化的影响与应对策略

内容概要 随着互联网的不断发展&#xff0c;搜索引擎算法也在不断地进行更新和优化。了解这些算法更新的背景与意义&#xff0c;对于网站管理者和优化人员而言&#xff0c;具有重要的指导意义。不仅因为算法更新可能影响到网站的排名&#xff0c;还因为这些变化也可能为网站带…

省域经济高质量发展水平测算及数据2000-2021年

经济高质量发展水平测算&#xff0c;是通过一系列科学的方法和指标&#xff0c;对经济活动的各个方面进行评估和量化的过程。这不仅涉及到经济增长的速度&#xff0c;更涵盖了效益、效率、可持续性等多个维度。包含了2000年至2021年期间&#xff0c;全国31个省份、自治区、直辖…

MacOS/Macbook用户自定义字体安装教程

Mac本自定义字体 示例机型一、下载相关字体文件到本地二、打开启动台三、选择其他四、选择字体册五、添加字体六、选择字体七、安装字体八、安装完成 MacOS官网安装教程 示例机型 系统&#xff1a;MacOS12.6&#xff0c;芯片&#xff1a;M1Pro 一、下载相关字体文件到本地 二…

【undefined reference to xxx】zookeeper库编译和安装 / sylar项目ubuntu20系统编译

最近学习sylar项目&#xff0c;编译项目时遇到链接库不匹配的问题&#xff0c;记录下自己解决问题过程&#xff0c;虽然过程很艰难&#xff0c;但还是解决了&#xff0c;以下内容供大家参考&#xff01; undefined reference to 问题分析 项目编译报错 /usr/bin/ld: ../lib/lib…

sql进阶篇

1.更新记录 AC&#xff1a; update examination_info set tag replace(tag, "PYTHON", "Python") where tag "PYTHON";2.删除记录 AC&#xff1a; DELETE FROM exam_record WHERE timestampdiff(minute, start_time, submit_time) < 5AND…

【每日刷题】Day145

【每日刷题】Day145 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 375. 猜数字大小 II - 力扣&#xff08;LeetCode&#xff09; 2. LCR 112. 矩阵中的最长递增路径 …

【tomcat系列漏洞利用】

Tomcat 服务器是一个开源的轻量级Web应用服务器&#xff0c;在中小型系统和并发量小的场合下被普遍使用。主要组件&#xff1a;服务器Server&#xff0c;服务Service&#xff0c;连接器Connector、容器Container。连接器Connector和容器Container是Tomcat的核心。一个Container…

WPF的行为(Behavior)

WPF&#xff08;Windows Presentation Foundation&#xff09;是微软.NET框架中用于构建Windows客户端应用程序的UI框架。它提供了一种声明性的方式来定义用户界面&#xff0c;并且支持MVVM&#xff08;Model-View-ViewModel&#xff09;设计模式。 在WPF中&#xff0c;“行为…

【LeetCode】两数之和、大数相加

主页&#xff1a;HABUO&#x1f341;主页&#xff1a;HABUO 1.两数之和 题目&#xff1a;给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一…

React核心思维模型(一)

一、数据和视图分离&#xff0c;数据改变驱动视图更新 <div>Tom</div>如果我们想修改上述div盒子中的Tom为Jerry&#xff0c;应该怎样修改呢 在jquery中我们直接把界面元素抓过来修改 document.getElementsByTagName(div).item(0) Jerry 但在react中&#xf…

DataSophon集成ApacheImpala的过程

注意: 本次安装操作系统环境为Anolis8.9(Centos7和Centos8应该也一样) DataSophon版本为DDP-1.2.1 整合的安装包我放网盘了: 通过网盘分享的文件&#xff1a;impala-4.4.1.tar.gz等2个文件 链接: https://pan.baidu.com/s/18KfkO_BEFa5gVcc16I-Yew?pwdza4k 提取码: za4k 1…

使用 Kibana 将地理空间数据导入 Elasticsearch 以供 ES|QL 使用

作者&#xff1a;来自 Elastic Craig Taverner 如何使用 Kibana 和 csv 采集处理器将地理空间数据采集到 Elasticsearch 中&#xff0c;以便在 Elasticsearch 查询语言 (ES|QL) 中进行搜索。Elasticsearch 具有强大的地理空间搜索功能&#xff0c;现在 ES|QL 也具备这些功能&am…

ffmpeg视频滤镜:定向模糊-dblur

滤镜简述 dblur 官网链接 > https://ffmpeg.org/ffmpeg-filters.html#dblur 有一个模糊滤镜&#xff0c;我试了一下&#xff0c;没有感觉到它的特殊之处, 这里简单介绍一下。 滤镜使用 滤镜的参数 angle <float> ..FV.....T. set angle (from 0 t…

浏览器HTTP缓存解读(HTTP Status:200 304)

为什么要有浏览器缓存&#xff1f; 浏览器缓存(Brower Caching)是浏览器对之前请求过的文件进行缓存&#xff0c;以便下一次访问时重复使用&#xff0c;节省带宽&#xff0c;提高访问速度&#xff0c;降低服务器压力 http缓存机制主要在http响应头中设定&#xff0c;响应头中…