当前位置: 首页 > news >正文

阿里大佬总结,Java高并发必读!

作者:wxdoop

原文:https://blog.csdn.net/qq_36235098

来源:前程有光

前言

进程是计算机中程序关于某几何数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位。是操作系统结构的基础

线程可以说是轻量级的进程,是程序执行的最小单位,使用多线程而不用多进程去进行并发程序的设计,是因为线程之间的切换与调度的成本远小于进程。

线程的几种状态

New状态表示刚刚创建的线程,这种线程还没有开始执行
RUNNABLE:当线程创建好之后,调用线程的start方法就会进入就绪状态。
BLOCKED:当线程运行过程如果遇到了Syschronized就会进入阻塞状态。
TIMED_WATING:表示等待状态,有时间限制的(sleep)
WAITING:表示进入一个无时间显示的等待状态(wait)
TERMINATED:表示结束状态。

线程的基本操作

新建线程

实现Runable接口或者new Thread

终止线程

为什么不建议使用stop?

因为stop方法比较暴力,强行把执行的程序终止,可能会引发数据不一致的问题,Thread.stop()方法在结束线程时,会直接终止线程,并立即释放这个线程的所持有的锁。比如数据写到一半,强行终止掉了,数据就会被破坏。

线程中断

interrupt() //中断线程。相当于设置中断标志位
isInterrypted() //判断是否被中断
interrupted() //判断是都被中断,并清除当前的中断状态

Thread.sleep()方法由于中断而抛出异常,此时它会清除中断标记,如果不加处理,那么下一次循环开始时,就无法捕获这个异常,可以再次设置中断标记来方便循环判断。

等待和通知

当在一个对象实力上条用wait()方法之后。当前线程就会在这个对象上等待。在线程A中调用了obj.wait()方法,那么变成A就会停止继续执行,转为等待状态,直到其他线程调用了obj.notify()方法才有机会继续执行(并不是一定)

如果一个线程调用了obj.wait()那么它就会进入这个object的等待队列,这个等待队列可能会有多个线程,因为系统运行多个线程同时等待某一个对象,当obj.notify()方法被调用时,就会随机选择一个线程,将其唤醒,这个唤醒是完全随机的。

notifyAll()方法的功能是唤醒所有在该对象等待队列中等待的线程。

守护线程的finally不一定会执行。如果除守护线程外的所有线程都结束了,那么守护线程就立即退出没有执行finally代码块的机会。

挂起(suspend)和继续(resume)执行

不推荐执行suspend(),因为该方法在导致线程暂停的同时,并不会释放任何资源。影响其他想要访问该资源的线程,直到当前线程使用resume操作,是当前线程继续执行,但是此处有一个隐患。如果resume在suspend之前运行了,那么被挂起的线程将不能继续执行下去。

public class BadSuspend {
    public static Object u = new Object();
    static ChangeObjectThread t1 = new ChangeObjectThread("t1");
    static ChangeObjectThread t2 = new ChangeObjectThread("t2");

    public static class ChangeObjectThread extends Thread {
        public ChangeObjectThread(String name){
            super.setName(name);
        }
        @Override
        public void run() {
            synchronized (u) {
                System.out.println("in "+getName());
                Thread.currentThread().suspend();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        t1.start();
        Thread.sleep(100);
        t2.start();
        t1.resume();
        t2.resume();//无法直到这一行先执行,还是Thread.currentThread().suspend();先执行
        t1.join();
        t2.join();
    }
}

等待线程结束(join)和谦让(yeild)

public final synchronized void join(long millis) throws InterruptedException//有最大等待时长的等待

public final synchronized void join(long millis, int nanos)
throws InterruptedException//毫秒,加纳秒级别的等待

public final void join() throws InterruptedException//表示无限期的等待

package com.atmb.me;

public class joinAndYeild {
    public volatile  static  int i =0;

    public static class myThread implements Runnable{
        @Override
        public void run() {
            for (int j = 0; j < 1000000; j++) {
                i+=1;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new myThread());
        thread.start();
        thread.join();//等待thread线程执行结束
        System.out.println(i);//结果为1000000
    }
}

    public static native void yield();

这个方法一旦执行,就会使当前线程让出cpu,让出cpu之后,该线程会继续进行cpu资源的争夺,之后继续正常运行。

线程组

package com.atmb.me;

public class ThreadGroupTest implements Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getThreadGroup().getName()+"==="+Thread.currentThread().getName());
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        ThreadGroup group1 = new ThreadGroup("group1");
        Thread thread1 = new Thread(group1, new ThreadGroupTest());
        Thread thread2 = new Thread(group1, new ThreadGroupTest());
        thread1.start();
        thread2.start();
        System.out.println(group1.activeCount());//活跃线程数
        group1.list();
    }
}
<<<
2
group1===Thread-0
java.lang.ThreadGroup[name=group1,maxpri=10]
group1===Thread-1
    Thread[Thread-0,5,group1]
    Thread[Thread-1,5,group1]

