Java 多线程

发布时间:2023年12月20日

1、引入

  • 操作系统他的发展史:

    1. 手工操作

    2. 批处理系统

    3. 多道批处理

    4. 分时系统

    5. 实时系统

  • 进程和线程:

    1. 进程:正在执行的程序,其实就是一块儿内存区域,内部存储着程序的资源

    2. 线程:程序被CPU调度的最小单位。

2、java多线程

  1. 继承Thread类,重写run方法。

  2. 实现Runnable接口,实现run方法。

必须:run(), start()

run(): 线程执行的时候要执行的代码 start():启动一个线程

  1. 继承Thread类 实现MyThread类

    public class MyThread extends Thread {
     ? ?@Override
     ? ?public void run() {
     ? ? ? ?// 要把子线程执行的内容写在run里面
     ? ? ? ?for(int i = 0; i < 1000; i ++) {
     ? ? ? ? ? ?System.out.println("我是子线程:" + i);
     ? ? ?  }
     ?  }
    }

    线程的使用与创建

    // 1. 创建线程对象
    MyThread mt = new MyThread();
    // 2.调用start方法启动一个线程
    mt.start();

  2. 实现Runable接口

    实现Runable中fun方法

    public class MyRunnable implements Runnable {
     ? ?public void run() {
     ? ? ? ?for(int i = 0; i < 1000; i ++) {
     ? ? ?      System.out.println("我是子线程:" + i);
     ? ? ?  }
     ?  }
    }

    线程的使用与创建

    // 1. 先创建Runnable类
    Runnable r = new MyRunnable();
    // 2.创建线程对象必须指向我的Runnable
    Thread td = new Thread(r);
    // 3.调用start方法启动一个线程
    td.start();

3、线程相关操作

3.1、设置优先级

优先级高的线程,是会有一定程度的先执行的权限。但是具体是操作系统来实现的,不同的操作系统可以效果不同。 至于先执行哪个线程,还是看操作系统是怎么分的。优先级相当于只是让操作系统稍微参考一下。

public class test {
 ? ?public static void main(String[] args) {
 ? ? ? ?MyThread mt1 = new MyThread("A线程");
 ? ? ? ?MyThread mt2 = new MyThread("B线程");
 ? ? ? ?mt2.setPriority(10);
 ? ? ? ?mt1.setPriority(1);
 ? ? ? ?mt1.start();
 ? ? ? ?mt2.start();
 ?  }
}
?

3.2、睡眠

睡眠函数,是让当前线程等待一段时间再去执行之后的语句。

sleep里面的单位是毫秒,下面代码中相当于是每一秒执行一次。

@Override
public void run() {
 ? ?while (true) {
 ? ? ? ?SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 ? ? ? ?Date now = new Date();
 ? ? ? ?System.out.println(sdf.format(now));
 ? ? ? ?try {
 ? ? ? ? ? ?Thread.sleep(1000);
 ? ? ?  } catch (InterruptedException e) {
 ? ? ? ? ? ?throw new RuntimeException(e);
 ? ? ?  }
 ?  }
}

3.3、join(主线程等待子线程)

mt.join相当于是会程序卡在这个位置,等到mt线程执行完毕之后再去执行后面的语句。

public class MyThread extends Thread {
 ? ?@Override
 ? ?public void run() {
 ? ? ? ?for(int i = 0; i < 1000; i ++) {
 ? ? ? ? ? ?System.out.println("我是子线程:" + i);
 ? ? ?  }
 ?  }
?
 ? ?public static void main(String[] args) {
 ? ? ? ?MyThread mt = new MyThread();
 ? ? ? ?mt.start();
 ? ? ? ?for(int i = 0; i < 100; i++) {
 ? ? ? ? ? ?System.out.println("------我是主线程:" + i);
 ? ? ?  }
 ? ? ? ?try {
 ? ? ? ? ? ?mt.join();
 ? ? ?  } catch (InterruptedException e) {
 ? ? ? ? ? ?throw new RuntimeException(e);
 ? ? ?  }
 ? ? ? ?System.out.println("---------------------------");
 ?  }
}

3.4、yield(让出CPU一下)

