zoukankan      html  css  js  c++  java
  • Java必会之多线程

    一、线程的基本知识

    1.1 线程知识

    进程和线程的关系和区别

    线程:

    线程是进程的基本执行单元,进程想要执行任务,必须要有线程。程序启动默认开启一条线程,这个线程被称为主线程。

    进程:

    进程是指在系统中正在运行的一个应用程序。每个进程之间是独立的,每个进程均运行在其专用且受保护的内存里。

    线程的六个状态:

    NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED

    Thread流程图:

    Thread的方法:

    方法 说明
    void join() t.join() 当前线程调用其他线程的t.join()方法,当前线程进入等待状态,当前线程不会释放已经持有的锁。线程t执行完毕后,当前线程进入就绪状态。
    static native void sleep() 静态方法,线程睡眠,并让出CPU时间片
    void wait() 当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依靠notify()/notifyAll()唤醒。
    native void notify() 唤醒在此对象监视器上等待的单个线程,选择是任意性的。
    native void notifyAll() 发送信号通知所有等待线程

    1.2 线程安全

    并发的相关性质:

    • 原子性:原子操作。对基本数据类型的读取和赋值操作是原子性操作,即这些操作是不可被中断的,要么执行,要么不执行。

    • 可见性:对于可见性,java提供了volatile关键字来保证可见性。

      当一个共享变量被volatile修饰时,他会保证修改的值会立即被更新到主存,当有其他线程需要读取时,他会去主存中读取新值。

      volatile 不保证原子性。

    • 有序性:Java允许编译器和处理器对指令重排序,但是重排序不会影响到单线程的执行,却会影响到多线程并发执行的正确性。

    synchronized

    使用对象头标记字实现

    使用场景:

    • 修饰方法:
      一个对象中的加锁方法只允许一个线程访问。
    • 修饰静态方法:
      由于静态方法是类方法,所以多个线程不同对象访问这个静态方法,也是可以保证同步的。
    • 修饰代码块:
      如:synchronized(obj){...} 这里的obj可以是类中的一个属性,也可以是对象,这时他跟修饰普通方法一样,如果obj是Object.class这样的,那么效果跟修饰静态方法类似。

    volatile

    1. 每次读取都强制从主内存刷数据
    2. 适用场景:单个线程写,多个线程读
    3. 原则:能不用就不用,不确定的时候也不用
    4. 语义
    • 可见性
    • 禁止指令重排序(不完全保证有序性)
    • 不能保证原子性。

    为什么不保证有序性呢?举个例子说明

    上述代码,语句1和2,不会被重排到3的后面,4和5也不会到3的前面。但是1和2的顺序、4和5的顺序无法保证。

    final

    final定义类型 说明
    final class XXX 不允许继承
    final 方法 不允许Override
    final 局部变量 不允许修改
    final 实例属性 构造函数、初始化块后不能变更。只能赋值一次。构造函数结束返回时,final域最新的值保证对其他线程可见。
    final static 属性 静态块执行后不允许变更,只能赋值一次

    二、线程池

    1. Executor :执行者 -顶层接口
    2. ExecutorService :继承于Executor,线程池的接口API
    3. ThreadFactory:线程工厂
    4. Executors:工具类

    submit方法和execute方法的区别:

    • submit方法:有返回值,用Future封装,执行的方法异常了可以在主线程里catch到。
    • execute方法:无返回值,方法执行异常是捕捉不到的

    如下图:

    ExecutorService主要方法:

    构造线程池的参数

    public ThreadPoolExecutor(int corePoolSize,
                                  int maximumPoolSize,
                                  long keepAliveTime,
                                  TimeUnit unit,
                                  BlockingQueue<Runnable> workQueue,
                                  ThreadFactory threadFactory,
                                  RejectedExecutionHandler handler)
    

    corePoolSize :核心线程数
    maximumPoolSize:最大线程数
    keepAliveTime:当线程数超过了核心线程数,空闲线程需要等待多久等待不到新任务就终止。
    workQueue:任务队列
    threadFactory:线程工厂
    handler:拒绝策略

    提交任务逻辑:

    1. 判断核心线程数
    2. 加入workQueue
    3. 判断最大线程数,没达到就创建
    4. 执行拒绝策略(默认是抛异常)

    缓冲队列

    1. ArrayBlockingQueue:规定大小的BlockingQueue,构造时必须指定大小
    2. LinkedBlockingQueue:大小不固定的BlockingQueue,如果构造时指定大小,则有大小限制,不指定大小,则用Integer.MAX_VALUE来决定
    3. PriorityBlockingQueue:类似于LinkedBlockingQueue,不同在它根据对象的自然顺序或者构造函数的Comparator进行排序,不是FIFO
    4. SynchronizedQueue:特殊的BlockingQueue,对其的操作必须是放和取交替完成。

    拒绝策略:

    1. AbortPolicy:丢弃任务并抛异常
    2. DiscardPolicy:丢弃任务,不抛异常
    3. DiscardOldestPolicy:丢弃队列最前面的任务,重新提交被拒绝的任务
    4. CallerRunsPolicy:由提交任务的线程处理该任务。

    线程工厂(ThreadFactory):

    自定义示例:

    public class CustomThreadFactory implements ThreadFactory {
    
        private AtomicInteger count=new AtomicInteger();
    
        @Override
        public Thread newThread(Runnable r) {
            Thread thread=new Thread(r);
            thread.setDaemon(false);
            thread.setName("customThread-"+count.getAndIncrement());
            return thread;
        }
    }
    

    线程工具类:

    1. newSingleThreadExecutor

      创建一个单线程的线程池。如果这个线程因为异常结束,那么会有一个新的线程替代它。此线程池保证所有的任务的执行顺序按任务提交顺序执行。

    2. newFixedThreadPool

      创建固定大小的线程池。缺点:队列使用的LinkedBlockingQueue,且没有限制大小。

    3. newCachedThreadPool

      创建一个可缓存的队列,如果线程池大小超过了处理任务需要的线程,那么就会回收部分空闲线程。缺点:此线程池不会对线程池大小做限制。

    4. newScheduledThreadPool

      创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

    创建固定线程池的经验:

    假设服务器核心数为N

    1. 如果是CPU密集型应用,则线程池大小设置为N或N+1
    2. 如果是IO密集型应用,则线程池大小设置为2N或2N+2
    书山有路勤为径,学海无涯苦作舟
  • 相关阅读:
    算法学习记录单链表
    算法学习记录排序
    算法学习记录图——最小生成树之prim算法
    算法学习记录图——应用之拓扑排序(Topological Sort)
    算法学习记录图(DFS BFS)
    算法学习记录排序——冒泡排序(Bubble Sort)
    算法学习记录图——最小路径之Floyd算法
    自用SqlHelper.cs
    WPF的DataGrid的标题加粗样式
    .NET Framework 4.0不能用sqlite的问题解决方法
  • 原文地址:https://www.cnblogs.com/javammc/p/14837189.html
Copyright © 2011-2022 走看看