# 多线程、线程同步、反射、注解的基本原理

# 多线程

为什么需要多线程?这个问题问得很基础,但也很核心。简单来说,多线程是为了榨干 CPU 的性能,同时让程序响应得更快。当然,多线程也带来了新麻烦比如资源竞争、死锁,但为了效率和体验,这些代价通常是值得的。

# Java 开启线程一

class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i <= 500; i++) {
            System.out.println("i=" + i);
        }
    }
}
class MyThread2 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i <= 500; i++) {
            System.out.println("ww=" + i);
        }
    }
}
public class TDemo2 {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.setName("线程1");
        myThread.start();
        MyThread2 myThread2 = new MyThread2();
        Thread thread = new Thread(myThread2);
        thread.setName("线程2");
        thread.start();
    }
}

# Java 的垃圾回收

当无用对象过多的时候,JVM 会自动执行垃圾回收。

class Dome2 {
    @Override
    protected void finalize() throws Throwable {
        System.out.println("finalize!");
    }
}
public class TDemo2 {
    public static void main(String[] args) {
        for (int i = 0; i < 500000; i++) {
            new Dome2();
        }
    }
}

# Java 开启线程二

implements Runnable 是 Java 推荐的方式,把任务和线程分离开,比继承 Thread 类更灵活。

class MyThread implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 500; i++) {
            System.out.println("i = " + i);
        }
    }
}
class MyThread2 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 500; i++) {
            System.out.println("ww = " + i);
        }
    }
}
public class TDemo2 {
    public static void main(String[] args) {
        new Thread(new MyThread()).start();
        new Thread(new MyThread2()).start();
    }
}

# Java 开启线程三

Callable 代替了 Runnable,它的 call () 方法不仅能执行任务,还能返回结果(这里返回累加和 Integer),而 Runnable 的 run () 是 void 无返回的。

class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 0; i < 500; i++) {
            System.out.println("i = " + i);
            sum += i;
        }
        return sum;
    }
}
class MyCallable2 implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 0; i < 600; i++) {
            System.out.println("ww = " + i);
            sum += i;
        }
        return sum;
    }
}
public class TDemo2 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCallable myCallable = new MyCallable();
        FutureTask<Integer> integerFutureTask = new FutureTask<Integer>(myCallable);
        new Thread(integerFutureTask).start();
        Integer i = integerFutureTask.get();
        System.out.println("ans : " + i);
        MyCallable2 myCallable2 = new MyCallable2();
        FutureTask<Integer> integerFutureTask2 = new FutureTask<Integer>(myCallable2);
        new Thread(integerFutureTask2).start();
        Integer i2 = integerFutureTask2.get();
        System.out.println("ans : " + i2);
    }
}

# 设置线程优先级

class PriorityTask implements Runnable {
    private String taskName;
    
    public PriorityTask(String taskName) {
        this.taskName = taskName;
    }
    
    @Override
    public void run() {
        // 让线程多跑一会儿,便于观察效果
        for (int i = 0; i < 100; i++) {
            System.out.println(taskName + " 执行第 " + i + " 次");
            // 主动让出 CPU,增加其他线程执行机会
            Thread.yield();
        }
        System.out.println(taskName + " 执行完毕!");
    }
}
public class PriorityDemo {
    public static void main(String[] args) {
        // 创建三个任务
        PriorityTask lowTask = new PriorityTask("【低优先级】");
        PriorityTask normalTask = new PriorityTask("【普通优先级】");
        PriorityTask highTask = new PriorityTask("【高优先级】");
        
        // 创建三个线程
        Thread lowThread = new Thread(lowTask);
        Thread normalThread = new Thread(normalTask);
        Thread highThread = new Thread(highTask);
        
        // 设置优先级(1-10,默认 5)
        lowThread.setPriority(Thread.MIN_PRIORITY);    // 1
        normalThread.setPriority(Thread.NORM_PRIORITY); // 5
        highThread.setPriority(Thread.MAX_PRIORITY);    // 10
        
        // 启动线程
        lowThread.start();
        normalThread.start();
        highThread.start();
        
        // 让主线程等待子线程执行完
        try {
            lowThread.join();
            normalThread.join();
            highThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        System.out.println("所有线程执行结束!");
    }
}

# 守护线程

守护线程是后台保姆线程,当所有非守护线程执行完毕后,JVM 会自动退出并强制终止所有守护线程。

public class TDemo2 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 50; i++) {
                    System.out.println(Thread.currentThread().getName() + " i=" + i);
                }
            }
        }, "线程1");
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 500; i++) {
                    System.out.println(Thread.currentThread().getName() + " i=" + i);
                }
            }
        }, "线程2");
        thread1.setDaemon(true);
        thread.start();
        thread1.start();
    }
}