相当于是在可以把当前的cpu让出去,让别人先用一下,只是让一下,而不会一直让下去。 主要是实现一个交替执行的效果。 具体效果,主要还是看操作系统的分配。

public class MyThread extends Thread {
 ? ?public MyThread(String name) {
 ? ? ? ?super.setName(name);
 ?  }
 ? ?@Override
 ? ?public void run() {
 ? ? ? ?for(int i= 0; i < 500; i++) {
 ? ? ? ? ? ?System.out.println(super.getName() + "--" + i);
 ? ? ? ? ? ?if(i % 10 == 0) {
 ? ? ? ? ? ? ? ?Thread.yield();
 ? ? ? ? ?  }
 ? ? ?  }
 ?  }
 ? ?public static void main(String[] args) {
 ? ? ? ?MyThread mt1 = new MyThread("A线程");
 ? ? ? ?MyThread mt2 = new MyThread("B线程");
 ? ? ? ?mt1.start();
 ? ? ? ?mt2.start();
 ?  }
}

3.5、interrupt(打断睡眠)

相当于是子线程因为一些原因进行睡眠操作,但是可以用interrupt来打断子线程的睡眠操作。继续执行之后的工作。

public class MyThread extends Thread{
 ? ?@Override
 ? ?public void run() {
 ? ? ? ?System.out.println("我要睡觉了");
 ? ? ? ?try {
 ? ? ? ? ? ?Thread.sleep(1000000000);
 ? ? ?  } catch (InterruptedException e) {
 ? ? ? ? ? ?System.out.println("为什么不上睡了");
 ? ? ?  }
 ? ? ? ?System.out.println("清醒了,开始工作了。");
 ?  }
 ? ?public static void main(String[] args) {
 ? ? ? ?MyThread mt = new MyThread();
 ? ? ? ?mt.start();
 ? ? ? ?for(int i = 0; i < 1000; i ++) {
 ? ? ? ? ? ?System.out.println(i);
 ? ? ?  }
 ? ? ? ?mt.interrupt(); // 打断正在睡眠的子线程
 ?  }
}

4、线程同步

线程同步: 当多个线程共享同一个资源的时候,我们可以在某个一个线程访问到这个资源的时候,把这个资源暂时封锁,等待执行结束,释放这个锁,其他线程才可以进行执行,线程同步。

总结:等待其它线程释放锁,让线程变得更加安全。

例子:如果在不同线程中操作同一个资源,比如ATM机以及银行柜台同时取钱,同时查询都是有钱的,同时取钱的话会让银行赔钱。所以需要对这个公共资源进行处理,不能有多个人同时使用这个资源,就需要在使用的时候对资源进行封锁。当其他人要用的时候需要等之前的人用完才能用,这样可以避免产生问题。

封锁的方法如下:

4.1、synchronized关键字

synchronized关键字可以将整个方法上锁,只要有人使用就上锁,用完之后自动释放。

public class Account {
 ? ?private double blance;
 ? ?public Account(double blance) {
 ? ? ? ?this.blance = blance;
 ?  }
 ? ?public synchronized void getMoney() {
 ? ? ? ?if(this.blance <= 0) {
 ? ? ? ? ? ?System.out.println("没钱了!!");
 ? ? ? ? ? ?return;
 ? ? ?  }
 ? ? ? ?System.out.println("我要取钱了,目前还剩下:" + this.blance);
 ? ? ? ?this.blance -= 1000;
 ? ? ? ?System.out.println("取完了,还剩下:" + this.blance);
 ?  }
}

4.2、synchronized语句

通过synchronized() {}语句将需要上所的位置上锁。 相比于synchronized关键字的话,语句比较灵活,所以用synchronized的话首推语句。

public class Account {
 ? ?private double blance;
 ? ?public Account(double blance) {
 ? ? ? ?this.blance = blance;
 ?  }
 ? ?public void getMoney() {
 ? ? ? ?synchronized (this){
 ? ? ? ? ? ?if(this.blance <= 0) {
 ? ? ? ? ? ? ? ?System.out.println("没钱了!!");
 ? ? ? ? ? ? ? ?return;
 ? ? ? ? ?  }
 ? ? ? ? ? ?System.out.println("我要取钱了,目前还剩下:" + this.blance);
 ? ? ? ? ? ?this.blance -= 1000;
 ? ? ?  }
 ? ? ? ?System.out.println("取完了,还剩下:" + this.blance);
 ?  }
}

