首页 > 编程语言 >Java多线程

Java多线程

时间:2024-12-19 19:54:54浏览次数:3  
标签:Java Thread void 线程 println new 多线程 public

多线程总结

Java中的多线程是Java编程语言中的一个重要特性,它允许多个线程同时执行,从而提高程序的性能和响应能力。多线程在处理并发任务、优化资源利用率以及构建高性能应用程序方面发挥着关键作用。本文将详细介绍Java中的多线程,包括其基本概念、线程的创建与管理、线程同步、并发工具类以及最佳实践等内容。

一、多线程基础

1. 什么是线程

线程是操作系统能够进行运算调度的最小单位,它是比进程更小的独立运行的基本单位。一个进程可以包含多个线程,这些线程共享进程的资源(如内存空间),但每个线程有自己独立的执行路径。

2. 多线程的优势

  • 提高程序性能:通过并行执行多个任务,充分利用多核处理器的能力。
  • 增强应用响应性:在图形用户界面(GUI)应用中,使用多线程可以防止界面冻结,提高用户体验。
  • 更好的资源利用:通过合理调度线程,可以更有效地使用系统资源,如CPU和内存。

3. 多线程的挑战

  • 线程安全:多个线程访问共享资源时,必须确保数据的一致性和完整性。
  • 死锁和竞态条件:不当的线程同步可能导致死锁或竞态条件,影响程序的稳定性。
  • 调试和维护复杂性:多线程程序的调试和维护比单线程程序更为复杂。

二、线程的创建与管理

Java提供了多种创建线程的方式,主要包括继承Thread类、实现Runnable接口以及使用CallableFuture接口。

1. 继承Thread

通过继承Thread类并重写run()方法来定义线程的任务。

示例:

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("线程 " + Thread.currentThread().getName() + " 正在运行");
    }
}

public class ThreadExample1 {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        t1.setName("线程1");
        t1.start();

        MyThread t2 = new MyThread();
        t2.setName("线程2");
        t2.start();
    }
}

输出:

线程 线程1 正在运行
线程 线程2 正在运行

2. 实现Runnable接口

通过实现Runnable接口并将其实例传递给Thread对象来创建线程。这种方式避免了单继承的限制,更加灵活。

示例:

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("线程 " + Thread.currentThread().getName() + " 正在运行");
    }
}

public class ThreadExample2 {
    public static void main(String[] args) {
        Thread t1 = new Thread(new MyRunnable(), "线程1");
        Thread t2 = new Thread(new MyRunnable(), "线程2");
        t1.start();
        t2.start();
    }
}

3. 使用CallableFuture

Callable接口类似于Runnable,但它可以返回结果并抛出异常。Future接口用于获取异步计算的结果。

示例:

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "Callable线程执行结果";
    }
}