线程 1 执行完 50 次循环后,JVM 会立即退出,此时线程 2 是守护线程即使才执行到一半,也会被强制终止,控制台不会打印完所有线程 2 的输出。

# 线程安全

public class TDemo2 {
    class TicketTast implements Runnable {
        int ticket = 100;
        @Override
        public void run() {
            while (ticket != 0) {
                System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票");
                ticket--;
            }
        }
    }
    public static void main(String[] args) {
        new Thread(new TDemo2().new TicketTast(), "窗口1").start();
        new Thread(new TDemo2().new TicketTast(), "窗口2").start();
        new Thread(new TDemo2().new TicketTast(), "窗口3").start();
    }
}

这段代码是多线程卖票的经典错误示例,核心问题在于每个线程各自拥有独立的票池。

窗口 3 正在卖第 100 张票
窗口 3 正在卖第 99 张票
窗口 3 正在卖第 98 张票
窗口 3 正在卖第 97 张票
窗口 3 正在卖第 96 张票
窗口 3 正在卖第 95 张票
窗口 1 正在卖第 100 张票
窗口 1 正在卖第 99 张票

# 通过线程同步技术

# 同步代码块 synchronized

synchronized 中使用 TicketTast.class 作为锁对象,可以让所有 TicketTast 实例共享同一把锁(类锁)。

class TicketTast implements Runnable {
    int ticket = 1000;
    @Override
    public void run() {
        while (true) {
            synchronized (TicketTast.class) {
                if (ticket <= 0) {
                    break;
                }
                System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票");
                ticket--;
            }
        }
    }
}

# 同步方法 synchronized

synchronized 修饰在 extracted () 方法上,锁的是当前 TicketTast 实例对象。因为两个线程共享同一个 ticketTast 对象,所以锁生效,保证票数安全递减。

public class TDemo2 {
    class TicketTast implements Runnable {
        int ticket = 10;
        @Override
        public void run() {
            while (true) {
                if (extracted()) break;
            }
        }
        private synchronized boolean extracted() {
            if (ticket <= 0) {
                return true;
            }
            System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票");
            ticket--;
            return false;
        }
    }
    public static void main(String[] args) {
        TDemo2 tDemo2 = new TDemo2();
        TicketTast ticketTast = tDemo2.new TicketTast();
        new Thread(ticketTast, "窗口1").start();
        new Thread(ticketTast, "窗口2").start();
    }
}
方法类型 锁对象 说明
synchronized 修饰非静态方法 this (当前实例对象) 每个实例有自己的锁
synchronized 修饰静态方法 类名.class (Class 对象) 所有实例共享一把锁

# 代码中到底几把锁?

TicketTast ticketTast = tDemo2.new TicketTast();  // 只创建了 1 个 TicketTast 对象
new Thread(ticketTast, "窗口1").start();
new Thread(ticketTast, "窗口2").start();

只有 1 把锁!因为两个线程共享同一个 ticketTast 对象,非静态同步方法锁的是 this ,也就是这个对象。

# 如果是多个实例呢?

TicketTast task1 = new TicketTast();  // 对象 1
TicketTast task2 = new TicketTast();  // 对象 2
new Thread(task1, "窗口1").start();
new Thread(task2, "窗口2").start();

有 2 把锁!每个对象都有自己的锁,窗口 1 锁 task1,窗口 2 锁 task2,互不干扰 —— 这就是 "每个实例有自己的锁"。

结果就是各卖各的 10 张票,共卖出 20 张(又回到了最初的错误)。

synchronized 锁的是对象本身:非静态方法锁 this ,有多少个对象就有多少把锁;静态方法锁 类.class ,所有对象只有一把锁。代码只创建了一个 TicketTast 实例,所以只有一把锁,两个窗口争抢这一把锁,安全有效。

# Lock 锁

public class TDemo2 {
    class TicketTast implements Runnable {
        int ticket = 10;
        ReentrantLock lock = new ReentrantLock();
        @Override
        public void run() {
            while (true) {
                try {
                    lock.lock();
                    if (ticket <= 0) {
                        break;
                    }
                    System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票");
                    ticket--;
                } finally {
                    lock.unlock();
                }
            }
        }
    }
    public static void main(String[] args) {
        TDemo2 tDemo2 = new TDemo2();
        TicketTast ticketTast = tDemo2.new TicketTast();
        new Thread(ticketTast, "窗口1").start();
        new Thread(ticketTast, "窗口2").start();
    }
}

ReentrantLock lock = new ReentrantLock(); 是 TicketTast 的成员变量,两个线程共享同一个 ticketTast 对象,所以共享同一把锁。即使 ticket<=0 执行 break 退出循环, finally 块依然会执行,保证锁被释放 —— 这是正确的做法。

