当前位置: 首页 > news >正文

java之多线程

目录

创建多线程的三种创建方式

常用的成员方法

守护线程

多线程的声明周期

​编辑 同步代码块​编辑

同步方法

死锁

等待唤醒机制(线程协调)

线程池


创建多线程的三种创建方式

 继承 Thread
通过继承 Thread 类并重写 run() 方法创建线程。

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread running: " + getName());
    }
}
public class Main {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
    }
}

实现 Runnable 接口
实现 Runnable 接口并将实例传递给 Thread 对象。推荐此方式,因为可以避免 Java 单继承的限制。

class MyRunnable implements Runnable {
    @Override
    public void run() {
  // 注意这里不能直接getName,因为getName是Thread里面的方法,我们必须先获取当前的线程在调用此方法
        System.out.println("Runnable running: " + Thread.currentThread().getName());
    }
}
public class Main {
    public static void main(String[] args) {
        // 创建MyRun的对象 表示多线程要执行的任务
        MyRun mr = new MyRun();
        // 创建线程对象
        Thread t1 = new Thread(mr);
        Thread t2 = new Thread(mr);
        t1.start();
        t2.start();
    }
}

 通过 CallableFutureTask
Callable 可以返回结果或抛出异常,结合 FutureTask 执行。

import java.util.concurrent.Callable;
// 第三种开启线程的方法;特点:可以得到多线程运行的结果
public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        // 开启线程 求1~100之间的和
        int sum = 0;
        for (int i = 1; i <100 ; i++) {
            sum+=i;
        }
        return sum;
    }
}

public class ThreadDemo {

    public static void main(String[] args) throws ExecutionException, InterruptedException{
        // 第三种方式:
        MyCallable mc = new MyCallable();
        FutureTask<Integer> ft = new FutureTask<>(mc);

        Thread t1 = new Thread(ft);
        // 给线程设置名字
        t1.setName("线程1");

        // 开启线程
        t1.start();
        Integer result = ft.get();
        System.out.println(result);
    }
}

常用的成员方法

  • start(): 启动线程并调用 run() 方法。
  • run(): 线程执行的具体逻辑。
  • join(): 等待线程执行结束。
  • sleep(long millis): 暂停当前线程执行一段时间。
  • yield(): 暂时释放 CPU 资源,让其他线程有机会执行。
  • getName() / setName(): 获取或设置线程名称。
  • isAlive(): 检查线程是否仍然存活。
  • setPriority(int priority): 设置线程优先级。

守护线程

守护线程在后台运行,用于服务用户线程(非守护线程)。当所有用户线程结束时,守护线程自动退出。
通过 setDaemon(true) 将线程设置为守护线程。当主线程结束之后,守护线程不会立刻结束而是陆续结束。

public class Main {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            while (true) {
                System.out.println("Daemon thread running...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    break;
                }
            }
        });
        thread.setDaemon(true); // 设置为守护线程
        thread.start();

        System.out.println("Main thread ends.");
    }
}

多线程的声明周期

 同步代码块

使用 synchronized
确保同一时间只有一个线程访问共享资源。

注意在synchronized小括号里面的一般会是该类的字节码文件(类.class)必须保证其唯一性。

package Thread;