public class ThreadExample3 {
    public static void main(String[] args) {
        Callable<String> callable = new MyCallable();
        FutureTask<String> futureTask = new FutureTask<>(callable);
        Thread t = new Thread(futureTask, "Callable线程");
        t.start();

        try {
            String result = futureTask.get(); // 获取返回结果
            System.out.println("返回结果: " + result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

输出:

返回结果: Callable线程执行结果

4. 使用线程池

线程池通过复用线程来提高性能,避免频繁创建和销毁线程的开销。Java的java.util.concurrent包提供了强大的线程池实现。

示例:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class Task implements Runnable {
    private String name;
    
    public Task(String name) {
        this.name = name;
    }
    
    @Override
    public void run() {
        System.out.println("任务 " + name + " 在 " + Thread.currentThread().getName() + " 执行");
    }
}

public class ThreadPoolExample {
    public static void main(String[] args) {
        // 创建一个固定大小的线程池
        ExecutorService executor = Executors.newFixedThreadPool(3);
        
        for (int i = 1; i <= 5; i++) {
            executor.execute(new Task("Task-" + i));
        }
        
        executor.shutdown(); // 关闭线程池
    }
}

输出:

任务 Task-1 在 pool-1-thread-1 执行
任务 Task-2 在 pool-1-thread-2 执行
任务 Task-3 在 pool-1-thread-3 执行
任务 Task-4 在 pool-1-thread-1 执行
任务 Task-5 在 pool-1-thread-2 执行

三、线程生命周期

线程在其生命周期中会经历多个状态:

  1. 新建(New):线程对象被创建,但尚未调用start()方法。
  2. 就绪(Runnable):线程已准备好运行,等待CPU调度。
  3. 运行(Running):线程正在执行其任务。
  4. 阻塞(Blocked/Waiting):线程因等待某个条件而暂停执行,如等待锁或等待某个事件。
  5. 终止(Terminated):线程的run()方法执行完毕或因异常终止。

1. 线程状态示例

示例:

public class ThreadLifecycleExample {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            System.out.println("线程状态: " + Thread.currentThread().getState());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "测试线程");
        
        System.out.println("线程状态: " + t.getState()); // NEW
        t.start();
        System.out.println("线程状态: " + t.getState()); // RUNNABLE
        
        try {
            t.join();
            System.out.println("线程状态: " + t.getState()); // TERMINATED
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

输出:

线程状态: NEW
线程状态: RUNNABLE
线程状态: TERMINATED

四、线程同步

在多线程环境中,多个线程可能会同时访问共享资源,导致数据不一致。为确保线程安全,需要对共享资源进行同步控制。

1. synchronized关键字

synchronized关键字可以用于方法或代码块,确保同一时刻只有一个线程可以执行被同步的代码。

示例:

class Counter {
    private int count = 0;
    
    // 同步方法
    public synchronized void increment() {
        count++;
    }
    
    public int getCount() {
        return count;
    }
}

public class SynchronizedExample {
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        
        Runnable task = () -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        };
        
        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);
        
        t1.start();
        t2.start();
        
        t1.join();
        t2.join();
        
        System.out.println("最终计数值: " + counter.getCount()); // 应为2000
    }
}

2. 同步代码块

除了同步整个方法,还可以同步方法中的某个代码块,以提高并发性能。

示例:

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

3. volatile关键字

volatile关键字用于声明变量在多个线程间的可见性,确保每个线程都读取到最新的变量值。但它不能保证原子性。

示例:

class Flag {
    private volatile boolean running = true;
    
    public void stop() {
        running = false;
    }
    
    public boolean isRunning() {
        return running;
    }
}

public class VolatileExample {
    public static void main(String[] args) throws InterruptedException {
        Flag flag = new Flag();
        
        Thread t = new Thread(() -> {
            while (flag.isRunning()) {
                // 执行任务
            }
            System.out.println("线程已停止");
        });
        
        t.start();
        Thread.sleep(1000);
        flag.stop();
    }
}

4. ReentrantLock

ReentrantLockjava.util.concurrent.locks包中的一个灵活的锁实现,提供了比synchronized更高级的功能,如可中断锁获取、公平锁等。

示例:

import java.util.concurrent.locks.ReentrantLock;

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

public class ReentrantLockExample {
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        
        Runnable task = () -> {
            for(int i=0; i<1000; i++) {
                counter.increment();
            }
        };
        
        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);
        
        t1.start();
        t2.start();
        
        t1.join();
        t2.join();
        
        System.out.println("最终计数值: " + counter.getCount()); // 应为2000
    }
}

五、Java并发工具类

Java的java.util.concurrent包提供了一系列并发工具类,简化了多线程编程中的同步和协调工作。

1. CountDownLatch

CountDownLatch用于让一个或多个线程等待,直到其他线程完成一组操作。

示例:

import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
    public static void main(String[] args) throws InterruptedException {
        int numWorkers = 3;
        CountDownLatch latch = new CountDownLatch(numWorkers);
        
        for(int i=1; i<=numWorkers; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + " 开始工作");
                try {
                    Thread.sleep((long)(Math.random() * 1000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " 完成工作");
                latch.countDown();
            }, "Worker-" + i).start();
        }
        
        latch.await(); // 等待所有工作线程完成
        System.out.println("所有工作线程已完成,继续执行主线程");
    }
}

2. Semaphore

Semaphore用于控制同时访问某个资源的线程数量,常用于限流和资源池管理。

示例:

import java.util.concurrent.Semaphore;

public class SemaphoreExample {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3); // 同时允许3个线程访问
        
        for(int i=1; i<=5; i++) {
            new Thread(() -> {
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + " 获取许可证,正在执行任务");
                    Thread.sleep(2000);
                    System.out.println(Thread.currentThread().getName() + " 释放许可证");
                    semaphore.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, "Thread-" + i).start();
        }
    }
}

3. CyclicBarrier

CyclicBarrier用于让一组线程在某个点上等待,直到所有线程都到达该点,然后同时继续执行。

示例:

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierExample {
    public static void main(String[] args) {
        int numParties = 3;
        CyclicBarrier barrier = new CyclicBarrier(numParties, () -> {
            System.out.println("所有线程已到达屏障,继续执行");
        });
        
        for(int i=1; i<=numParties; i++) {
            new Thread(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + " 正在执行任务");
                    Thread.sleep((long)(Math.random() * 1000));
                    System.out.println(Thread.currentThread().getName() + " 已到达屏障");
                    barrier.await();
                    System.out.println(Thread.currentThread().getName() + " 继续执行任务");
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }, "Thread-" + i).start();
        }
    }
}

4. BlockingQueue

BlockingQueue是一个支持阻塞操作的队列,常用于生产者-消费者模型。

示例:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

class Producer implements Runnable {
    private BlockingQueue<Integer> queue;
    
    public Producer(BlockingQueue<Integer> queue) {
        this.queue = queue;
    }
    