守护线程(Daemon)

守护线程要守护的对象已经不存在了,那么整个应用程序就应该技术,因此当java应用内只有守护线程时,java虚拟机就会自然退出。

package geym.ch2;

public class DaemonDemo {
    public static class DaemonT extends Thread{
        public void run(){
            while(true){
                System.out.println("I am alive");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Thread t=new DaemonT();
        t.setDaemon(true);
        t.start();

//        Thread.sleep(2000);
    }
}

设置守护线程,必须要在start之前设置,否则会得到异常信息。

线程优先级

Java中的线程可以有自己的优先级。优先级高的线程在竞争资源时,会更有优势,更大概率抢占到资源,java中线程优先级可以设置为1-10.
其中有三个静态标量标识三个对应的优先级。

    /**
     * The minimum priority that a thread can have.
     */
    public final static int MIN_PRIORITY = 1;

   /**
     * The default priority that is assigned to a thread.
     */
    public final static int NORM_PRIORITY = 5;

    /**
     * The maximum priority that a thread can have.
     */
    public final static int MAX_PRIORITY = 10;

public class PriorityDemo {
    public static class HightPriority extends Thread{
        static int count=0;
        public void run(){
            while(true){
                synchronized(PriorityDemo.class){
                    count++;
                    if(count>10000000){
                        System.out.println("HightPriority is complete");
                        break;
                    }
                }
            }
        }
    }
    public static class LowPriority extends Thread{
        static int count=0;
        public void run(){
            while(true){
                synchronized(PriorityDemo.class){
                    count++;
                    if(count>10000000){
                        System.out.println("LowPriority is complete");
                        break;
                    }
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread high=new HightPriority();
        LowPriority low=new LowPriority();
        high.setPriority(Thread.MIN_PRIORITY);
        low.setPriority(Thread.MAX_PRIORITY);
        low.start();
        high.start();
    }
}

线程安全与关键字synchronized

synchronized

  • 指定加锁对象,对给定的对象进行加锁,

  • 用于实例方法,对当前的实例进行加锁

  • 用于静态方法,对当前类进行加锁

错误的加锁

package com.atmb.me;

public class lockError implements Runnable {
    public static Integer i =0;
    public static lockError instance =  new lockError();

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(instance);
        Thread thread2 = new Thread(instance);
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println(i);
    }

    @Override
    public void run() {
        for (int j = 0; j < 100000; j++) {
            synchronized (i){
                i+=1;
            }
        }
    }
}
<<<
142957

原因在于Integer类型属于不可变对象,一旦创建就不可能被改变,当integer对象赋值为新值时,当前引用指向的对象就改变了。

指令重排的前提

指令重排需要保证串行语义的一致性,指令重排不会使串行的语义逻辑发生问题。

指令重排的目的?

为了减少中断流水线

那些指令不能重排

  • 程序顺序原则,一个线程内保证语义的串行性

  • volatile规则:volatile变量的写先与读发生,宝整理volatile变量的可见性

  • 传递性:解锁必然发生在随后的加锁前

  • 线程的start()方法先于它的动作

  • 线程的所有操作先于线程的终结(interrupt)

  • 线程的中断(interrupt())先于被中断线程的代码

  • 对象的构造函数的执行,结束先于finalize()的方法

是否会释放锁

  • yield让出cpu执行权,不会释放锁

  • sleep休眠时,不会释放锁

  • 调用wait方法之后,会释放锁,唤醒之后,会再次竞争锁,然后执行wait()方法后的代码