// 三个窗口售卖票 总共有50张票
public class practice {
    public static void main(String[] args) {
        Myr myr = new Myr();
        Thread t1 = new Thread(myr);
        Thread t2 = new Thread(myr);
        Thread t3 = new Thread(myr);
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

class Myr implements Runnable {
    private int cnt = 1; // 初始票号设为 1

// 同步代码块
    @Override
    public void run() {
        while (true) {
            synchronized (Myr.class) {
                if (cnt > 50) break; // 先检查是否有票可卖
                System.out.println(Thread.currentThread().getName() + "正在售卖第 " + cnt + " 张票!");
                cnt++; // 售出当前票后再自增
            }
            try {
                Thread.sleep(100); // 模拟售票时间
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

同步方法

同步方法是锁住方法里面所有的代码,其实就是把同步代码块里面的代码提取成同步方法。

注意:同步方法锁的对象是不可以指定的。在java中,非静态的锁对象是this。静态的锁对象是本类的字节码文件对象(类.class)

package Thread;

// 三个窗口售卖票 总共有50张票
public class practice {
    public static void main(String[] args) {
        Myr myr = new Myr();
        Thread t1 = new Thread(myr);
        Thread t2 = new Thread(myr);
        Thread t3 = new Thread(myr);
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

class Myr implements Runnable {
    private int cnt = 1; // 初始票号设为 1

    @Override
    public void run() {
        while (true) {
            if (!method()) break;;
        }
    }
// 同步方法
    private synchronized boolean method() {
        if (cnt > 50) return false; // 先检查是否有票可卖
        System.out.println(Thread.currentThread().getName() + "正在售卖第 " + cnt + " 张票!");
        cnt++; // 售出当前票后再自增
        try {
            Thread.sleep(100); // 模拟售票时间
        } catch (
                InterruptedException e) {
            throw new RuntimeException(e);
        }
        return true;
    }
}

死锁

死锁就是两个锁嵌套,在以后写代码的时候需要注意不要写嵌套的锁

等待唤醒机制(线程协调)

 等待唤醒机制中,一般会将锁对象(通常是一个共享资源或 Object)和线程操作关联起来。这是因为 wait()notify()notifyAll() 方法必须在同步代码块或同步方法中调用,而这些同步需要一个明确的锁对象来协调线程间的通信。

代码中通过 Desk.lock.wait()Desk.lock.notify() 有效地将线程等待与特定的锁对象关联起来。这样线程可以知道锁住和这个锁对象关联起来的线程,也可以唤醒与这个锁对象关联起来的线程

package waitandnotify;

public class Desk {
    public static final Object lock = new Object(); // 锁对象
    public static int cnt = 5; // 剩余次数
    public static int foodFlag = 0; // 食物标志:0 - 无食物,1 - 有食物
}

class Cook extends Thread {
    @Override
    public void run() {
        while (true) {
            synchronized (Desk.lock) {
                if (Desk.cnt == 0) {
                    break; // 没有剩余任务,结束线程
                }
                if (Desk.foodFlag == 1) {
                    try {
                        Desk.lock.wait(); // 有食物,等待消费者
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else if (Desk.foodFlag == 0) {
                    // 生产食物
                    System.out.println("厨师做了一碗面条");
                    Desk.foodFlag = 1;
                    Desk.lock.notify(); // 唤醒消费者
                }
            }
        }
    }
}

class Consumer extends Thread {
    @Override
    public void run() {
        while (true) {
            synchronized (Desk.lock) {
                if (Desk.cnt == 0) {
                    break; // 没有剩余任务,结束线程
                }
                if (Desk.foodFlag == 0) {
                    try {
                        Desk.lock.wait(); // 没有食物,等待生产者
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else if (Desk.foodFlag == 1) {
                    // 消费食物
                    System.out.println("消费者吃了一碗面条");
                    Desk.foodFlag = 0;
                    Desk.cnt--; // 减少任务次数
                    Desk.lock.notify(); // 唤醒生产者
                }
            }
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Cook cook = new Cook();
        Consumer consumer = new Consumer();
        cook.start();
        consumer.start();
    }
}

线程池

package Thread.Threadpool;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class MyThreadPool {
    public static void main(String[] args) {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                3, // 核心线程数量不能少于0
                6, // 最大线程数量,不能小于0 最大线程数量大于等于核心线程
                60, // 临时线程60s就会销毁
                TimeUnit.SECONDS, // 时间单位
                new ArrayBlockingQueue<>(3), // 任务队列
                Executors.defaultThreadFactory(), // 创建线程工厂
                new ThreadPoolExecutor.AbortPolicy() //任务的拒绝策略
        );
        // 为什么任务的拒绝策略需要定义在类的内部类当中?
        //划重点:什么时候创建内部类,当一个类依赖另一个类存在时,而他单独存在没有意义时,
        // 就可以创建某个类的内部类

        pool.submit(new MyRunnable());
    }
}

相关文章:

  • [Dify] 使用 Docker 本地部署 Dify 并集成 Ollama 模型的详细指南
  • Android监测顶层包名+类名
  • 动态规划(6)——01背包问题
  • 2025蓝桥杯JavaB组
  • jupyter notebook 无法启动- markupsafe导致
  • Day3—循环起来吧
  • 深入理解 PyTorch 的 nn.Embedding:词向量映射及变量 weight 的更新机制
  • 算法专题:双指针
  • 470用 Rand7() 实现 Rand10()
  • [MSPM0开发]之五 MSPM0G3507 SysTick定时器的配置与使用(systick中断实现延时函数)
  • 微信小程序运行机制详解
  • 单片机Day05---动态数码管显示01234567
  • WindowsPE文件格式入门08.导出表
  • 蓝桥杯嵌入式历年省赛客观题
  • GPU虚拟化技术在深度学习集群中的应用实践
  • Spring AI 结构化输出详解
  • 【foc思考】为什么svpwm要不停变换占空比
  • Python 实现最小插件框架
  • JDK(Java Development Kit)从发布至今所有主要版本 的详细差异、新增特性及关键更新的总结,按时间顺序排列
  • 【架构师从入门到进阶】第五章:DNSCDN网关优化思路——第七节:网关-XSS攻击与预防
  • 百度网站提交入口网址/世界最新新闻
  • 超市网站建设方案模板/新闻热点大事件
  • 上海做兼职上哪个网站/外贸网站推广方式
  • dz论坛网站建设/怎样创建自己的电商平台
  • 网站程序源代码/高质量外链购买
  • 谷多网站/免费推广网站注册入口