多线程的相关知识

本文介绍了进程和线程的基本概念,以及并发和并行的区别。接着讲解了Java中多线程的三种实现方式:继承Thread类、实现Runnable接口和实现Callable接口。同时,文章探讨了线程安全问题,通过买票问题展示了如何使用synchronized和Lock解决并发问题。最后,文章通过生产者-消费者模型展示了wait()和notify()的等待唤醒机制。
摘要由CSDN通过智能技术生成

一、概述

1.基本概念

进程:进程(Process)是计算机中正在运行的程序的实例。换句话来说进程就是计算机正在运行的软件。
在这里插入图片描述
线程:线程(Thread)是操作系统能够进行运算调度的最小单位,是进程的实际运行单位。

并发:同一时刻多个指令在单个cpu上交替执行

并行:同一时刻多个指令在多个cpu上同时执行

2.多线程的应用场景

  1. 拷贝、迁移大文件
  2. 加载大量资源文件
  3. 处理大量请求
  4. 处理耗时操作

二、多线程的实现方式

  1. 继承Thread类并重写run()方法
class MyThread extends Thread {
  public void run() {
    // 线程执行的代码
  }
}

// 创建并启动线程
MyThread thread = new MyThread();
thread.start();

2.实现Runnable接口

class MyRunnable implements Runnable {
  public void run() {
    // 线程执行的代码
  }
}

// 创建Runnable实例
MyRunnable runnable = new MyRunnable();

// 创建并启动线程
Thread thread = new Thread(runnable);
thread.start();

3.实现Callable接口

class MyCallable implements Callable<String> {
  public String call() throws Exception {
    System.out.println("线程开始执行");
    for (int i = 0; i < 5; i++) {
      System.out.println("线程执行中:" + i);
      Thread.sleep(1000); // 休眠1秒
    }
    System.out.println("线程执行完毕");
    return "线程执行完毕";
  }
}

// 创建Callable实例
MyCallable callable = new MyCallable();

// 创建FutureTask实例
FutureTask<String> futureTask = new FutureTask<>(callable);

// 创建并启动线程
Thread thread = new Thread(futureTask);
thread.start();

// 获取线程执行结果
String result = futureTask.get();
System.out.println("线程执行结果:" + result);

三、线程安全问题

产生原因:多个线程操作同一个共享数据时,一个线程访问的共享 数据被其他线程修改了, 那么就发生了线程安全问题。

**解决方法:**使用synchronized,Lock锁。

买票问题

public class SaleTick extends Thread{
    public static int tick=0;
    @Override
    public void run() {
        while (true){
                try {
                    sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                if (tick<100){
                    tick++;
                    System.out.println(getName()+"正在售卖第"+tick+"张票");
                }else {
                    break;
                }
        }
    }
}
public class Test {
    public static void main(String[] args) {
        SaleTick saleTick = new SaleTick();
        SaleTick saleTick1 = new SaleTick();
        SaleTick saleTick2 = new SaleTick();
        saleTick.start();
        saleTick1.start();
        saleTick2.start();
    }
}

上述代码如果使用三个线程分别卖票那么可能会照成两种情况,一种是可能导致重复的票,另一种是可能出现超出范围的票,从而引发线程安全问题。

public class SaleTick extends Thread{
    public static int tick=0;
    static Object object=new Object();
    @Override
    public void run() {
        while (true){
            synchronized (object){
                try {
                    sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                if (tick<100){
                    tick++;
                    System.out.println(getName()+"正在售卖第"+tick+"张票");
                }else {
                    break;
                }
            }
        }
    }
}

解决办法是把操作买票的这个过程变成同步的,只有一个线程卖完了票另一个线程才能卖票。使用同步代码块要注意 synchronized的地方,确保同步的地方就是会产生线程安全的位置,同时synchronized中锁的对象是同一个对象。当然也可以把同步代码块的方法抽出变成一个同步方法。

使用Lock锁

public class SaleTick extends Thread{
    public static int tick=0;
    static Lock lock=new ReentrantLock();
    @Override
    public void run() {
        while (true){
            lock.lock();
                try {
                    sleep(100);
                    if (tick<100){
                        tick++;
                        System.out.println(getName()+"正在售卖第"+tick+"张票");
                    }else {
                        break;
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }finally {
                    
                   lock.unlock();
                }
        }
    }
}

Lock锁的特点是他可以自己空锁的开启和释放,使用Lock锁的时候也要注意两个问题,一个是什么时候是否为同一个锁,一个是要记得释放锁,释放锁的过程可以放在finally语句中。

四、生产者-消费者模型(等待唤醒机制)

使用wait()和notify()方法实现的方式生产者消费者模型,首先需要的是要有一个缓存区来存放生产的产品。当然也可以用队列来实现生产者-消费者模型。

缓存区

public class Buffer {
    private int[] data;
    private int size;
    private int count;
    private int in;
    private int out;

    public Buffer(int size) {
        this.data = new int[size];
        this.size = size;
        this.count = 0;
        this.in = 0;
        this.out = 0;
    }

    public synchronized void put(int value) throws InterruptedException {
        while (count == size) {
            wait();
        }
        data[in] = value;
        in = (in + 1) % size;
        count++;
        notifyAll();
    }

    public synchronized int get() throws InterruptedException {
        while (count == 0) {
            wait();
        }
        int value = data[out];
        out = (out + 1) % size;
        count--;
        notifyAll();
        return value;
    }
}

生产者

public class Producer implements Runnable {
    private Buffer buffer;

    public Producer(Buffer buffer) {
        this.buffer = buffer;
    }

    public void run() {
        try {
            for (int i = 0; i < 10; i++) {
                buffer.put(i);
                System.out.println("Produced " + i);
                Thread.sleep((int) (Math.random() * 100));
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

消费者

public class Consumer implements Runnable {
    private Buffer buffer;

    public Consumer(Buffer buffer) {
        this.buffer = buffer;
    }

    public void run() {
        try {
            for (int i = 0; i < 10; i++) {
                int value = buffer.get();
                System.out.println("Consumed " + value);
                Thread.sleep((int) (Math.random() * 100));
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

测试代码

public class Main {
    public static void main(String[] args) {
        Buffer buffer = new Buffer(5);
        Producer producer = new Producer(buffer);
        Consumer consumer = new Consumer(buffer);
        new Thread(producer).start();
        new Thread(consumer).start();
    }
}

上面的代码中,我们创建了一个大小为5的缓冲区,并分别启动了一个生产者和一个消费者线程。生产者会向缓冲区中添加10个数据,消费者会从缓冲区中取出10个数据。由于缓冲区的大小为5,因此生产者和消费者需要交替执行,以避免缓冲区溢出或空出。

五、线程的状态

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力发光的程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值