初识Java并发,一问读懂Java并发知识文集(4)

在这里插入图片描述

🏆作者简介,普修罗双战士,一直追求不断学习和成长,在技术的道路上持续探索和实践。
🏆多年互联网行业从业经验,历任核心研发工程师,项目技术负责人。
🎉欢迎 👍点赞✍评论⭐收藏

Java注解知识专栏学习

Java并发知识云集访问地址备注
Java并发知识点(1)https://blog.csdn.net/m0_50308467/article/details/135216289Java并发专栏
Java并发知识点(2)https://blog.csdn.net/m0_50308467/article/details/135260439Java并发专栏
Java并发知识点(3)https://blog.csdn.net/m0_50308467/article/details/135301948Java并发专栏
Java并发知识点(4)https://blog.csdn.net/m0_50308467/article/details/135357383Java并发专栏

文章目录

  • 🏆 初识Java并发编程
    • 🔎 Java 并发编程面试题(4)
      • 🍁🍁 01、线程之间是如何通信的?
      • 🍁🍁 02、如何创建守护线程?
      • 🍁🍁 03、如何确保线程安全?
      • 🍁🍁 04、Java中的ReadWriteLock是什么?
      • 🍁🍁 05、volatile变量和atomic变量有什么不同?
      • 🍁🍁 06、可以直接调用Thread类的run()方法么?
      • 🍁🍁 07、如何让正在运行的线程暂停一段时间?
      • 🍁🍁 08、你对线程优先级的理解是什么?
      • 🍁🍁 09、什么是线程调度器(ThreadScheduler)和时间分片(TimeSlicing)?
      • 🍁🍁 10、你如何确保main()方法所在的线程是Java程序最后结束的线程?
      • 🍁🍁 11、为什么线程通信的方法wait(),notify()和notifyAll()被定义在Object类里?
      • 🍁🍁 12、为什么wait(),notify()和notifyAll()必须在同步方法或者同步块中被调用?
      • 🍁🍁 13、为什么Thread类的sleep()和yield()方法是静态的?
      • 🍁🍁 14、同步方法和同步块,哪个是更好的选择?
      • 🍁🍁 15、什么是JavaTimer类?如何创建一个有特定时间间隔的任务?

🏆 初识Java并发编程

🔎 Java 并发编程面试题(4)

🍁🍁 01、线程之间是如何通信的?

线程之间可以通过以下方式进行通信:

  • 共享内存:共享内存是指多个线程可以访问的同一块内存区域。线程可以通过直接访问共享内存来进行通信。
  • 信号量:信号量是一个计数器,它可以用来控制对共享资源的访问。线程可以通过获取或释放信号量来进行通信。
  • 管道:管道是一个双向数据流,它可以用来在两个线程之间传递数据。
  • 消息队列:消息队列是一个先进先出(FIFO)的数据结构,它可以用来在两个线程之间传递数据。
  • 同步对象:同步对象是指可以用来同步多个线程的对象。线程可以通过同步对象来等待其他线程完成某些操作。

在实际开发中,我们通常会使用共享内存、信号量、管道或消息队列来实现线程之间的通信。

在Java程序中,线程之间可以通过共享变量的方式来进行通信。线程之间共享变量时,需要注意多线程并发访问共享变量可能会导致数据不一致的问题,需要使用同步机制来保证共享变量的安全访问。

常用的同步机制有以下几种:

  1. synchronized 关键字: 通过 synchronized 关键字来控制对共享变量的访问,保证在同一时刻只有一个线程可以访问共享变量。

  2. wait()、notify()、notifyAll() 方法: 通过 Object 对象的 wait()、notify()、notifyAll() 方法来实现线程的等待、唤醒等操作。

  3. Lock 接口: 使用 Lock 接口和 ReentrantLock 类可以实现更细粒度的同步控制。

这些同步机制可以保证线程之间共享变量的安全访问和互相协作。此外,Java 中还提供了一些高级的线程通信机制和工具,如 CountDownLatch、Semaphore、CyclicBarrier 等,这些工具可以方便地实现线程之间的控制和协作。

🍁🍁 02、如何创建守护线程?

在 Java 中,可以通过以下方式创建守护线程:

  • 在构造方法中将 daemon 参数设置为 true
  • setDaemon() 方法中将 daemon 参数设置为 true

以下是一个创建守护线程的示例:

public class DaemonThread extends Thread {public DaemonThread() {super("DaemonThread");setDaemon(true);}@Overridepublic void run() {while (true) {System.out.println("I am a daemon thread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}public static void main(String[] args) {DaemonThread daemonThread = new DaemonThread();daemonThread.start();}
}

运行这个程序,会看到如下输出:

I am a daemon thread
I am a daemon thread
I am a daemon thread
...

可以看到,守护线程会一直运行,直到程序结束。

🍁🍁 03、如何确保线程安全?

在 Java 中,可以通过以下方式确保线程安全:

  • 使用 synchronized 关键字。
  • 使用 volatile 关键字。
  • 使用 Lock 对象。

以下是这三种方法的详细介绍:

  • 使用 synchronized 关键字

synchronized 关键字可以保证在同一时间只有一个线程可以访问某个对象。如果多个线程同时访问一个 synchronized 对象,则只有一个线程可以获得该对象的锁,其他线程必须等待该线程释放锁之后才能获得锁。

以下是一个使用 synchronized 关键字来保证线程安全的示例:

public class Counter {private int count = 0;public synchronized void increment() {count++;}public int getCount() {return count;}
}

在上述代码中, increment() 方法使用了 synchronized 关键字,因此在同一时间只有一个线程可以调用该方法。这保证了 count 变量的值不会被多个线程同时修改。

  • 使用 volatile 关键字

volatile 关键字可以保证在同一时间所有线程都看到共享变量的最新值。如果多个线程同时访问一个 volatile 变量,则每个线程都会看到其他线程对该变量所做的最新修改。

以下是一个使用 volatile 关键字来保证线程安全的示例:

public class Counter {private volatile int count = 0;public void increment() {count++;}public int getCount() {return count;}
}

在上述代码中, count 变量使用了 volatile 关键字,因此在同一时间所有线程都看到 count 变量的最新值。这保证了 count 变量的值不会被多个线程同时修改。

  • 使用 Lock 对象

Lock 对象是一个更灵活的线程同步机制。 Lock 对象可以提供比 synchronized 关键字更细粒度的控制。

以下是一个使用 Lock 对象来保证线程安全的示例:

public class Counter {private final Lock lock = new ReentrantLock();private int count = 0;public void increment() {lock.lock();try {count++;} finally {lock.unlock();}}public int getCount() {return count;}
}

在上述代码中, lock 变量是一个 Lock 对象。 increment() 方法使用 lock.lock() 方法获取 lock 对象的锁,然后使用 count++ 方法将 count 变量的值加 1。最后,使用 lock.unlock() 方法释放 lock 对象的锁。

这三种方法都可以保证线程安全。在实际开发中,我们可以根据具体的情况选择合适的方法。

🍁🍁 04、Java中的ReadWriteLock是什么?

ReadWriteLock 是 Java 并发包中的一种锁,它可以让多个线程同时读取共享资源,但只能有一个线程可以写入共享资源。

ReadWriteLock 由两个锁组成:读锁和写锁。读锁允许多个线程同时读取共享资源,但不允许写入共享资源。写锁则只允许一个线程写入共享资源。

ReadWriteLock 可以用来实现读写分离,即读操作和写操作使用不同的锁。这样可以提高并发性,因为读操作不需要锁,因此可以同时进行。

ReadWriteLock 的使用方法如下:

  1. 创建一个 ReadWriteLock 对象。
  2. 获取读锁。
  3. 执行读操作。
  4. 释放读锁。
  5. 获取写锁。
  6. 执行写操作。
  7. 释放写锁。

以下是一个使用 ReadWriteLock 实现读写分离的示例:

public class ReadWriteLockDemo {private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();private final Map<String, String> map = new HashMap<>();public String get(String key) {readWriteLock.readLock().lock();try {return map.get(key);} finally {readWriteLock.readLock().unlock();}}public void put(String key, String value) {readWriteLock.writeLock().lock();try {map.put(key, value);} finally {readWriteLock.writeLock().unlock();}}
}

在上述代码中, readWriteLock 对象是一个 ReadWriteLock 对象。 get() 方法获取读锁,然后读取 map 中的值。 put() 方法获取写锁,然后将值写入 map 中。

ReadWriteLock 可以提高并发性,但它也有一定的缺点。例如,如果有多个线程同时获取读锁,那么这些线程可能会阻塞,直到第一个线程释放读锁。

在实际开发中,我们可以根据具体的情况选择是否使用 ReadWriteLock。如果读操作比较频繁,写操作比较少,那么可以使用 ReadWriteLock。如果读写操作都比较频繁,那么可以使用 synchronized 或 Lock。

🍁🍁 05、volatile变量和atomic变量有什么不同?

volatile变量和Atomic变量(如AtomicIntegerAtomicLong等)都可以用于多线程环境下保证变量的可见性和原子性,但它们在实现机制和使用方式上有一些不同。

1. 可见性volatile变量保证了可见性,即一个线程对volatile变量的修改对其他线程是可见的。而Atomic变量也提供了可见性,但是更多是通过底层的内存屏障(memory barrier)和原子操作实现的。

2. 原子性volatile变量只能保证单个变量的读写操作是原子性的,但是对于复合操作(比如i++这样的自增操作)无法保证原子性。而Atomic变量则提供了一些原子操作的方法,如getAndIncrement()compareAndSet()等,可以保证多个操作的原子性。

3. 使用方式volatile变量的使用比较简单,只需要声明为volatile类型即可,对该变量的读写操作都会具有可见性。而Atomic变量需要通过调用相应的原子操作方法来实现原子性操作。

总的来说,volatile变量适用于只有简单的读写操作的场景,并且对于并发性能要求不高的情况下使用。而Atomic变量则适用于对多个操作需要保证原子性的场景,可以方便地进行原子操作。

以下是volatile变量和Atomic变量的区别的表格说明:

特性volatile变量Atomic变量
可见性保证可见性保证可见性
原子性不保证原子性提供一些原子操作方法,保证多个操作的原子性
使用方式简单,只需声明为volatile类型通过调用原子操作方法实现原子性操作
并发性能要求适用于并发性能要求不高的情况适用于需要保证多个操作的原子性和高并发性能的情况

从上表可以看出,volatile变量适用于简单的读写操作,并且对并发性能要求不高的情况下使用。它可以保证可见性,但不能保证复合操作的原子性。而Atomic变量则提供了一些原子操作方法,能够保证多个操作的原子性,适用于需要高并发性能和原子性操作的场景。

🍁🍁 06、可以直接调用Thread类的run()方法么?

可以,但是不建议直接调用 Thread 类的 run() 方法。

Thread 类的 run() 方法是一个普通的方法,它不会启动一个新的线程。如果直接调用 Thread 类的 run() 方法,那么这个方法会在当前线程中执行,而不是在一个新的线程中执行。

如果需要启动一个新的线程,可以使用 Thread 类的 start() 方法。Thread 类的 start() 方法会创建一个新的线程,并在该线程中执行 run() 方法。

以下是一个直接调用 Thread 类的 run() 方法的示例:

public class MyThread extends Thread {@Overridepublic void run() {System.out.println("Hello, world!");}public static void main(String[] args) {MyThread myThread = new MyThread();myThread.run();}
}

在上述代码中,MyThread 类继承了 Thread 类,并重写了 run() 方法。run() 方法中打印了一条消息。

在 main() 方法中,创建了一个 MyThread 对象,并调用了它的 run() 方法。由于 MyThread 类继承了 Thread 类,因此它的 run() 方法实际上是调用了 Thread 类的 run() 方法。

由于 Thread 类的 run() 方法是一个普通的方法,它会在当前线程中执行,而不是在一个新的线程中执行。因此,在上述代码中,run() 方法会在 main() 方法所在的线程中执行,而不是在一个新的线程中执行。

如果我们希望在一个新的线程中执行 run() 方法,那么应该使用 Thread 类的 start() 方法。Thread 类的 start() 方法会创建一个新的线程,并在该线程中执行 run() 方法。

以下是一个使用 Thread 类的 start() 方法的示例:

public class MyThread extends Thread {@Overridepublic void run() {System.out.println("Hello, world!");}public static void main(String[] args) {MyThread myThread = new MyThread();myThread.start();}
}

在上述代码中,MyThread 类继承了 Thread 类,并重写了 run() 方法。run() 方法中打印了一条消息。

在 main() 方法中,创建了一个 MyThread 对象,并调用了它的 start() 方法。由于 Thread 类的 start() 方法会创建一个新的线程,因此 run() 方法会在一个新的线程中执行。

因此,使用 Thread 类的 start() 方法可以启动一个新的线程,并在该线程中执行 run() 方法。

🍁🍁 07、如何让正在运行的线程暂停一段时间?

有几种方法可以让正在运行的线程暂停一段时间。

