Java 多线程

1、多线程简介

1.1线程和进程

Java 给多线程编程提供了内置的支持。 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。多线程是多任务的一种特别的形式,但多线程使用了更小的资源开销。


这里定义和线程相关的另一个术语 - 进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。

多线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的。**
(进程——自我理解——一个正在执行的程序,称之为一个进程。)
(线程——本身没有资源,所有线程共享进程的资源。)

同一时间,一个CPU(一个核),只能处理一个线程!!!

你在任务管理器中查看到的几千个线程,并不不是在同一时间执行的。

1.4 线程的交互

交互的方式 包括 :  互斥  、同步。
争用条件

互斥

同步

1.5 Java对线程的支持

1.6 如何正确地停止一个线程?

不正确的线程停止方法

stop()调用后,会不知道,线程执行到了哪里,甚至,还没有执行完,就被突然停止了。会造成很多问题。比如,如果此处涉及到事务,那么,事务,可能就不会被提交或者出现异常后,不会被回滚。

不正确的停止线程的方法2:

注意:此时,线程的中断状态,就会被清除,再次调用interrupted()方法,就会返回false。

正确停止线程的方法——使用退出标志

1.7 Daemon 守护线程和 用户线程

daemon线程解释: 当线程只剩下守护线程的话,jvm会退出。但是如果还有其他的任意一个用户线程还在,jvm就不会退出。main线程是用户线程。

守护线程和主线程有着同样的生命周期,主线程不存在,子线程也就不存在了
非守护线程和主线程无关,主线程关闭,子线程继续执行

1.8 可重入锁

A:概念
最大的作用是可以多次获取该锁,而避免死锁。

电影院售票示例

B:可重入所实现原理

精品文章1

C:可重入锁应用场景

D:注意事项

  1. lock.lock() ,不要放在try语句里面,防止获取锁失败,直接释放锁。
  2. 获取锁的次数和释放锁的次数要一致!

E:使用方式
1. 一般在成员变量位置,定义一个锁(对象锁?)。在方法(临界区)进行上锁。finally中一定要unlock.

1.9 多线程的理解

  • (1) 程序可以同时由多个线程执行多个方法(代码)

3、线程池

3.1 线程池的创建

  • 阿里巴巴开发规范
    线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors返回的线程池对象的弊端如下:
    1)FixedThreadPool和SingleThreadPool:
      允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。
    2)CachedThreadPool:
      允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。

Positive example 1:

//org.apache.commons.lang3.concurrent.BasicThreadFactory
    ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1,
        new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build());

Positive example 2:

ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
        .setNameFormat("demo-pool-%d").build();

    //Common Thread Pool
    ExecutorService pool = new ThreadPoolExecutor(5, 200,
        0L, TimeUnit.MILLISECONDS,
        new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());

    pool.execute(()-> System.out.println(Thread.currentThread().getName()));
    pool.shutdown();//gracefully shutdown

Junit 与 Java多线程

暂时,无法正确测试

总结

1、线程创建后,调用线程的start()方法开启一个线程,线程开启后,每个线程会执行线程的核心业务方法run()方法 。并且只会执行一次。

2、创建线程类的三种方式:

继承Thread  类

实现 Runnable 接口

实现Callable 或者Futre接口

5、并发工具类

A:CountDownLatch
参考源码中的文档查看:

B:FutureTask 配合Callable接口使用

6、Java原子类以及实现原理

精品文章1
精品文章2

7、悲观锁和乐观锁

悲观锁: Synchronized

乐观锁:CAS 机制

8、 线程编写经验

  • (1)run方法中,会写一个while 无限循环,在特定条件下使用return结束任务。
  • (2)在构造器中启动线程将会产生问题,因为,另一个任务可能会在构造器结束之前,开始执行,这意味着,该线程可以访问不稳定状态的对象。
  • (3)使用内部类隐藏线程代码

9、 线程中异常的处理

  • (1) 异常不能够跨线程传播,必须在任务方法中进行捕获处理。

二、多线程编程思想

2.1 多线程的使用场景?

  • 1. 一次请求很久才能响应

    当主线程在处理一件事情时,发现在处理过程中,会消耗很长时间。主线程可能会等待后续的响应。此时,可以使用多条子线程来处理后续的任务。
    示例:主线程一次性查询出 线程数量 * 1000 条数据从数据库。然后使用线程池,提交一定数量的线程来处理这些数据。

三、Java并发编程的艺术

3.1 上下文切换

Linux 如何测试上下文切换消耗?

A:Java性能调优工具

  • jstack
    Jstack是Jdk自带的线程跟踪工具,用于打印指定Java进程的线程堆栈信息。

配合使用Linux文本分析命令分析
grep | awk | sort | uniq -c

四、多线程面试题

4.1 wait 和sleep 方法的区别 ?

wati 和sleep 方法的区别?

喜欢出发、喜欢离开、喜欢不一样的事物。——May
原文地址:https://www.cnblogs.com/I-Say/p/14479299.html