JAVA并发编程知识点

这篇文章主要记录了多线程基础知识,不会对知识点深入探讨,仅供参考。

线程创建

创建线程的方式

继承Thread类

public class ThreadCreate {
    public static void main(String[] args) {
        // 继承的方式
        SubTread subTread = new SubTread();
        subTread.start();
        System.out.println("主线程已经结束");
    }

    private static class SubTread extends Thread {
        @Override
        public void run() {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("我是继承了 Thread 的线程");;
        }
    }
}

实现Runable接口

public class ThreadCreate {
    public static void main(String[] args) {
        // 实现Runnable接口
        RunnableThread runnableThread = new RunnableThread();
        // 直接调用run方法会使主线程等待三秒钟
        // runnableThread.run();

        // 调用需要使用new Thread()
        new Thread(runnableThread).start();

        System.out.println("主线程已经结束");
    }

    private static class RunnableThread implements Runnable {
        public void run() {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("我是实现了 Runnable 接口的线程");
        }
    }
}

实现Callable接口

public class ThreadCreate {
    public static void main(String[] args) {
        // 实现Callable接口
        CallableThread callableThread = new CallableThread();
        FutureTask<CallableThread> futureTask = new FutureTask<CallableThread>(callableThread);
        new Thread(futureTask).start();
        try {
            System.out.println("实现了 Callable 接口的线程,返回值为:" + futureTask.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("主线程已经结束");
    }

    private static class CallableThread implements Callable {
        public Object call() throws Exception {
            System.out.println("我是实现了 Callable 的线程");
            return "100";
        }
    }
}

三种创建线程的方式的优缺点

  • 对于继承Thread方式
    • 优点是:编码实现简单;
    • 缺点是:java只支持单集成,继承Thread之后无法集成其他类,扩展性有限制。
  • 对于实现Runnable接口方式
    • 优点是:规避了单继承问题,可以实现多个接口;
    • 缺点是:无法拿到线程的返回值。
  • 对于实现Callable接口方式
    • 优点是:规避单继承问题,可以实现多个接口;
    • 缺点是:编码复杂度更高。

线程的状态

线程的状态有哪些

  • new 初始状态
  • runnable 运行状态
    • ready 就绪状态
    • running 运行中
  • blocked 阻塞状态
  • waiting 等待状态
  • time_waiting 超时等待状态
  • terminated 线程终止状态

各状态的关系

image

线程操作

启动线程

调用线程的start()方法。

start()方法和run()方法的区别

start()方法用于启动一个新的线程;run()方法只是一个普通的方法,在主线程中会串行执行。

终止线程

  • 调用stop()方法,强制停止线程。这是过期作废的方法,不推荐使用。
  • 调用interrupt()方法中断线程。
  • 使用退出标志,让线程正常退出。即run()方法执行结束。

interrupt()方法

给线程设置中断标志,并不会立马中断线程。

interrupted()方法与isInterruputed()方法

  • interrupted()是获取当前现场是否被中断,并且清除中断位标志。
  • isInterrupted()是获取被调用线程是否中断,不清除中断位标志。
public static void main(String[] args) {
        try {
            MyThread thread = new MyThread();
            thread.start();
            thread.interrupt();
            System.out.println("线程thread是否中断=" + thread.isInterrupted());
            System.out.println("线程thread是否中断=" + thread.isInterrupted());
            Thread.currentThread().interrupt();
            System.out.println("main是否中断=" + thread.interrupted());
            // 清除了中断位,所以第二次打印是false
            System.out.println("main是否中断=" + thread.interrupted());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static class MyThread extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 500000; ) {
                i++;
            }
        }
    }

执行结果

线程thread是否中断=true
线程thread是否中断=true
main是否中断=true
main是否中断=false

线程安全

代码在多线程环境下和单线程环境下,执行的结果相同,代表是线程安全的。

线程和进程的区别

  1. 从概念上来说,进程是一个“执行中的程序”,是系统进行资源分配和调度的一个独立单位;线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。

  2. 从CPU层面来说,进程和线程其实都是CPU工作时间段的描述,只不过是颗粒大小不同。

  3. 从地址空间来说,同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间;

线程之间怎么通信

  1. 内存共享,在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过读-写内存中的公共状态来隐式的进行通信。

  2. 消息传递。顾名思义,就是通过明确的发送消息来显式的进行通信。这里又经常会涉及到等待唤醒机制:wait() 和 notify() 等相关问题了。

线程中yield()方法的作用

运行中的线程,让出自己的CPU执行时间,给自己或者其他线程使用。不是单纯的让给其他线程,有可能自己马上就能获取到CPU的使用权。

yield()方法与sleep()方法的区别

  1. 从线程优先级来说,yield()方法只会给相同优先级或者更高优先级的线程运行机会;但是,sleep()方法给其他线程运行机会时不考虑线程的优先级,因此也会给优先级低的线程运行的机会。

  2. 从运行后状态来说,运行yield()方法之后,线程回到就绪状态,可能又马上被执行;但是,如果执行sleep()方法后,线程会转入time_waiting状态,在指定的时间内肯定不会被执行。

  3. 从抛出异常来说,yield()方法没有声明任何异常;而sleep()方法声明抛出InterruptedException。

  4. 从可移植性上来说,sleep()方法比yield()方法具有更好的可移植性。

线程池

创建线程池的方式

newCachedThreadPool

创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行
很多短期异步任务的程序而言,这些线程池通常可提高程序性能。 调用 execute 将重用以前构造
的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并
从缓存中移除那些已有 60 秒钟未被使用的线程。 因此,长时间保持空闲的线程池不会使用任何资
源。

newFixedThreadPool

创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。在任意点,在大
多数 nThreads 线程会处于处理任务的活动状态。如果在所有线程处于活动状态时提交附加任务,
则在有可用线程之前,附加任务将在队列中等待。如果在关闭前的执行期间由于失败而导致任何
线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。在某个线程被显式地关闭之
前,池中的线程将一直存在。

newScheduledThreadPool

创建一个线程池,给定延迟时间,执行命令或者定期执行命令。

newSingleThreadExecutor

Executors.newSingleThreadExecutor()返回一个线程池(这个线程池只有一个线程) ,这个线程
池可以在线程死后(或发生异常时)重新启动一个线程来替代原来的线程继续执行下去!

posted @ 2021-06-21 16:32  青河丶  阅读(30)  评论(0)    收藏  举报