# 线程池

线程池的核心作用是:复用线程、控制并发、提升性能。

1. 复用线程(降低开销)
创建和销毁线程是有成本的(内存分配、系统调用)。线程池让线程执行完一个任务后不销毁,而是继续执行下一个任务,避免了频繁创建销毁的开销。

2. 控制并发(防止资源耗尽)
没有线程池时,你可以无限制地创建线程(比如循环创建 10000 个),系统可能崩溃。线程池限制了最大线程数,超过的任务进入队列排队,保护系统稳定。

3. 统一管理(方便监控)
线程池提供了任务统计(执行数、失败数)、定时执行、线程状态监控等功能,方便统一管理。

没有线程池:每次来客人都临时招聘一个服务员,客人走了就辞退 —— 频繁招聘培训,成本极高。

有线程池:店里固定养了 10 个服务员,客人来了直接服务,走了继续服务下一位 —— 高效、可控。

public class TDemo2 {
    public static void main(String[] args) {
        // 创建线程池
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        // 提交任务
        for (int i = 0; i < 100; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "正在执行任务");
                }
            });
        }
        // 关闭线程池
        executorService.shutdown();
    }
}

# 自定义线程池

public class TDemo2 {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                2,
                5,
                60,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(10),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        for (int i = 0; i < 103; i++) {
            threadPoolExecutor.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + " is running");
                }
            });
        }
    }
}

Exception in thread “main” java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@6442b0a6[Not completed, task = java.util.concurrent.Executors$RunnableAdapter@67b64c45[Wrapped task = com.KarryCode.process.TDemo21@4411d970]]rejectedfromjava.util.concurrent.ThreadPoolExecutor@60f82f98[Running,poolsize=5,activethreads=5,queuedtasks=10,completedtasks=0]atjava.base/java.util.concurrent.ThreadPoolExecutor1@4411d970]] rejected from java.util.concurrent.ThreadPoolExecutor@60f82f98[Running, pool size = 5, active threads = 5, queued tasks = 10, completed tasks = 0] at java.base/java.util.concurrent.ThreadPoolExecutorAbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2055)
at java.base/java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:825)
at java.base/java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1355)
at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:118)
at com.KarryCode.process.TDemo2.main(TDemo2.java:30)

参数 值 含义
corePoolSize 2 核心线程数:池中保有的最小线程数,即使空闲也不会销毁
maximumPoolSize 5 最大线程数:池中最多允许的线程数
keepAliveTime 60 空闲存活时间:非核心线程空闲 60 秒后被回收
unit TimeUnit.SECONDS 时间单位:秒
workQueue new ArrayBlockingQueue<>(10) 任务队列:容量 10 的有界阻塞队列,存放等待执行的任务
threadFactory Executors.defaultThreadFactory() 线程工厂:创建线程的方式(默认即可)
handler new ThreadPoolExecutor.AbortPolicy() 拒绝策略:队列满了且线程数达上限时,抛出 RejectedExecutionException

当提交了超过线程池承载能力的任务数。根据配置:

  • 核心线程:2 个
  • 最大线程:5 个
  • 队列容量:10 个

核心线程 = 2:这 2 个线程一直活着,即使空闲也不销毁(除非设置 allowCoreThreadTimeOut )
最大线程 = 5:这是总线程数上限,包含核心线程 2 个 + 临时线程最多 3 个

# 枚举应用

public enum Constant {
    DOWN, UP, HALF_UP, DELETE_LEFT
}
public class test {
    public static void handleData(double number, Constant constant) {
        switch (constant) {
            case DOWN:
                System.out.println("DOWN: " + Math.floor(number));
                break;
            case UP:
                System.out.println("UP: " + Math.ceil(number));
                break;
            case HALF_UP:
                System.out.println("HALF_UP: " + Math.round(number));
                break;
            case DELETE_LEFT:
                System.out.println("DELETE_LEFT: " + (int) number);
                break;
            default:
                throw new IllegalArgumentException("Unknown constant: " + constant);
        }
    }
    public static void main(String[] args) {
        System.out.println("Hello World");
    }
}

# 反射

反射就是加载某个类到内存,允许访问变量、方法、构造器。反射就是要去拿类中的信息。

public class re {
    public static void main(String[] args) throws ClassNotFoundException {
        // 方法 1
        Class c1 = Student.class;
        System.out.println(c1);
        // 方法 2
        Class c2 = Class.forName("com.KarryCode.process.Student");
        System.out.println(c2);
        Student zhangsan = new Student("zhangsan", 18);
        Class c3 = zhangsan.getClass();
        System.out.println(c3);
    }
}

