文章

深入多线程

2020.12.8 ・ 共 1773 字,您可能需要 4 分钟阅读

Tags: 多线程, Java, 学习笔记

在多线程的操作之中,大多数情况下使用的是Threadstart(),如果对于多线程需要进行停止处理,Thread提供有stop(),但是其在 JDK 1.2开始就不被推荐使用了。要想停止线程,则需要选择一种柔和的形式。

public class Test {
  public static boolean flag = true;

  public static void main(String[] args) throws InterruptedException {
    new Thread(
            () -> {
              long num = 0;
              while (flag) {
                try {
                  Thread.sleep(50);
                } catch (InterruptedException e) {
                  e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "运行中 num = " + num++);
              }
            },
            "执行线程")
        .start();
    Thread.sleep(200);
    flag = false;
  }
}

万一有其他的线程控制 flag 的内容,对于线程的停止并不是立刻停止的,而是通过判断 flag 的值来完成的。

只能是为一个活着的线程进行守护,死了的线程不会守护。如果主线程的程序或者其他线程还在执行的时候,那么守护线程将一直存在,并且运行在后台。

Thread中提供有如下的守护线程操作方法。

  • 设置为守护线程:public final void setDaemon(boolean on)
  • 判断是否为守护线程:public final boolean isDaemon()
public class Test {
  public static boolean flag = true;

  public static void main(String[] args) throws InterruptedException {
    Thread userThraed =
        new Thread(
            () -> {
              for (int x = 0; x < 10; x++)
                System.out.println(Thread.currentThread().getName() + "运行中 x = " + x);
            },
            "用户线程");
    Thread daemonThread =
        new Thread(
            () -> {
              for (int x = 0; x < Integer.MAX_VALUE; x++)
                System.out.println(Thread.currentThread().getName() + "运行中 x = " + x);
            },
            "守护线程");
    daemonThread.setDaemon(true); // 设置为守护线程,随着整体程序而存在。
    userThraed.start();
    daemonThread.start();
  }
}

在整个 JVM 中,最大的守护线程就是 GC 线程。程序执行完毕,GC 线程也将消失。

在多线程定义之中,Volatile 主要是在属性定义上使用的,表示该属性为直接数据操作,而不进行副本的复制处理。

注意:此属性并不是描述同步的。

image-20201208103252588

在正常进行变量处理时往往会经历如下步骤。

  1. 获取变量原有的数据内容副本。
  2. 为变量进行数学计算。
  3. 将计算后的变量保存到原始空间之中。

如果一个属性上追加了Volatile,则表示不使用副本,直接操作原变量。

  1. volatile主要在属性上使用,synchronized是在代码块、方法上使用的。
  2. volatile无法描述同步的处理,它只是一种直接内存的处理,避免了副本的操作 。synchronize是实现同步的。

设计 4 个线程对象,两个线程执行减操作,两个线程执行加操作。

public class Test {
  public static void main(String[] args) {
    Resource resource = new Resource();
    AddThread at = new AddThread(resource);
    SubThread st = new SubThread(resource);
    new Thread(at, "加法a").start();
    new Thread(at, "加法b").start();
    new Thread(st, "减法a").start();
    new Thread(st, "减法b").start();
  }
}

class Resource { // 操作资源
  private int num = 0;
  private boolean flag = true;
  // true 表示可以进行加法 无法减法
  public synchronized void add() throws InterruptedException {
    if (!this.flag) {
      super.wait();
    }
    Thread.sleep(100);
    this.num++;
    System.out.println("加法操作" + Thread.currentThread().getName() + " num = " + this.num);
    this.flag = false;
    super.notifyAll();
  }

  public synchronized void sub() throws InterruptedException {
    if (this.flag) {
      super.wait();
    }
    Thread.sleep(200);
    this.num--;
    System.out.println("减法操作" + Thread.currentThread().getName() + " num = " + this.num);
    this.flag = true;
    super.notifyAll();
  }
}

class AddThread implements Runnable {
  private Resource resource;

  public AddThread(Resource resource) {
    this.resource = resource;
  }

  public void run() {
    for (int x = 0; x < 50; x++) {
      try {
        this.resource.add();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}

class SubThread implements Runnable {
  private Resource resource;

  public SubThread(Resource resource) {
    this.resource = resource;
  }

  public void run() {
    for (int x = 0; x < 50; x++) {
      try {
        this.resource.sub();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}

设计一个生产电脑和搬运电脑的类,要求生产出一台电脑就搬走一台电脑,如果没有新的电脑生产出来,则搬运要等待,如果生产的没有搬走,则要等待搬走后再生产,并统计处生产的电脑的数量。

public class Test {
  public static void main(String[] args) {
    Resource resource = new Resource();
    new Thread(new Producer(resource)).start();
    new Thread(new Consumer(resource)).start();
  }
}

class Producer implements Runnable {
  private Resource resource;

  public Producer(Resource resource) {
    this.resource = resource;
  }

  public void run() {
    for (int x = 0; x < 50; x++) {
      try {
        this.resource.produce();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}

class Consumer implements Runnable {
  private Resource resource;

  public Consumer(Resource resource) {
    this.resource = resource;
  }

  public void run() {
    for (int x = 0; x < 50; x++) {
      try {
        this.resource.get();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}

class Resource {
  private Computer computer;

  public synchronized void produce() throws InterruptedException {
    if (this.computer != null) {
      super.wait();
    }
    this.computer = new Computer("名字", 1111);
    System.out.println("生产" + this.computer);
    super.notifyAll();
  }

  public synchronized void get() throws InterruptedException {
    if (this.computer == null) {
      super.wait();
    }
    System.out.println("取走" + this.computer);
    this.computer = null;
    super.notifyAll();
  }
}

class Computer {
  private static int count = 0;
  private String name;
  private double price;

  public Computer(String name, double price) {
    this.name = name;
    this.price = price;
    count++;
  }

  public String toString() {
    return "名字" + this.name + "价值 " + this.price + "第" + count + "台";
  }
}

竞拍抢答:设置三个抢答线程,同时发出抢答指令,输出抢答结果

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

public class Test {
  public static void main(String[] args) throws ExecutionException, InterruptedException {
    MyThread mt = new MyThread();
    FutureTask<String> task = new FutureTask<>(mt);
    new Thread(task, "A").start();
    new Thread(task, "B").start();
    new Thread(task, "C").start();
    System.out.println(task.get());
  }
}

class MyThread implements Callable<String> {
  private boolean flag = false;

  public String call() {
    synchronized (this) {
      if (this.flag == false) {
        this.flag = true;
        return Thread.currentThread().getName() + " 成功";
      } else {
        return Thread.currentThread().getName() + " 失败";
      }
    }
  }
}