  • nofify nodifyall 对锁没有影响,一般放同步代码块的最后一行

最后

感谢你看到这里,每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!

RECOMMEND

推荐阅读

01

《Java高并发编程详解:多线程与架构设计》

点击链接了解详情并购买

推荐理由:作者毫无保留地分享了多年的Java服务器、大数据程序开发架构经验和最佳实践,从底层原理的角度总结和归纳各个技术细节,,不仅介绍了高速缓存Cache、Cache Line、Java内存模型,以及现代CPU基本架构等相关知识,还结合真实的案例详解Java跨平台的高并发解决方案。

02

《Java高并发编程详解:深入理解并发核心库》

点击链接了解详情并购买

推荐理由:《Java高并发编程详解:多线程与架构设计》的姊妹篇!从底层原理总结和归纳各个技术细节,结合真实的案例深入分析微基准测试、性能度量、Java高并发类库的原理及应用。详细介绍Java微基准测试工具集JMH与平台级性能指标数据度量工具Metrics的使用方法,帮助读者快速开发出高效、健壮的并发应用程序。

 

03

《Java并发编程实战》

点击链接了解详情并购买

推荐理由:Java并发编程里程碑著作!10年畅销100000册。书中采用循序渐进的讲解方式,从并发编程的基本理论入手,逐步介绍了在设计Java并发程序时各种重要的设计原则、设计模式以及思维模式,同时辅以丰富的示例代码作为对照和补充,使得开发人员能够更快地领悟Java并发编程的要领,快速地构建大规模的并发应用程序。

04

《Java多线程编程核心技术(第2版)》

点击链接了解详情并购买

推荐理由:技术畅销书全新升级,案例式讲解,逐一分析和验证每个技术点,通俗易懂。本书涵盖多线程编程的核心库、方法、原理,解决高并发环境下的业务瓶颈。

更多精彩回顾

书讯 |10月书讯(下)| 双节同庆,读书正当时

书讯 |10月书讯(上)| 双节同庆,读书正当时

资讯 |TIOBE 10 月编程语言排行榜出炉:C语言居首,Python将超Java?

上新 | 百度官方出品 | 全面解读PaddlePaddle,零基础快速入门深度学习
书单 | 开学季——计算机专业学生必读的10本畅销经典

干货 | 数据分析必读干货:简单而实用的3大分析方法

收藏 | (万字长文)Spring的核心知识尽揽其中

视频 | 大佬出镜推荐不可不读系列——程序员陈彼得

赠书 | 【第26期】Coroutines(协程)我是这样理解的!

点击阅读全文查看更多好书

相关文章:

  • 软萌硬核科普必读《大圣陪你学AI》
  • 什么是HBase?它是怎样工作的?终于有人讲明白了
  • “脚本小子”和真正黑客的区别是什么?
  • “饿了么”​从测试标准化走向测试服务化
  • 机器学习领域已经百花齐放了,为何还要搞个TinyML?
  • 京东程序员狂欢节,上百本好书一抢而空
  • 《数字孪生体技术白皮书》重磅发布(附完整版下载)
  • 1024长沙程序员节北京移动开发者大会,独家现场报道
  • 为什么数字中台是企业应用新基建?
  • 作者专访 | 从受益者到贡献者,朱凯的 ClickHouse 升级之路
  • 这本书堪称仿真领域的“隆中对”
  • 当Nginx遇到重定向
  • “深入浅出”《Head First Kotlin程序设计》
  • RPA的总成本和总收益分析
  • 前端音视频之WebRTC初探
  • [微信小程序] 使用ES6特性Class后出现编译异常
  • Android优雅地处理按钮重复点击
  • ES2017异步函数现已正式可用
  • Flex布局到底解决了什么问题
  • git 常用命令
  • httpie使用详解
  • Iterator 和 for...of 循环
  • Java的Interrupt与线程中断
  • java第三方包学习之lombok
  • jQuery(一)
  • Redis的resp协议
  • select2 取值 遍历 设置默认值
  • Sublime Text 2/3 绑定Eclipse快捷键
  • vue-loader 源码解析系列之 selector
  • 第三十一到第三十三天:我是精明的小卖家(一)
  • 工作手记之html2canvas使用概述
  • 简单实现一个textarea自适应高度
  • 利用阿里云 OSS 搭建私有 Docker 仓库
  • 码农张的Bug人生 - 初来乍到
  • 你不可错过的前端面试题(一)
  • 前端技术周刊 2019-01-14:客户端存储
  • 如何邀请好友注册您的网站(模拟百度网盘)
  • 深度学习入门:10门免费线上课程推荐
  • 一天一个设计模式之JS实现——适配器模式
  • raise 与 raise ... from 的区别
  • ​人工智能之父图灵诞辰纪念日,一起来看最受读者欢迎的AI技术好书
  • # 数据结构
  • #NOIP 2014#Day.2 T3 解方程
  • (06)Hive——正则表达式
  • (2020)Java后端开发----(面试题和笔试题)
  • (ibm)Java 语言的 XPath API
  • (vue)页面文件上传获取:action地址
  • (第8天)保姆级 PL/SQL Developer 安装与配置
  • (二)fiber的基本认识
  • (二)Linux——Linux常用指令
  • (二十五)admin-boot项目之集成消息队列Rabbitmq
  • (翻译)terry crowley: 写给程序员
  • (附源码)计算机毕业设计SSM基于java的云顶博客系统
  • (附源码)小程序儿童艺术培训机构教育管理小程序 毕业设计 201740
  • (解决办法)ASP.NET导出Excel,打开时提示“您尝试打开文件'XXX.xls'的格式与文件扩展名指定文件不一致