# 反射获取构造器

public class Cat {
    public static int a;
    public static final String COUNTY = "China";
    private String name;
    private int age;
    public Cat() {
        System.out.println("我是无参构造方法");
    }
    private Cat(String name, int age) {
        System.out.println("我是有参构造方法");
        this.name = name;
        this.age = age;
    }
    public void get() {
        System.out.println("我是公开方法,eat!!");
    }
    private void run() {
        System.out.println("我是私有方法,run!!");
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}
@Test
public void test1() throws NoSuchMethodException {
    Class catClass = Cat.class;
    Constructor[] constructors1 = catClass.getConstructors();
    for (Constructor constructor : constructors1) {
        System.out.println(constructor);
        System.out.println(constructor.getParameterCount());
    }
    System.out.println("--------------");
    Constructor[] declaredConstructors = catClass.getDeclaredConstructors();
    for (Constructor constructor : declaredConstructors) {
        System.out.println(constructor);
        System.out.println(constructor.getParameterCount());
    }
    System.out.println("--------------");
    Constructor declaredConstructor = catClass.getDeclaredConstructor(String.class, int.class);
    System.out.println(declaredConstructor);
}

# 通过反射的构造器实例化对象

Constructor declaredConstructorNoneParam = catClass.getDeclaredConstructor();
Constructor declaredConstructorDoubleParam = catClass.getDeclaredConstructor(String.class, int.class);

# 无参实例化

Cat noNameCat = (Cat) declaredConstructorNoneParam.newInstance();
System.out.println(noNameCat.toString());

# 有参实例化

此时会报错:

Cat TomCat = (Cat) declaredConstructorDoubleParam.newInstance("Tom", 3);
System.out.println(TomCat.toString());

因为,有参构造器是私有的。

java.lang.IllegalAccessException: class com.KarryCode.process.reTest cannot access a member of class com.KarryCode.process.Cat with modifiers “private”

# 暴力反射

将 setAccessible 设置为真。

declaredConstructorDoubleParam.setAccessible(true);
Cat TomCat = (Cat) declaredConstructorDoubleParam.newInstance("Tom", 3);
System.out.println(TomCat.toString());

我是无参构造方法
name=null, age=0
我是有参构造方法
name=Tom, age=3

# 通过暴力反射修改成员变量

@Test
public void test2() throws NoSuchFieldException, IllegalAccessException {
    Cat karry = new Cat("karry", 22, true);
    System.out.println(karry.toString());
    Class catClass = Cat.class;
    Field[] declaredFields = catClass.getDeclaredFields();
    for (Field field : declaredFields) {
        System.out.println(field);
        System.out.println(field.getType());
        System.out.println(field.getName());
        System.out.println("________");
    }
    System.out.println("修改前的karry对象:" + karry.toString());
    Field age = karry.getClass().getDeclaredField("age");
    age.setAccessible(true);
    age.setInt(karry, 100);
    System.out.println("修改后的karry对象:" + karry.toString());
}

# 通过暴力反射执行方法

@Test
public void test3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    Class catClass = Cat.class;
    for (Method declaredMethod : catClass.getDeclaredMethods()) {
        System.out.println(declaredMethod);
        System.out.println(declaredMethod.getParameterCount());
        System.out.println(declaredMethod.getReturnType());
        System.out.println("----------");
    }
    Method run = catClass.getDeclaredMethod("run", String.class, int.class);
    run.setAccessible(true);
    run.invoke(new Cat("Karry", 22, true), "北京", 22);
}

# 反射的作用

  1. 破坏封装性
  2. 适合做 Java 的框架功能

# 注解

注解本质上是一种元数据(即 "数据的标签"),它本身不包含业务逻辑,只是给代码打上标记。真正起作用的是解析注解的处理器。

# 元注解

# @Retention(保留策略)

指定注解在什么阶段保留。

取值 保留阶段 说明
SOURCE 源代码 编译时丢弃,只存在于 .java 文件中
CLASS 字节码 编译到 .class 文件,但运行时不可用(默认值)
RUNTIME 运行时 编译到 .class ,运行时可通过反射获取

# @Target(适用目标)

指定注解可以用在什么地方。

取值 目标
TYPE 类、接口、枚举
FIELD 字段(包括枚举常量)
METHOD 方法
PARAMETER 方法参数
CONSTRUCTOR 构造函数
LOCAL_VARIABLE 局部变量
ANNOTATION_TYPE 注解类型
PACKAGE 包
TYPE_PARAMETER 泛型类型参数(Java 8)
TYPE_USE 类型使用处(Java 8)
更新于

请我喝[茶]~( ̄▽ ̄)~*

KarryLiu 微信支付

微信支付

KarryLiu 支付宝

支付宝