并发基础知识

线程的概念并发基础知识

进程是系统进行资源分配和调度的基本单位,线程则是进程的一个执行路径,一个进程中至少有一个线程,进程中的多个线程共享进程的资源。真正占用 CPU 的是线程,线程是 CPU 分配的基本单位。

Java 经常以 Main 函数作为程序执行的入口,Main 函数所在的线程就是进程中的一个线程,一般称为主线程。在 Java 中多个线程共享进程的堆和方法区,但是每个线程有自己的程序计数器和栈。

线程创建的方式

  • 继承 Thread 方式
    public class ThreadStudy extends Thread {
        @Override
        public void run() {
            System.out.println(Thread.currentThread.getName());
      }
    }
    // 启动方式
    Thread threadStudy = new ThreadStudy();
    threadStudy.start();
  • 实现 Runnable 接口方式
    public class RunnableTaskStudy implements Runnable {
        @Override
        public void run() {
            System.out.println(Thread.currentTread.getName());
      }
    }
    // 启动方式
    RunnableTaskStudy taskStudy = new RunnableTaskStudy();
    Thread thread1 = new Thread(taskStudy);
    Thread thread2 = new Thread(taskStudy);
    thread1.start();
    thread2.start();
  • 实现 Callable 接口,该方式可以获取返回值
    public class CallableTaskStudy implements Callable<String{
        @Override
        public void call() throws Exception {
            return "hello";
        }
    }

    // 启动
    FutureTask<String> futureTask = new FutureTask<>(new CallableStudy());
    Thread thread = new Thread(futureTask);
    thread.start();
    try {
        String result = futureTask.get();
        LOGGER.info("Result is " + result);
    catch (InterruptedException | ExecutionException e) {
        LOGGER.info(e.getMessage());
        throw e;
    }

线程通知与等待

  • 当一个线程调用一个共享变量的 wait() 方法后,该线程被阻塞挂起,直到下面几件事情发生返回:
    1. 其它线程调用了该共享变量的 notify(), notifyAll() 方法。
    2. 其它线程调用了该线程的 interrupt() 方法,该线程抛出 InterruptedException 异常后返回。

    调用共享变量的 wait() 方法前,必须先获取该共享变量的监视器锁。

  • 一个线程调用了共享变量的 notify() 方法时后,会唤醒一个在该共享变量上调用 wait() 系列方法后被挂起的线程

    被唤醒的线程不能马上从 wait() 方法返回并继续执行,它必须在获取了共享变量的监视器锁之后才可以返回。

  • 代码实现

    代码实现需要注意的地方是在判断是否满足执行条件时需要使用 while 循环来防止虚假唤醒

    static class PurchaseThread extends Thread {
        private static final Logger LOGGER = Logger.getLogger(PurchaseThread.class.toString());

        private final Resource resource;

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

        @Override
        public void run() {
            try {
                resource.purchase();
            } catch (InterruptedException exp) {
                LOGGER.severe(exp.getMessage());
            }
        }
    }

    static class SaleThread extends Thread {
        private static final Logger LOGGER = Logger.getLogger(SaleThread.class.toString());

        private final Resource resource;

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

        @Override
        public void run() {
            try {
                resource.sale();
            } catch (InterruptedException exp) {
                LOGGER.severe(exp.getMessage());
            }
        }
    }

    static class Resource {
        private static final Logger LOGGER = Logger.getLogger(Resource.class.toString());

        private static final int MAX_SIZE = 10;

        private final Deque<String> products;

        public Resource(Deque<String> products) {
            this.products = products;
        }

        public void purchase() throws InterruptedException {
            synchronized (products) {
                while (products.size() == MAX_SIZE) {
                    products.wait();
                }
                while (products.size() < MAX_SIZE) {
                    products.addLast(UUID.randomUUID().toString());
                }
                products.notifyAll();
            }
        }

        public void sale() throws InterruptedException {
            synchronized (products) {
                // 这里需要通过while循环来防止虚假唤醒,如果这里使用if作为判断,当线程醒来之后就直接执行后面的逻辑了
                while (products.size() == 0) {
                    products.wait();
                }
                LOGGER.info(products.removeLast());
                products.notifyAll();
            }
        }
    }

线程中断

  • interrupt() 方法

    A 线程可以调用 B 线程的 interrupt() 方法来将 B 线程的中断标志位设置为 true 并返回,这里并不会影响 B 线程的执行。如果 B 调用了 wait() 系列函数,join() 方法,sleep() 方法而被挂起,这个时候 A 调用 B 线程的 interrupt() 方法,线程 B 会在调用方法出抛出 InterruptedException 异常并返回。

  • isInterrupted()

    检测当前线程是否被中断

  • interrupted()

    检测当前线程被中断,如果当前线程被中断,则清除中断标志位。

  • 代码实现
    static class PrintThread extends Thread {
        private static final Logger LOGGER = Logger.getLogger(PrintThread.class.toString());

        @Override
        public void run() {
            while (!Thread.currentThread().isInterrupted()) {
                LOGGER.info("Print Yes");
                try {
                    TimeUnit.SECONDS.sleep(5);
                } catch (InterruptedException exp) {
                    // 当休眠时被中断会抛出中断异常
                    LOGGER.severe(exp.getMessage());
                    break;
                }
            }
        }
    }

    PrintThread thread = new PrintThread();
    thread.start();
    TimeUnit.SECONDS.sleep(1);
    // 这里中断线程
    thread.interrupt();
    LOGGER.info("Main thread is over.");

并发基础知识

上一篇:CSUST 黄金矿工 题解(分组背包+转换dp方程状态)


下一篇:解析export * from 与 export {default} from