  • 使用 Thread.sleep() 方法Thread.sleep() 方法可以让线程暂停指定的时间。
  • 使用 Thread.yield() 方法Thread.yield() 方法可以让线程暂停,并让其他线程有机会运行。
  • 使用 synchronized 关键字synchronized 关键字可以让线程在获取锁之前暂停。
  • 使用 wait() 方法wait() 方法可以让线程在等待某个条件满足之前暂停。
  • 使用 join() 方法join() 方法可以让线程等待另一个线程结束。

在实际开发中,我们可以根据具体的情况选择合适的方法。

以下是几种让线程暂停一段时间的方法的示例代码:

1. 使用 Thread.sleep() 方法:

public class SleepExample {public static void main(String[] args) {System.out.println("Thread started");try {Thread.sleep(2000); // 暂停2秒} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Thread resumed");}
}

在上述代码中,使用 Thread.sleep(2000) 让线程暂停2秒。

2. 使用 Thread.yield() 方法:

public class YieldExample {public static void main(String[] args) {System.out.println("Thread 1 started");Thread.yield(); // 暂停当前线程,让其他线程有机会运行System.out.println("Thread 1 resumed");System.out.println("Thread 2 started");System.out.println("Thread 2 resumed");}
}

在上述代码中,使用 Thread.yield() 让线程暂停,并让其他线程有机会运行。

3. 使用 synchronized 关键字:

public class SynchronizedExample {public static void main(String[] args) {System.out.println("Thread 1 started");synchronized (SynchronizedExample.class) {// 临界区代码System.out.println("Thread 1 resumed");}System.out.println("Thread 2 started");System.out.println("Thread 2 resumed");}
}

在上述代码中,使用 synchronized 关键字让线程在获取锁之前暂停。

4. 使用 wait() 方法:

public class WaitExample {public static void main(String[] args) {final Object lock = new Object();Thread thread1 = new Thread(() -> {synchronized (lock) {try {System.out.println("Thread 1 started");lock.wait(); // 线程1等待System.out.println("Thread 1 resumed");} catch (InterruptedException e) {e.printStackTrace();}}});Thread thread2 = new Thread(() -> {synchronized (lock) {System.out.println("Thread 2 started");lock.notify(); // 唤醒线程1System.out.println("Thread 2 resumed");}});thread1.start();thread2.start();}
}

在上述代码中,使用 wait() 方法让线程在等待某个条件满足之前暂停,并使用 notify() 方法唤醒等待的线程。

5. 使用 join() 方法:

public class JoinExample {public static void main(String[] args) {Thread thread1 = new Thread(() -> {System.out.println("Thread 1 started");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Thread 1 resumed");});Thread thread2 = new Thread(() -> {System.out.println("Thread 2 started");try {thread1.join(); // 等待线程1结束} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Thread 2 resumed");});thread1.start();thread2.start();}
}

在上述代码中,使用 join() 方法让线程等待另一个线程结束,然后再继续执行。

以上示例代码展示了不同方法让线程暂停一段时间的方式。根据具体的需求,选择合适的方法来实现线程的暂停。

🍁🍁 08、你对线程优先级的理解是什么?

线程优先级是操作系统调度线程执行的相对重要性的指示。每个线程都可以具有不同的优先级,优先级较高的线程在调度时会更有可能被选择执行。

线程的优先级可以通过设置 Thread 类的 setPriority() 方法来指定,优先级范围从 1 到 10,其中 1 表示最低优先级,10 表示最高优先级。默认情况下,线程的优先级与创建它的父线程相同。

然而,需要注意的是,线程优先级仅是给操作系统的一个建议,操作系统可以根据自身的调度策略来决定是否遵循线程优先级。不同的操作系统可能对线程优先级的处理方式有所差异。

线程优先级的使用要谨慎,因为过分依赖线程优先级可能导致不可预测的结果,并且在不同的操作系统上可能表现不一致。在编写多线程应用程序时,更重要的是通过合理的设计和同步机制来确保正确的线程行为,而不是过度依赖线程优先级。

🍁🍁 09、什么是线程调度器(ThreadScheduler)和时间分片(TimeSlicing)?

线程调度器(Thread Scheduler) 是操作系统中的一个组件,负责决定在多线程环境下,哪个线程获得 CPU 的执行时间。它根据一定的调度算法,将 CPU 时间划分成多个时间片,并按照一定的策略分配给各个线程,以实现多线程的并发执行。

时间分片(Time Slicing) 是线程调度器的一种策略,它将 CPU 时间划分为多个固定大小的时间片,每个时间片分配给一个线程执行。当一个线程的时间片用完后,线程调度器会暂停该线程的执行,并切换到下一个就绪的线程继续执行。这种轮流分配时间片的方式使得多个线程看起来是同时执行的。

时间分片 的优势是可以使多个线程共享 CPU 资源,实现并发执行,提高系统的吞吐量和响应性。它能够公平地分配 CPU 时间给各个线程,避免某个线程长时间占用 CPU 而导致其他线程无法执行的情况。

线程调度器和时间分片 是操作系统中实现多线程并发的重要机制。通过合理的调度算法和时间分片策略,线程调度器可以高效地管理多个线程的执行,使得多线程程序能够充分利用 CPU 资源,提高系统的性能和响应速度。

🍁🍁 10、你如何确保main()方法所在的线程是Java程序最后结束的线程?

在 Java 中,可以使用 Thread.join() 方法来确保 main() 方法所在的线程是程序中最后结束的线程。 join() 方法会使当前线程等待调用该方法的线程执行完毕。

以下是一个示例代码,演示如何使用 join() 方法来确保 main() 方法所在的线程是程序中最后结束的线程:

public class MainThreadExample {public static void main(String[] args) throws InterruptedException {Thread thread1 = new Thread(() -> {System.out.println("Thread 1 started");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Thread 1 finished");});Thread thread2 = new Thread(() -> {System.out.println("Thread 2 started");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Thread 2 finished");});thread1.start();thread2.start();// 等待 thread1 和 thread2 执行完毕thread1.join();thread2.join();System.out.println("Main thread finished");}
}

在上述代码中,创建了两个线程 thread1thread2 ,它们分别执行一些任务。在 main() 方法中,通过调用 join() 方法,使主线程等待 thread1thread2 执行完毕。只有在这两个线程执行完毕后,主线程才会继续执行,输出 “Main thread finished”。

通过使用 join() 方法,可以确保 main() 方法所在的线程是程序中最后结束的线程。

🍁🍁 11、为什么线程通信的方法wait(),notify()和notifyAll()被定义在Object类里?

线程通信的方法 wait() , notify()notifyAll() 被定义在 Object 类中是因为线程通信是基于对象的锁机制实现的。

在 Java 中,每个对象都有一个内部锁(也称为监视器锁或互斥锁),用于控制对对象的访问。当一个线程持有对象的锁时,其他线程需要等待该锁释放才能访问对象。

wait() , notify()notifyAll() 方法是与对象的锁紧密相关的。这些方法必须在持有对象的锁的情况下调用,否则会抛出 IllegalMonitorStateException 异常。

  • wait() 方法使当前线程进入等待状态,同时释放对象的锁,直到其他线程调用该对象的 notify()notifyAll() 方法来唤醒等待的线程。
  • notify() 方法唤醒在该对象上等待的一个线程,使其进入就绪状态,但不会立即释放对象的锁。
  • notifyAll() 方法唤醒在该对象上等待的所有线程,使它们进入就绪状态,但不会立即释放对象的锁。

由于每个对象都有这些基本的线程通信方法,因此它们被定义在 Object 类中,以便所有的对象都可以使用这些方法进行线程之间的通信。这种设计使得任何对象都可以作为线程间通信的锁和条件变量的持有者。

🍁🍁 12、为什么wait(),notify()和notifyAll()必须在同步方法或者同步块中被调用?

wait(), notify()notifyAll() 方法必须在同步方法或同步块中被调用,是因为这些方法涉及线程之间的协作和共享资源的同步。

在Java中,对象的监视器(通常是对象本身)用于实现线程之间的同步。同步方法和同步块使用监视器来保护共享资源的访问并确保线程的互斥执行。

在同步方法或同步块中调用wait()方法会导致当前线程释放对监视器的持有,并进入阻塞状态,等待其他线程发出的通知。当其他线程通过notify()notifyAll()方法唤醒了等待的线程后,被唤醒的线程才能重新获得监视器并继续执行。

如果不在同步方法或同步块中调用这些方法,将会抛出IllegalMonitorStateException异常,因为这些方法的调用依赖于监视器的存在和有效的获取。

通过在同步方法或同步块中使用wait(), notify()notifyAll(),可以确保线程之间正确地进行等待和唤醒,并且保护共享资源的一致性和正确性。同步机制使得线程之间能够有序地进行协作,避免了竞态条件和数据不一致的问题。

🍁🍁 13、为什么Thread类的sleep()和yield()方法是静态的?

Thread类的sleep()方法和yield()方法是静态的,是因为它们不是针对特定线程对象的操作,而是作用于正在执行的当前线程或当前线程所属的线程组。

  1. sleep()方法:线程通过Thread.sleep(long millis)方法在指定的时间内暂停执行,进入阻塞状态。这个方法是静态的,因为它不会直接操作线程对象,而是让当前正在执行的线程暂停执行。可以使用Thread.sleep()方法模拟定时任务、线程调度等需求。

  2. yield()方法:线程通过Thread.yield()方法告诉调度器当前线程可以放弃当前CPU时间片,将自己重新放回就绪状态,让其他线程有机会执行。这个方法是静态的,因为它不会直接操作线程对象,而是操作当前线程的调度状态。

由于sleep()yield()方法不依赖于特定的线程对象,所以将它们定义为静态方法更为合理。通过静态方法的方式,可以直接通过Thread.sleep()Thread.yield()调用这些方法,无需先创建Thread对象。

需要注意的是,虽然这些方法是静态的,但它们仍然会影响到调用它们的当前线程。因此,它们的使用需要谨慎,需要在适当的时机和场景下使用,并考虑线程安全和协调性。

🍁🍁 14、同步方法和同步块,哪个是更好的选择?

同步方法和同步块都可以用于实现线程安全和共享资源的同步,它们各自有不同的适用场景和优缺点。

  1. 同步方法:

    • 优点:使用同步方法简单,只需在方法声明上加上synchronized关键字,无需显式地获取和释放锁,编码更加简洁。同时,同步方法可以保证整个方法的执行过程是原子性的,即线程在执行同步方法期间,其他线程无法进入该方法。
    • 缺点:同步方法只能对整个方法进行同步,无法对方法中的某一部分代码进行精确控制,可能会导致过度同步。另外,当多个线程同时访问不同的同步方法,由于使用的是同一个锁(对象的监视器),可能会导致一些线程不必要地等待。
  2. 同步块:

    • 优点:同步块可以对指定的代码块进行同步,灵活性更高。可以使用不同的锁对象来控制同步粒度,从而提高并发性能。此外,同步块还可以通过使用Lock接口的实现类提供更细粒度的锁控制。
    • 缺点:同步块需要显式地获取和释放锁,代码编写稍微复杂一些。若使用不当,可能会导致死锁、竞态条件等问题。

综上所述,选择使用同步方法还是同步块取决于具体的场景和需求。通常情况下,推荐使用同步方法,因为它简单、易于理解和实现。但对于需要更细粒度控制的同步需求,或者需要使用多个锁对象来提高并发性能的情况下,同步块更适合。同时,还可以考虑使用Lock接口的实现类提供更高级的锁定机制。

🍁🍁 15、什么是JavaTimer类?如何创建一个有特定时间间隔的任务?

java.util.Timer类是Java中的一个定时器工具,用于安排在将来的指定时间执行任务。它可以用来定期执行任务,或者在指定的时间点执行任务。

要创建一个具有特定时间间隔的任务,可以按照以下步骤进行操作:

1. 创建一个继承自java.util.TimerTask的任务类,并实现run()方法,在该方法中定义要执行的任务逻辑。

import java.util.TimerTask;public class MyTask extends TimerTask {public void run() {// 执行任务的逻辑}
}

2. 创建一个java.util.Timer对象。

import java.util.Timer;Timer timer = new Timer();

3. 使用schedule()方法安排任务的执行。 该方法有多个重载形式,可以根据需求选择适合的方法。

以下是一种常用的方式,创建一个每隔一定时间执行一次的任务:

MyTask task = new MyTask();
long delay = 0; // 初始延迟时间(单位:毫秒)
long period = 1000; // 执行间隔时间(单位:毫秒)
timer.schedule(task, delay, period);

上述代码将会创建一个定时任务,初始延迟0毫秒后开始执行,然后每隔1000毫秒执行一次。

需要注意的是,java.util.Timer类是单线程的,如果一个任务执行的时间过长,可能会影响后续任务的执行。另外,如果任务抛出未捕获的异常,整个定时器将会停止运行。

为了更好地应对复杂的定时任务需求,Java 5开始引入了更强大的ScheduledExecutorService接口,通常被认为是java.util.Timer的替代品。它提供了更灵活的执行方式和更好的错误处理机制。

在这里插入图片描述

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

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

相关文章

智能标志桩:防盗防外物入侵_图像监测_态势感知_深圳鼎信

智能标志桩是一种新型的智能化标志设备&#xff0c;主要用于标识地下管道的位置、类型等&#xff0c;起警示作用。这与传统的标志桩大不相同&#xff0c;物联网的高速发展赋予了智能标志桩科技的力量&#xff0c;使它可以连接互联网&#xff0c;还具备图像监控的功能&#xff0…

2024年P气瓶充装证考试题库及P气瓶充装试题解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年P气瓶充装证考试题库及P气瓶充装试题解析是安全生产模拟考试一点通结合&#xff08;安监局&#xff09;特种作业人员操作证考试大纲和&#xff08;质检局&#xff09;特种设备作业人员上岗证考试大纲随机出的P气…

C++Qt6 多种排序算法的比较 数据结构课程设计 | JorbanS

一、 问题描述 在计算机科学与数学中&#xff0c;一个排序算法&#xff08;英语&#xff1a;Sorting algorithm&#xff09;是一种能将一串资料依照特定排序方式排列的算法。最常用到的排序方式是数值顺序以及字典顺序。有效的排序算法在一些算法&#xff08;例如搜索算法与合…

Linux系统操作常用指令

打开终端&#xff1a; ctrlshiftt:切换标签 ctrlshiftn:新增窗口 Linux命令大全(超详细版)_第二范式的博客-CSDN博客 VMware运行时以管理员身份运行&#xff0c;可以避免许多问题。 输入法切换 ctrl 空格 放大终端&#xff1a;ctrlshift"" 缩小终端&#xff1a;ctrl“…

【深度学习-基础学习】Transformer 笔记

本篇文章学习总结 李宏毅 2021 Spring 课程中关于 Transformer 相关的内容。课程链接以及PPT&#xff1a;李宏毅Spring2021ML这篇Blog需要Self-Attention为前置知识。 Transfomer 简介 Transfomer 架构主要是用来解决 Seq2Seq 问题的&#xff0c;也就是 Sequence to Sequence…

web前端——clear可以清除浮动产生的影响

clear可以解决高度塌陷的问题&#xff0c;产生的副作用要小 未使用clear之前 <!DOCTYPE html> <head><meta charset"UTF-8"><title>高度塌陷相关学习</title><style>div{font-size:50px;}.box1{width:200px;height:200px;backg…

【已解决】打印PDF文件,如何跳过不需要的页面?

打印PDF文件的时候&#xff0c;有时候我们只需要打印其中的几页&#xff0c;并不需要全部打印&#xff0c;那如何在打印时跳过那些不需要的页面呢&#xff1f;不清楚的小伙伴一起来看看吧&#xff01; 如果你是通过网页打开PDF文件&#xff0c;那么可以在页面中找到并点击“打…

[每周一更]-(第53期):Python3和Django环境安装并搭建Django

Python和Django 的安装 Python和Django 兼容情况 django 1.11.x python 2.7 3.4 3.5 3.6 LTS python 目前在用版本 Python 3.6.5 2018-03-28 更新Python 2.7.15 2018-05-01 更新Python 2.7.5 2013-05-12 更新 python和python3安装pip 同时安装上 python2.7.18、python3.11…

c语言结构体学习上篇

文章目录 前言一、结构体的声明1&#xff0c;什么叫结构体?2&#xff0c;结构体的类型3,结构体变量的创建和初始化4&#xff0c;结构体的类型5&#xff0c;结构体的初始化 二、结构体的访问1&#xff0c;结构体成员的点操作符访问2&#xff0c;结构体体成员的指针访问 前言 昨…

用户管理第2节课--idea 2023.2 后端--实现基本数据库操作(操作user表) -- 自动生成 --【本人】

一、插件安装 1.1 搜索插件 mybatis 安装 1.2 接受安装 1.3 再次进入&#xff0c;说明安装好了 1.4 与鱼皮不同点 1&#xff09;mybatis 版本不一致 鱼皮&#xff1a; 本人&#xff1a; 2&#xff09;鱼皮需重启安装 本人不需要 1.5 【需完成 三、步骤&#xff0c;再来看】 …

Git - 强制替换覆盖 master 分支解决方案

问题描述 在版本迭代中&#xff0c;通常会保持一个主分支 master&#xff0c;及多个 dev 分支&#xff0c;但是因为 dev 分支的开发周期过长&#xff0c;迭代太多而没有及时维护 master &#xff0c;导致后来发版上线的大部分代码都在 dev 分支上&#xff0c;如果将代码在 mas…

LiveGBS流媒体平台GB/T28181功能-用户管理通道权限管理关联通道支持只看已选只看未选添加用户备注角色

LiveGBS功能用户管理通道权限管理关联通道支持只看已选只看未选添加用户备注角色 1、用户管理2、添加用户3、关联通道3.1、只看已选3.2、只看未选 4、自定义角色5、搭建GB28181视频直播平台 1、用户管理 LiveGBS支持用户管理&#xff0c;添加用户&#xff0c;及配置相关用户权…

promise.prototype.finally重写和兼容火狐低版本浏览器

一、finally()方法用于指定不管 Promise 对象最后状态如何&#xff0c;都会执行的操作。该方法是 ES2018 引入标准的 let promise new Promise() promise .then(result > {}) .catch(error > {}) .finally(() > {})finally方法的回调函数不接受任何参数;finally方法…

element-ui Tree 树形控件 过滤保留子级并获取过滤后的数据

本示例基于vue2 element-ui element-ui 的官网demo是只保留到过滤值一级的&#xff0c;并不会保留其子级 目标 1、Tree 树形控件 保留过滤值的子级 2、在第一次过滤数据的基础上进行第二次过滤 先看效果 Tree 树形控件 保留过滤值的子级 <el-treeclass"filter-t…

直观从零理解 梯度下降(Gradient descent) VS 随机梯度下降 (Stochastic gradient descent) 函数优化

首发于Data Science 单变量微分(Differentiation) 常用基本微分有&#xff1a; 四则运算法则&#xff1a; 链式法则(Chain-rule) 极大值(maxima)与极小值(minima) 向量微分 梯度下降(Gradient descent):几何直觉 学习率&#xff08;Learning Rate&#xff09;的直观理解…

笔记中所得(已删减)

1.交流电的一个周期内电压/电流的平均值都为0 2.电动势:电池将单位正电荷由负极搬到正极所做的功 5.额定能量:电池的额定容量乘以标称电压,以Wh为单位 6.500mAh意义是可以以500mA的电流放电1小时 7.电池容量的单位是mAh 13.实际电流源不能串联 14. 15. 16. 17. 18. 19.电…

C++ 二进制图片的读取和blob插入mysql_stmt_init—新年第一课

关于二进制图片的读取和BLOB插入一共包含五步 第一步&#xff1a;初始化 MYSQL_STMT* stmt mysql_stmt_init(&mysql); 第二步&#xff1a;预处理sql语句 mysql_stmt_prepare(stmt,sql,sqllen); 第三步&#xff1a;绑定字段 mysql_stmt_bind_param(stmt,bind); 第四…

车规级芯片NCV4275CDT50RKG LDO线性电压调节器 原理、参数及应用

关于车规级芯片&#xff08;Automotive Grade Chip&#xff09;&#xff0c;车规级芯片是专门用于汽车行业的芯片&#xff0c;具有高可靠性、高稳定性和低功耗等特点&#xff0c;以满足汽车电子系统的严格要求。这些芯片通常用于车载电子控制单元&#xff08;ECU&#xff09;和…

用ChatGPT方式编程!GitHub Copilot Chat全面开放使用

全球著名开源分享平台GitHub在官网宣布&#xff0c;经过几个月多轮测试的GitHub Copilot Chat&#xff0c;全面开放使用&#xff0c;一个用ChatGPT方式写代码的时代来啦&#xff01; 据悉&#xff0c;Copilot Chat是基于OpenAI的GPT-4模型&#xff0c;再结合其海量、优质的代码…

cJSON代码解读

1、背景 cJSON用了很久&#xff0c;但是对它一直不太了解。这次向添加对long long类型的支持&#xff0c;一直出问题。因为有以前添加两位小数float的经历&#xff0c;我觉得会很轻松&#xff0c;没想到翻车了。于是有了这边文档&#xff0c;阅读了部分博主对cJSON的解析&…