zoukankan      html  css  js  c++  java
  • Java多线程基础

    线程内存模型

    参考:

    多线程-内存模型
    java线程详解

    概述:每个线程都有自己的工作内存,在JVM层面,包含:

    • 程序计数器
    • 线程栈

    线程分类

    常规划分为两类:

    • 用户线程:除守护线程都是用户线程
    • 守护线程:为用户线程提供一种通用的服务,典型如GC线程,当进程中不存在非守护线程,则守护线程会自动销毁
    • 用户线程转变为守护线程:用户线程在start之前可以通过setDaemo(true)来转变为守护线程。如果在start之后调用setDaemo(true),将会throw IllegalThreadStateException, 参考用户线程和守护线程

    线程状态

    参考:

    线程的五种状态

    线程可以分为5种状态:(常用这几种,实际可分为6种)

    • 初始(new): 刚创建的线程
    • 就绪(Runnable): 线程被其他线程(如main线程)调用了该线程对象的start()方法,即会进入就绪线程池,等待cpu以随机的时间调用线程的run()方法,运行线程
    • 运行(Running): 线程获得了cpu 时间片(timeslice), 正在执行程序代码。
    • 阻塞(Blocked): 阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行(running)状态。阻塞的情况分三种:

    (一). 等待阻塞:运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)中。
    (二). 同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。
    (三). 其他阻塞:运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。

    • 死亡(Dead) :线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。

    线程不安全

    当多个线程对同一个对象的实例变量(全局变量),进行读写时,发生变量值不一致,在多个线程间值不同步的情况,进而影响程序执行流程


    start()

    • 线程通过start()方法通知“线程规划器”此线程已经准备就绪,等待调用线程对象的run()方法运行线程
    • 如果有多个线程start,cpu对线程的调用是随机的,即执行start()方法的顺序不代表线程的启动顺序(具有异步执行的效果)
    • 如果调用thread.run()就不是异步执行,而是同步,那么此线程对象并不交给“线程规划器”来进行处理,而是由main主线程来调用run()方法,也就是必须等待run()方法中的代码执行完毕后才执行后面的代码
    • 线程执行run方法,即创建一个栈帧在自己的线程栈中

    共享变量

    • 局部变量不是共享变量,当多个线程指向同一个线程对象时(该线程对象位于堆中),线程对象中的局部变量会在每个线程栈中单独读写,参考java中的变量类型
    • 共享变量,该变量位于堆区,多个线程都可以进行读写
    • 为了保证共享变量在多个线程间的可见性,可以通过synchronized和volatile关键字,锁机制等

    synchronized关键字

    • synchronized关键字可以加在任意方法和对象前,即对它们上锁,而加锁的这段代码称为同步代码,解决多个线程之间访问资源的同步性(保证原子性和可见性)
    • 当一个运行状态的线程想要执行同步代码时,需要先拿到这上面的锁,如果拿不到(被别的线程占用),会和处于锁标志等待池(lock pool)的线程都会一直尝试争夺这把锁
    • synchronized取得的锁都是对象锁,如下:

    1: 修饰实例方法,锁为当前实例对象
    2: 修饰静态方法,锁为当前类的class对象
    3: 修饰代码块,锁可以设置this(当前实例对象),或是this.class(当前类的class对象),或是其他对象

    • synchronized锁重入,当一个线程得到一个对象锁后,再次请求此对象锁(同一把锁)时可以再次得到,如:

    1: 在一个synchronized方法/块的内部调用本地的其他synchronized方法/块时,是永远可以得到锁的
    2: 父子类继承关系中,子类可以通过可重入锁调用父类的同步方法


    volatile关键字

    • 当修饰共享变量时,可以保证变量在多线程下的可见性,不保证原子性
    • 当CPU发现是volatile修饰的共享变量时,会通知其他线程缓存的该变量无效,当其他线程读写该变量时,发现无效后会重新从主存中加载数据
    • 全面理解Java内存模型(JMM)及volatile关键字

    线程中断

    • 停止线程即在一个线程未完成任务前放弃当前的操作
    • java中停止正在运行的线程:

    1:使用退出标志,之后当线程运行完run方法后线程终止
    2:使用stop()方法强行终止,不推荐,stop(),suspend(),resume()方法都是过期作废的
    3: 使用 return 停止异常,即run()方法中符合条件后return,也会终止线程
    4:使用异常法(推荐),如:

    (1)先调用线程的interrupt()方法,在运行run()方法中出现sleep(),会抛出InterruptedException异常,终止线程
    (2)先在run()方法中出现sleep()方法,之后被调用interrupt()方法,也会抛出InterruptedException异常
    (3)未处理的RuntimeException,发生异常后线程终止


    wait(),notify(),notifyAll()

    • wait()方法即让当前线程释放对象琐,进入线程等待队列
    • notify()即从线程等待队列中移出任意一个线程放入锁池中,争抢对象锁
    • notifyAll()即将等待队列的所有线程移入锁池中

    join()

    • join()会让所属线程X运行run方法,而让当前线程Y进入无限期阻塞,等待X销毁后,再继续执行Y后面的代码
    • 在Y的run()方法中执行X.join()后,如果此时被Z线程调用Y.interrupt(),则Y线程会抛出InterruptedException异常,X线程正常运行
    • join(long)内部采用的是wait(long),会让当前线程释放锁,而sleep(long)不会让当前线程释放锁,即调用sleep(long)后,其他线程不能执行当前线程的同步方法

    ThreadLocal

    • ThreadLocal对象有get()和set()方法,会将放入其中的变量与调用set()方法的线程绑定,使每个线程都可以向其中存放自己的私有变量
    • InheritableThreadLocal类,可以让子线程获取到父线程存放在InheritableThreadLocal对象中的值

    ReentrantLock类

    • 是一个java类,调用该类实例对象的 lock() 方 法获取锁,调用 unlock() 方法释放锁
    • 与synchronized的区别
    • Lock使用的是乐观锁,synchronized使用的是悲观锁,具体

    Condition

    • 是一个类,通过Lock对象来创建
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    
    • await() , signal() , signalAll() 方法即实现线程间通信,注意:在调用它们前需要先调用 lock() 方法获得同步监视器,记得再通过 unlock() 方法释放

    公平锁与非公平锁

    • 公平锁 表示线程获取锁的顺序是按照线程加锁的顺序来分配的,即先来先得的FIFO先进先出顺序
    • 非公平锁 是一种锁的抢占机制,是随机获取锁的
    • ReentrantLock类 默认 使用的是非公平锁
    //公平锁
    ReentrantLock lock = new ReentrantLock(true);
    //查看是否为公平锁
    Boolean bo = lock.isFair() //true
    
    //非公平锁
    ReentrantLock lock = new ReentrantLock(false);
    //查看是否为公平锁
    Boolean bo = lock.isFair() //false
    

    ReentrantReadWriteLock类

    • 类ReentrantLock同一时间只有一个线程执行ReentrantLock.lock()方法后面的任务,保证了实例变量的安全性,但是效率较低
    • 读写锁ReentrantReadWriteLock表示有两个锁,一个读操作相关的锁,是共享锁;一个是写操作相关的锁,是排他锁
    • 同一时刻多个线程能进行读操作,只有一个线程能进行写操作
    ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    //读锁
    lock.readLock().lock();
    lock.readLock().unlock();
    
    //写锁
    lock.writeLock().lock();
    lock.writeLock.unlock();
    
  • 相关阅读:
    弱口令扫描.python脚本
    扫描web目录的Python小脚本
    Linux /etc目录重要文件
    linux(centos7)系统优化
    泛型程序设计
    对象包装器与自动装箱
    打包apk时,提示:error: Error: Resource is not public.
    SpringMVC分页实现
    IDEA搭建基于maven 的SSM框架
    ssm框架搭建
  • 原文地址:https://www.cnblogs.com/wuba/p/11627770.html
Copyright © 2011-2022 走看看