4.3、手动上锁

建一个锁、lock,使用的时候上锁,用完后解锁。

public class Account {
 ? ?private double blance;
 ? ?private Lock lock = new ReentrantLock();
 ? ?public Account(double blance) {
 ? ? ? ?this.blance = blance;
 ?  }
 ? ?public void getMoney() {
 ? ? ? ?lock.lock();
 ? ? ? ?if(this.blance <= 0) {
 ? ? ? ? ? ?System.out.println("没钱了!!");
 ? ? ? ? ? ?return;
 ? ? ?  }
 ? ? ? ?System.out.println("我要取钱了,目前还剩下:" + this.blance);
 ? ? ? ?this.blance -= 1000;
 ? ? ? ?System.out.println("取完了,还剩下:" + this.blance);
 ? ? ? ?lock.unlock();
 ?  }
}

5、死锁 (了解)

当两个线程都需要两个资源的时候,A拥有1 申请2,B拥有2 申请1,这个时候就会产生死锁。

  • 注意事项:使用synchronized的时候一定要格外注意,有没有互相调用的方法被锁定,慎重使用synchronized。

死锁实例代码:

分别锁定一个资源之后申请第二个资源。资源被占用,并且也是等待资源状态。

public class ResourceObject {
 ? ?public static final Object obj1 = new Object();
 ? ?public static final Object obj2 = new Object();
}
public class DeadLock1 extends Thread{
 ? ?@Override
 ? ?public void run() {
 ? ? ? ?synchronized(ResourceObject.obj1) {
 ? ? ? ? ? ?System.out.println("锁定资源1");
 ? ? ? ? ? ?try {
 ? ? ? ? ? ? ? ?Thread.sleep(3000);
 ? ? ? ? ?  } catch (InterruptedException e) {
 ? ? ? ? ? ? ? ?throw new RuntimeException(e);
 ? ? ? ? ?  }
 ? ? ? ? ? ?synchronized (ResourceObject.obj2) {
 ? ? ? ? ? ? ? ?System.out.println("锁定资源2");
 ? ? ? ? ? ? ? ?System.out.println("使用完毕");
 ? ? ? ? ?  }
 ? ? ?  }
 ?  }
}
public class DeadLock2 extends Thread{
 ? ?@Override
 ? ?public void run() {
 ? ? ? ?synchronized(ResourceObject.obj2) {
 ? ? ? ? ? ?System.out.println("锁定资源2");
 ? ? ? ? ? ?try {
 ? ? ? ? ? ? ? ?Thread.sleep(1000);
 ? ? ? ? ?  } catch (InterruptedException e) {
 ? ? ? ? ? ? ? ?throw new RuntimeException(e);
 ? ? ? ? ?  }
 ? ? ? ? ? ?synchronized (ResourceObject.obj1) {
 ? ? ? ? ? ? ? ?System.out.println("锁定资源1");
?
 ? ? ? ? ? ? ? ?System.out.println("使用完毕");
 ? ? ? ? ?  }
 ? ? ?  }
 ?  }
}
public class Test {
 ? ?public static void main(String[] args) {
 ? ? ? ?DeadLock1 dl1 = new DeadLock1();
 ? ? ? ?DeadLock2 dl2 = new DeadLock2();
 ? ? ? ?dl1.start();
 ? ? ? ?dl2.start();
 ?  }
}

6、线程的生命周期

Thread t = new Thread(); // 创建一个线程 t.start(); //开启一个线程,线程处于就绪状态

7、生产者消费者模型

是一个非常常用的模型,增加资源的利用率以及效率。

图中上半部分是一个在进行的时候另一个必须要等待。效率非常低,总会有一个是在等待。

异步,左边不用等右边,右边不用等左边。

生产者消费者模型:读取视频这一方被称为生产者,产品就是中间的视频,右边发送给公安局的就是消费者,消费的是视频。