    @Override
    public void run() {
        try {
            for(int i=1; i<=5; i++) {
                System.out.println("生产者生产: " + i);
                queue.put(i);
                Thread.sleep(500);
            }
            queue.put(-1); // 结束标志
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class Consumer implements Runnable {
    private BlockingQueue<Integer> queue;
    
    public Consumer(BlockingQueue<Integer> queue) {
        this.queue = queue;
    }
    
    @Override
    public void run() {
        try {
            while(true) {
                int value = queue.take();
                if(value == -1) break;
                System.out.println("消费者消费: " + value);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class BlockingQueueExample {
    public static void main(String[] args) {
        BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);
        
        Thread producer = new Thread(new Producer(queue), "Producer");
        Thread consumer = new Thread(new Consumer(queue), "Consumer");
        
        producer.start();
        consumer.start();
    }
}

输出:

生产者生产: 1
消费者消费: 1
生产者生产: 2
消费者消费: 2
生产者生产: 3
消费者消费: 3
生产者生产: 4
消费者消费: 4
生产者生产: 5
消费者消费: 5

六、线程安全与最佳实践

1. 避免共享可变状态

尽量减少线程间共享的可变状态,使用不可变对象(如StringInteger)可以提高线程安全性。

2. 使用并发集合

Java提供了线程安全的集合类,如ConcurrentHashMapCopyOnWriteArrayList等,避免手动同步。

示例:使用ConcurrentHashMap

import java.util.concurrent.ConcurrentHashMap;

public class ConcurrentHashMapExample {
    public static void main(String[] args) throws InterruptedException {
        ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
        
        Runnable task = () -> {
            for(int i=0; i<1000; i++) {
                map.merge("key", 1, Integer::sum);
            }
        };
        
        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);
        
        t1.start();
        t2.start();
        
        t1.join();
        t2.join();
        
        System.out.println("最终值: " + map.get("key")); // 应为2000
    }
}

3. 避免死锁

  • 有序获取锁:确保多个线程以相同的顺序获取锁,避免循环等待。
  • 锁超时:使用带超时的锁获取方法,如tryLock(long timeout, TimeUnit unit),避免无限等待。

示例:使用tryLock避免死锁

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.TimeUnit;

public class DeadlockAvoidanceExample {
    private Lock lock1 = new ReentrantLock();
    private Lock lock2 = new ReentrantLock();
    
    public void methodA() {
        try {
            if(lock1.tryLock(1, TimeUnit.SECONDS)) {
                System.out.println(Thread.currentThread().getName() + " 获取了 lock1");
                Thread.sleep(500);
                if(lock2.tryLock(1, TimeUnit.SECONDS)) {
                    try {
                        System.out.println(Thread.currentThread().getName() + " 获取了 lock2");
                        // 执行任务
                    } finally {
                        lock2.unlock();
                    }
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if(lock1.isHeldByCurrentThread()) {
                lock1.unlock();
            }
        }
    }
    
    public void methodB() {
        try {
            if(lock2.tryLock(1, TimeUnit.SECONDS)) {
                System.out.println(Thread.currentThread().getName() + " 获取了 lock2");
                Thread.sleep(500);
                if(lock1.tryLock(1, TimeUnit.SECONDS)) {
                    try {
                        System.out.println(Thread.currentThread().getName() + " 获取了 lock1");
                        // 执行任务
                    } finally {
                        lock1.unlock();
                    }
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if(lock2.isHeldByCurrentThread()) {
                lock2.unlock();
            }
        }
    }
    
    public static void main(String[] args) {
        DeadlockAvoidanceExample example = new DeadlockAvoidanceExample();
        
        Thread t1 = new Thread(() -> {
            example.methodA();
        }, "Thread-1");
        
        Thread t2 = new Thread(() -> {
            example.methodB();
        }, "Thread-2");
        
        t1.start();
        t2.start();
    }
}

4. 使用高层并发API

尽量使用Java提供的高层并发API,如ExecutorServiceForkJoinPool等,简化多线程编程并提高性能。

七、线程通信

线程之间有时需要协同工作,通过通信机制可以实现线程间的数据共享和同步。

1. wait()notify()

使用对象的wait()notify()notifyAll()方法进行线程通信。这些方法必须在同步块或同步方法中使用。

示例:生产者-消费者模型

class SharedResource {
    private int data;
    private boolean available = false;
    
    public synchronized void produce(int value) throws InterruptedException {
        while(available) {
            wait();
        }
        data = value;
        available = true;
        notifyAll();
    }
    
    public synchronized int consume() throws InterruptedException {
        while(!available) {
            wait();
        }
        available = false;
        notifyAll();
        return data;
    }
}

public class WaitNotifyExample {
    public static void main(String[] args) {
        SharedResource resource = new SharedResource();
        
        // 生产者线程
        Thread producer = new Thread(() -> {
            for(int i=1; i<=5; i++) {
                try {
                    resource.produce(i);
                    System.out.println("生产者生产: " + i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        
        // 消费者线程
        Thread consumer = new Thread(() -> {
            for(int i=1; i<=5; i++) {
                try {
                    int value = resource.consume();
                    System.out.println("消费者消费: " + value);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        
        producer.start();
        consumer.start();
    }
}

输出:

生产者生产: 1
消费者消费: 1
生产者生产: 2
消费者消费: 2
生产者生产: 3
消费者消费: 3
生产者生产: 4
消费者消费: 4
生产者生产: 5
消费者消费: 5

2. BlockingQueue替代wait()notify()

使用BlockingQueue可以更简洁地实现生产者-消费者模型,无需手动管理wait()notify()

示例:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

class Producer implements Runnable {
    private BlockingQueue<Integer> queue;
    
    public Producer(BlockingQueue<Integer> queue) {
        this.queue = queue;
    }
    
    @Override
    public void run() {
        try {
            for(int i=1; i<=5; i++) {
                System.out.println("生产者生产: " + i);
                queue.put(i);
                Thread.sleep(500);
            }
            queue.put(-1); // 结束标志
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class Consumer implements Runnable {
    private BlockingQueue<Integer> queue;
    
    public Consumer(BlockingQueue<Integer> queue) {
        this.queue = queue;
    }
    
    @Override
    public void run() {
        try {
            while(true) {
                int value = queue.take();
                if(value == -1) break;
                System.out.println("消费者消费: " + value);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class BlockingQueueCommunicationExample {
    public static void main(String[] args) {
        BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);
        
        Thread producer = new Thread(new Producer(queue), "Producer");
        Thread consumer = new Thread(new Consumer(queue), "Consumer");
        
        producer.start();
        consumer.start();
    }
}

八、线程优先级与中断

1. 线程优先级

Java线程有优先级,范围从Thread.MIN_PRIORITY(1)到Thread.MAX_PRIORITY(10),默认为Thread.NORM_PRIORITY(5)。优先级高的线程更有可能获得CPU时间,但优先级并不保证执行顺序。

示例:

public class ThreadPriorityExample {
    public static void main(String[] args) {
        Runnable task = () -> {
            System.out.println("线程 " + Thread.currentThread().getName() + " 优先级: " + Thread.currentThread().getPriority());
        };
        
        Thread t1 = new Thread(task, "低优先级线程");
        Thread t2 = new Thread(task, "高优先级线程");
        
        t1.setPriority(Thread.MIN_PRIORITY);
        t2.setPriority(Thread.MAX_PRIORITY);
        
        t1.start();
        t2.start();
    }
}

输出:

线程 低优先级线程 优先级: 1
线程 高优先级线程 优先级: 10

2. 线程中断

线程可以通过调用interrupt()方法来请求中断,线程需要自行处理中断请求。

示例:

public class ThreadInterruptExample {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            while(!Thread.currentThread().isInterrupted()) {
                try {
                    System.out.println("线程运行中...");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    System.out.println("线程被中断");
                    Thread.currentThread().interrupt(); // 重新设置中断标志
                }
            }
            System.out.println("线程结束");
        });
        
        t.start();
        
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        t.interrupt(); // 请求中断线程
    }
}

输出:

线程运行中...
线程运行中...
线程运行中...
线程被中断
线程结束

九、总结

Java的多线程机制为开发高性能和高响应性的应用程序提供了强大的支持。通过理解和掌握线程的创建、管理、同步和通信方法,开发者可以有效地利用多核处理器的能力,优化程序性能。然而,多线程编程也带来了诸如线程安全、死锁和调试复杂性等挑战。因此,在实际开发中,应遵循最佳实践,合理使用Java提供的并发工具类,确保程序的健壮性和可维护性。

主要内容回顾

  1. 线程的创建与管理:通过继承Thread类、实现RunnableCallable接口以及使用线程池等方式创建和管理线程。
  2. 线程同步:使用synchronized关键字、ReentrantLock等机制确保线程安全,避免数据不一致。
  3. 并发工具类:利用CountDownLatchSemaphoreCyclicBarrierBlockingQueue等工具类简化并发编程。
  4. 线程通信:通过wait()notify()以及BlockingQueue等方式实现线程间的协调与通信。
  5. 线程优先级与中断:合理设置线程优先级和处理中断请求,提高程序的响应能力和稳定性。

标签:Java,Thread,void,线程,println,new,多线程,public
From: https://www.cnblogs.com/wyz609/p/18617831

相关文章

  • Java并发编程(并发安全)
    并发编程中两个关键问题:线程之间如何通信(隐式进行,对程序员完全透明)以及如何同步线程之间的通信由JMM(java内存模型)控制,JMM决定一个线程对共享变量的写入何时对另一个线程可见,抽象来说共享变量存储在主内存,每个线程有一个私有的本地内存,里面存放了该线程读/写共享变量的副本就......
  • ITC228 - Programming in Java
    SubjectOutlineITC228-ProgramminginJavaCharlesSturtUniversity-TEQSAProviderIdentification:PRV12018(AustralianUniversity).CRICOSProvider:00005FPage2of28thatyouseekoursupportandguidanceifyouarehavinganydifficultieswiththi......
  • Java设计模式 —— 【结构型模式】桥接模式详解
    前言现在有一个需求,需要创建不同的图形,并且每个图形都有可能会有不同的颜色。首先我们看看用继承来实现:我们可以发现有很多的类,假如我们再增加一个形状或再增加一种颜色,就需要创建更多的类。试想,在一个有多种可能会变化的维度的系统中,用继承方式会造成类爆炸,扩展起来不......
  • 洛谷Java写 P5727冰雹猜想
    题目再现:        给出一个正整数n,然后对这个数字一直进行下面的操作:如果这个数字是奇数,那么将其乘3 再加1,否则除以2。经过若干次循环后,最终都会回到11。经过验证很大的数字(7*10^11)都可以按照这样的方式比变成 11,所以被称为“冰雹猜想”。例如当n是20,变化的过程是......
  • Java多线程第一篇-认识多线程
    文章目录进程和线程概念继承Thread重写run方法实现Runnable重写run方法(解耦合)通过匿名内部类lambda表达式线程的常见的属性(方法)Id(getId)名称(getName)是否后台线程(isDaemon)是否存活(isAlive)进程和线程概念进程(process):进程是操作系统资源分配的基本单位,操作系统......
  • java集合-Map HashMap 源码解析
    hashMap简介HashMap是基于哈希表实现的,每一个元素是一个key-value对,无序,不可重复。HashMap是非线程安全的,只是用于单线程环境下,多线程环境下可以采用concurrent并发包下的concurrentHashMap。HashMap实现了Serializable接口,因此它支持序列化,实现了Cloneable接口,能被克隆。has......
  • Java内卷加剧,死磕八股还有用吗?
    最近小伙伴在我后台留言是这样的:现在就这光景,不比以前,会个CRUD就有人要,即使大部分公司依然只需要做CRUD的事情......现在去面试,只会CRUD还要被吐槽:面试造火箭,工作拧螺丝,就是现在互联网最真实的写照。很多程序员都是死磕八股文,以应对面试。这种情况无可厚非,但其实最重要......
  • 一个Java程序员具备什么样的素质和能力才可以称得上高级工程师?
    一个Java程序员具备什么样的素质和能力才可以称得上高级工程师?这个问题也引发了我的一些思考,可能很多人会说,“作为高级工程师,基础得过硬、得熟练掌握一门编程语言、至少看过一个优秀开源项目的源代码、有过高并发/性能优化的工作经验、沟通能力强等等”。上面这些都很对,这些......
  • spring boot使用Jwt令牌时出现 java.lang.NoClassDefFoundError: javax/xml/bind/Data
    问题根源    在Java9及更高版本中,Java平台经历了模块化系统(Jigsaw项目)的重大变化。作为这一变化的一部分,某些API被移出了默认的JDK发行版,其中包括JAXB(JavaArchitectureforXMLBinding)API。因此,在使用这些被移除的API时,如果应用程序或库没有适当地包......
  • 前端必知必会-JavaScript HTML DOM 节点列表
    文章目录JavaScriptHTMLDOM节点列表HTMLDOMNodeList对象HTMLDOM节点列表长度HTMLCollection和NodeList之间的区别总结JavaScriptHTMLDOM节点列表HTMLDOMNodeList对象NodeList对象是从文档中提取的节点列表(集合)。NodeList对象与HTMLCollectio......