Queue:队列,BlockingQueue阻塞队列,当队列中没有数据的时候,需要拿出数据,队列将会将程序阻塞,阻塞到有数据,队列继续工作。

AtomicInteger 线程安全的数字类型。

public class Video { // 封装一个类用来储存单一的数据,仅仅用来表示数据。
 ? ?private String name;
 ? ?public String getName() {
 ? ? ? ?return name;
 ?  }
 ? ?public void setName(String name) {
 ? ? ? ?this.name = name;
 ?  }
 ? ?public Video(String name) {
 ? ? ? ?this.name = name;
 ?  }
}
public class ReadVideoThread extends Thread { // 生产者,读取视频
 ? ?// private static int i = 0; // 虽然这个语句是所有线程共享的,因为是静态的,但是不安全,因为多个线程同时i++的时候可能会产生冲突
 ? ?private static AtomicInteger i = new AtomicInteger(); // 线程安全的数字类型
 ? ?private BlockingQueue<Video> videoQueue; // 缓冲队列,用来存放读取的数据视频
 ? ?public ReadVideoThread(BlockingQueue videoQueue) { // 构造函数,将缓冲队列放入。
 ? ? ? ?this.videoQueue = videoQueue;
 ?  }
 ? ?@Override
 ? ?public void run() {
 ? ? ? ?while(true) {
 ? ? ? ? ? ?String name = "视频" + i.incrementAndGet();
 ? ? ? ? ? ?Video v = new Video(name);
 ? ? ? ? ? ?try {
 ? ? ? ? ? ? ? ?Thread.sleep(200); // 为了模拟生产和消费的时间
 ? ? ? ? ? ? ? ?videoQueue.put(v); // 放入队列
 ? ? ? ? ? ? ? ?System.out.println("发现了一个视频:" + name);
?
 ? ? ? ? ?  } catch (InterruptedException e) {
 ? ? ? ? ? ? ? ?throw new RuntimeException(e);
 ? ? ? ? ?  }
 ? ? ?  }
 ?  }
}
public class SendYellowThread extends Thread {
 ? ?private BlockingQueue<Video> videoQueue; ?// 缓冲队列,用来存放读取的数据视频
?
 ? ?public SendYellowThread(BlockingQueue<Video> videoQueue) { // 构造函数,更新当前类的缓冲队列
 ? ? ? ?this.videoQueue = videoQueue;
 ?  }
 ? ?@Override
 ? ?public void run() {
 ? ? ? ?while(true) {
 ? ? ? ? ? ?try {
 ? ? ? ? ? ? ? ?Thread.sleep(150); // 模拟生产者消费者的时间
 ? ? ? ? ? ? ? ?Video video = videoQueue.take(); // 从队列中拿出一个视频数据,会自动从队列中删除
 ? ? ? ? ? ? ? ?System.out.println("我发送了一个视频---->" + video.getName());
 ? ? ? ? ?  } catch (InterruptedException e) {
 ? ? ? ? ? ? ? ?throw new RuntimeException(e);
 ? ? ? ? ?  }
?
 ? ? ?  }
 ?  }
}
public class Test {
 ? ?public static void main(String[] args) {
 ? ? ? ?BlockingQueue<Video> videos = new LinkedBlockingQueue<Video>();
 ? ? ? ?// 创建3个生产者进程
 ? ? ? ?ReadVideoThread rvt1 = new ReadVideoThread(videos);
 ? ? ? ?ReadVideoThread rvt2 = new ReadVideoThread(videos);
 ? ? ? ?ReadVideoThread rvt3 = new ReadVideoThread(videos);
 ? ? ? ?// 创建2个消费者进程
 ? ? ? ?SendYellowThread syt1 = new SendYellowThread(videos);
 ? ? ? ?SendYellowThread syt2 ?= new SendYellowThread(videos);
?
 ? ? ? ?// 启动5个进程
 ? ? ? ?rvt1.start();
 ? ? ? ?rvt2.start();
 ? ? ? ?rvt3.start();
?
 ? ? ? ?syt1.start();
 ? ? ? ?syt2.start();
 ?  }
}
?
文章来源:https://blog.csdn.net/qq_64468032/article/details/135096698
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。