精华内容
下载资源
问答
  • oracle中如何判断一个字符串是否含有汉字 一.1 BLOG文档结构图 一.2 前言部分 一.2.1 导读 各位技术爱好者,看完本文后,你可以掌握如下技能,也可以学到一些其它你所不知道知识,~O(∩_∩)O~: ①全角字符...

    oracle中如何判断一个字符串是否含有汉字

    oracle中如何判断一个字符串是否含有汉字

    一.1  BLOG文档结构图

     

    wps3D5D.tmp 

     

    一.2  前言部分

     

    一.2.1  导读

    各位技术爱好者,看完本文后,你可以掌握如下的技能,也可以学到一些其它你所不知道的知识,~O(∩_∩)O~:

    全角字符的判断,或者是含有汉字的字符串的判断

     

     

     

    本文如有错误或不完善的地方请大家多多指正,ITPUB留言或QQ皆可,您的批评指正是我写作的最大动力。

    一.2.2  实验环境介绍

     

    11.2.0.3  RHEL6.5

     

     

     

    一.2.3  本文简介

     

     

    看到网友问,怎么查询表中某个字段数据是不是包含了全角字符啊? 这个问题涉及到几个个函数:to_single_bytelengthlengthb,我之前做开发的时候研究的是如何判断一个字符串中是否包含中文,其实和这个本质是一样的,且看实验部分。

     

    wps3D5E.tmp 

     

     

     

    一.3  实验部分

     

    一.3.1  lengthblength函数结合to_single_byte函数

    ---含有汉字,严格的说是含有全角字符

    SELECT l.name,

           length(l.name),

           lengthb(l.name)

    FROM   xb_link l

    WHERE  length(l.name) != lengthb(l.name)

    AND    length(l.name) < 20;

    wps3D6E.tmp 

     

     

    以下数据也满足条件:

     SELECT l.id,

            l.name

     FROM   xb_link l

     WHERE  length(l.name) != lengthb(l.name)

     AND    l.metacategory IN

            ('com.gxlu.ngrm.network.DDNCircuit',

              'com.gxlu.ngrm.network.FRCircuit',

              'com.gxlu.ngrm.network.ATMCircuit',

              'com.gxlu.ngrm.network.DDNOCircuit',

              'com.gxlu.ngrm.network.FROCircuit')

     AND    l.id IN ('301898331', '301898335', '301908187', '301929403');

     

    wps3D6F.tmp 

    所以可以借助to_single_byte函数来解决。

     

     SELECT l.id,

            l.name,

            to_single_byte(l.name),

            length(l.name) l1,

            lengthb(l.name) l2,

            length(to_single_byte(l.name)) l

     FROM   xb_link l

     WHERE  length(l.name) != lengthb(l.name)

     AND    l.metacategory IN

            ('com.gxlu.ngrm.network.DDNCircuit',

              'com.gxlu.ngrm.network.FRCircuit',

              'com.gxlu.ngrm.network.ATMCircuit',

              'com.gxlu.ngrm.network.DDNOCircuit',

              'com.gxlu.ngrm.network.FROCircuit')

     AND    l.id IN ('301898331', '301898335', '301908187', '301929403');

    wps3D70.tmp 

     

    一.3.2  regexp_replace --替换其它字符为’’

    SELECT 

    l.id,

    l.name

    FROM   xb_link l

    WHERE  regexp_replace(TRIM(l.name),

                          '([A-Za-z0-9]|[[:punct:]]|[[:space:]])',

                          '') IS NOT NULL;

    wps3D71.tmp 

     

     

    一.3.3  to_multi_byte 函数--全是全角字符

    ---全是汉字

    SELECT l.name,

           to_multi_byte(l.name)

    FROM   xb_link l

    WHERE  l.name = to_multi_byte(l.name);

     

    wps3D82.tmp 

     

    SELECT l.name,

           to_multi_byte(l.name)

    FROM   xb_link l;

    wps3D83.tmp 

     

     

     

    一.3.4  网友问题解答:某个字段数据是不是包含了全角字符?

    drop table aa;

    create table aa (col   varchar2(255));

    SELECT * FROM aa for update;

    wps3D84.tmp 

     

    SELECT a.col,

           to_single_byte(a.col),

           length(a.col),

           lengthb(a.col),

           length(to_single_byte(a.col)),

           lengthb(to_single_byte(a.col))

    FROM   aa a

    WHERE  (lengthb(a.col) - length(a.col)) <>

           (lengthb(to_single_byte(a.col)) - length(to_single_byte(a.col)));

    wps3D85.tmp 

    一.4  总结

     

    方法很简单,网友可能还有其他的办法,欢迎留言,对于不同的场景处理方式有很多种,我们应该学会灵活变通。

     

     

     

    一.5  About Me

     

    ...........................................................................................................................................................................................

    本文作者:小麦苗,只专注于数据库的技术,更注重技术的运用

    ITPUB BLOG:http://blog.itpub.net/26736162

    本文地址:http://blog.itpub.net/26736162/viewspace-1688209/

    本文pdf版:http://yunpan.cn/QCwUAI9bn7g7w  提取码:af2d

    QQ:642808185 若加QQ请注明你所正在读的文章标题

    创作时间地点:2015-06-05 10:00~ 2015-06-05 13:00 于外汇交易中心

    <版权所有,文章允许转载,但须以链接方式注明源地址,否则追究法律责任!>

    ...........................................................................................................................................................................................

     

     

    posted @ 2015-06-05 14:04 ^_^小麦苗^_^ 阅读(...) 评论(...) 编辑 收藏
    展开全文
  • oracle中如何判断一个字符串是否含有汉字 1.1 BLOG文档结构图 1.2 前言部分 1.2.1 导读 各位技术爱好者,看完本文后,你可以掌握如下技能,也可以学到一些其它你所不知道...

    oracle中如何判断一个字符串是否含有汉字

    1.1  BLOG文档结构图

     

    wps3D5D.tmp 

     

    1.2  前言部分

     

    1.2.1  导读

    各位技术爱好者,看完本文后,你可以掌握如下的技能,也可以学到一些其它你所不知道的知识,~O(∩_∩)O~:

    全角字符的判断,或者是含有汉字的字符串的判断

     

     

     

    本文如有错误或不完善的地方请大家多多指正,ITPUB留言或QQ皆可,您的批评指正是我写作的最大动力。

    1.2.2  实验环境介绍

     

    11.2.0.3  RHEL6.5

     

     

     

    1.2.3  本文简介

     

     

    看到网友问,怎么查询表中某个字段数据是不是包含了全角字符啊? 这个问题涉及到几个个函数:to_single_bytelengthlengthb,我之前做开发的时候研究的是如何判断一个字符串中是否包含中文,其实和这个本质是一样的,且看实验部分。

     

    wps3D5E.tmp 

     

     

     

    1.3  实验部分

     

    1.3.1  lengthblength函数结合to_single_byte函数

    ---含有汉字,严格的说是含有全角字符

    SELECT l.name,

           length(l.name),

           lengthb(l.name)

    FROM   xb_link l

    WHERE  length(l.name) != lengthb(l.name)

    AND    length(l.name)  20;

    wps3D6E.tmp 

     

     

    以下数据也满足条件:

     SELECT l.id,

            l.name

     FROM   xb_link l

     WHERE  length(l.name) != lengthb(l.name)

     AND    l.metacategory IN

            ('com.gxlu.ngrm.network.DDNCircuit',

              'com.gxlu.ngrm.network.FRCircuit',

              'com.gxlu.ngrm.network.ATMCircuit',

              'com.gxlu.ngrm.network.DDNOCircuit',

              'com.gxlu.ngrm.network.FROCircuit')

     AND    l.id IN ('301898331', '301898335', '301908187', '301929403');

     

    wps3D6F.tmp 

    所以可以借助to_single_byte函数来解决。

     

     SELECT l.id,

            l.name,

            to_single_byte(l.name),

            length(l.name) l1,

            lengthb(l.name) l2,

            length(to_single_byte(l.name)) l

     FROM   xb_link l

     WHERE  length(l.name) != lengthb(l.name)

     AND    l.metacategory IN

            ('com.gxlu.ngrm.network.DDNCircuit',

              'com.gxlu.ngrm.network.FRCircuit',

              'com.gxlu.ngrm.network.ATMCircuit',

              'com.gxlu.ngrm.network.DDNOCircuit',

              'com.gxlu.ngrm.network.FROCircuit')

     AND    l.id IN ('301898331', '301898335', '301908187', '301929403');

    wps3D70.tmp 

     

    1.3.2  regexp_replace --替换其它字符为’’

    SELECT 

    l.id,

    l.name

    FROM   xb_link l

    WHERE  regexp_replace(TRIM(l.name),

                          '([A-Za-z0-9]|[[:punct:]]|[[:space:]])',

                          '') IS NOT NULL;

    wps3D71.tmp 

     

     

    1.3.3  to_multi_byte 函数--全是全角字符

    ---全是汉字

    SELECT l.name,

           to_multi_byte(l.name)

    FROM   xb_link l

    WHERE  l.name = to_multi_byte(l.name);

     

    wps3D82.tmp 

     

    SELECT l.name,

           to_multi_byte(l.name)

    FROM   xb_link l;

    wps3D83.tmp 

     

     

     

    1.3.4  网友问题解答:某个字段数据是不是包含了全角字符?

    drop table aa;

    create table aa (col   varchar2(255));

    SELECT * FROM aa for update;

    wps3D84.tmp 

     

    SELECT a.col,

           to_single_byte(a.col),

           length(a.col),

           lengthb(a.col),

           length(to_single_byte(a.col)),

           lengthb(to_single_byte(a.col))

    FROM   aa a

    WHERE  (lengthb(a.col) - length(a.col))    (lengthb(to_single_byte(a.col)) - length(to_single_byte(a.col)));

    wps3D85.tmp 

    1.4  总结

     

    方法很简单,网友可能还有其他的办法,欢迎留言,对于不同的场景处理方式有很多种,我们应该学会灵活变通。

     






     

    可以利用LENGTHLENGTHBTO_SINGLE_BYTE函数来解决这个问题。其中,LENGTH返回以字符为单位的长度,LENGTHB返回以字节为单位的长度TO_SINGLE_BYTE将字符串中的多字节字符转化为单字节字符。此外,还可以使用ASCIISTRCONVERT函数找出包含汉字的字符串。利用LENGTHLENGTHBTO_SINGLE_BYTE函数来实现该需求,则类似的WHERE条件为:“LENGTHB(COL) LENGTH(COL) AND LENGTHB(TO_SINGLE_BYTE(COL)) LENGTH(TO_SINGLE_BYTE(COL))”。

    下面给出一个示例,在AA表中插入的“2”是全角字符。

    SYS@lhrdb> CREATE TABLE AA (COL   VARCHAR2(255));

    Table created.

    SYS@lhrdb> INSERT INTO AA (COL) VALUES ('1');

    1 row created.

    SYS@lhrdb> INSERT INTO AA (COL) VALUES (''); --全角字符

    1 row created.

    SYS@lhrdb> INSERT INTO AA (COL) VALUES ('小麦苗');

    1 row created.

    SYS@lhrdb> COMMIT;

    Commit complete.

    SYS@lhrdb> SELECT * FROM AA;   

    COL

    ----------

    1

    小麦苗

    SYS@lhrdb> COL COL FORMAT A10

    SYS@lhrdb> COL SINGLE_COL  FORMAT A10

    SYS@lhrdb> SELECT A.COL COL,

      2         TO_SINGLE_BYTE(A.COL) SINGLE_COL,

      3         LENGTH(A.COL) LENGTH_COL,

      4         LENGTHB(A.COL) LENGTHB_COL,

      5         LENGTH(TO_SINGLE_BYTE(A.COL)) SINGLE_LENGTH_COL,

      6         LENGTHB(TO_SINGLE_BYTE(A.COL))  SINGLE_LENGTHB_COL

      7    FROM AA A

      8   WHERE LENGTHB(A.COL) LENGTH(A.COL)

      9     AND LENGTHB(TO_SINGLE_BYTE(A.COL)) LENGTH(TO_SINGLE_BYTE(A.COL));

    COL        SINGLE_COL LENGTH_COL LENGTHB_COL SINGLE_LENGTH_COL SINGLE_LENGTHB_COL

    ---------- ---------- ---------- ----------- ----------------- ------------------

    小麦苗     小麦苗              3           6                 3                  6





    ORACLE判别字段是否包含中文

    2015-01-19 14:26 by 潇湘隐者, 8553 阅读, 0 评论, 收藏编辑

       

     

     

     
    
    
         
        
        
        
    
    
     
              
              
              
              
              
              
      

    clip_image001

      

     
      
     
     
     
     
     
    
    
    
    

     

       

        

        

     
     
                         
                  
     
     
     
                                   
    
    
                                    
                   

    clip_image002

     

      

       

    clip_image003

     

     













        

    img_e3029f287d989cd04bd75432ecc1c172.png
    DBA笔试面试讲解
    欢迎与我联系

    展开全文
  • 如何在信息技术条件下进行汉字的记忆训练? 信息技术条件下汉字的学习效率能否有新的突破? 在不断发展的信息技术条件下如何实现辅助汉字学习提高学习效率的软件功能? 希望能有更多的同仁参与我的研究,为中国汉字...
  • C语言如何编程判断用户输入汉字且人名是合法? 代码: # include # define N 8 # define At(t) (t'z')&&(t'Z')&&t!='-' int Getchar(char *A,int min,int max);//长度在[min,max] 之间时 函数结束 返回字符...

    C语言如何编程判断用户输入的是汉字且人名是合法的?


    代码:

    # include <stdio.h>
    # define N 8
    # define At(t)  (t<'a'||t>'z')&&(t<'A'||t>'Z')&&t!='-'
    int Getchar(char *A,int min,int max);//长度在[min,max]  <闭区间>  之间时 函数结束 返回字符串A的长度 
    int BF(char a[],char b[]);
    int check(char S[]);
    int ISxing(char A[],int B);//如果姓氏查到返回 1 
    char name[]={"赵 钱 孙 李 周 吴 郑 王 冯 陈 楮 卫 蒋 沈 韩 杨 朱 秦 尤 许 何 吕 施 张 孔 曹 严 华 金 魏 陶 姜 戚 谢 邹 喻 柏 水 窦 章 云 苏 潘 葛 奚 范 彭 郎 鲁 韦 昌 马 苗 凤 花 方 俞 任 袁 柳 酆 鲍 史 唐 费 廉 岑 薛 雷 贺 倪 汤 滕 殷 罗 毕 郝 邬 安 常 乐 于 时 傅 皮 卞 齐 康 伍 余 元 卜 顾 孟 平 黄 和 穆 萧 尹 姚 邵 湛 汪 祁 毛 禹 狄 米 贝 明 臧 计 伏 成 戴 谈 宋 茅 庞 熊 纪 舒 屈 项 祝 董 梁 杜 阮 蓝 闽 席 季 麻 强 贾 路 娄 危 江 童 颜 郭 梅 盛 林 刁 锺 徐 丘 骆 高 夏 蔡 田 樊 胡 凌 霍 虞 万 支 柯 昝 管 卢 莫 经 房 裘 缪 干 解 应 宗 丁 宣 贲 邓 郁 单 杭 洪 包 诸 左 石 崔 吉 钮 龚 程 嵇 邢 滑 裴 陆 荣 翁 荀 羊 於 惠 甄 麹 家 封 芮 羿 储 靳 汲 邴 糜 松 井 段 富 巫 乌 焦 巴 弓 牧 隗 山 谷 车 侯 宓 蓬 全 郗 班 仰 秋 仲 伊 宫 宁 仇 栾 暴 甘 斜 厉 戎 祖 武 符 刘 景 詹 束 龙 叶 幸 司 韶 郜 黎 蓟 薄 印 宿 白 怀 蒲 邰 从 鄂 索 咸 籍 赖 卓 蔺 屠 蒙 池 乔 阴 郁 胥 能 苍 双 闻 莘 党 翟 谭 贡 劳 逄 姬 申 扶 堵 冉 宰 郦 雍 郤 璩 桑 桂 濮 牛 寿 通 边 扈 燕 冀 郏 浦 尚 农 温 别 庄 晏 柴 瞿 阎 充 慕 连 茹 习 宦 艾 鱼 容 向 古 易 慎 戈 廖 庾 终 暨 居 衡 步 都 耿 满 弘 匡 国 文 寇 广 禄 阙 东 欧 殳 沃 利 蔚 越 夔 隆 师 巩 厍 聂 晁 勾 敖 融 冷 訾 辛 阚 那 简 饶 空 曾 毋 沙 乜 养 鞠 须 丰 巢 关 蒯 相 查 后 荆 红 游 竺 权 逑 盖 益 桓 公 万俟 司马 上官 欧阳 夏侯 诸葛 闻人 东方 赫连 皇甫 尉迟 公羊 澹台 公冶 宗政 濮阳 淳于 单于 太叔 申屠 公孙 仲孙 轩辕 令狐 锺离 宇文 长孙 慕容 鲜于 闾丘 司徒 司空 丌官 司寇 仉 督 子车 颛孙 端木 巫马 公西 漆雕 乐正 壤驷 公良 拓拔 夹谷 宰父 谷梁 晋 楚 阎 法 汝 鄢 涂 钦 段干 百里 东郭 南门 呼延 归 海 羊舌 微生 岳 帅 缑 亢 况 后 有 琴 梁丘 左丘 东门 西门 商 牟 佘 佴 伯 赏 南宫 墨 哈 谯 笪 年 爱 阳 佟 第五 言 福 "};
    char key[21][3]={"!","。","?","《","》",",","{","}","(",")","¥","‘","’",":",";","—","”","“","、","·","~"}; 
    int main(){
    	char XM[N+1];
    	int F; 
    	while(1){
    	  do{
    		printf("输入姓名<字节[4,%d]>:",N);
    		Getchar(XM,4,N);
    	   }while(check(XM));
           F=ISxing(XM,2)+ISxing(XM,4);
           if(!F)
              printf("姓氏有误!请重新");
           else printf("姓名:%s\n",XM);
       }
    	return 0;
    }
    int ISxing(char A[],int B)//如果姓氏查到返回 1 
    {
    	char X=A[B],F=0;
    	A[B]=0;
    	if(BF(name,A))
    	   F=1;
    	A[B]=X;
    	return F;
    }
    int check(char S[])
    {
    	int i=-1,j=0;
    	while(S[++i])
    	  if(S[i]>0)
    	     return printf("输入的不是中文字符!请重新");
         while(j<21)
            if(BF(S,key[j++]))
               return printf("输入的不是汉字!请重新");
        return 0; 
    }
    int Getchar(char *A,int min,int max)//长度在[min,max]  <闭区间>  之间时 函数结束 返回字符串A的长度          
    {          
        int B,C;        
     do{          
            A[max]=B=C=0;      
            while((A[B++]=getchar())!='\n'&&B<max);      
            if(A[B-1]!='\n')while(getchar()!='\n'&&++C);          
            else A[--B]=0;       
        if(C||B&&B<min)      
           printf("您录入的字符串长度:%d字节\n只录入(%d--%d)个字节!\n",B+C,min,max);          
        }while(C||B<min);          
        return B;        
    }  
    int BF(char a[],char b[])//检查B在A里是否完整 
    {//匹配 a中b单词完整的个数 
        int i=0,j=0,k=0;  
        do{  
            if (b[j]&&(a[i++]==b[j]))++j;//若a[i]与b[j]相等或是大小写关系 继续比较  
            else{  
               if(!b[j])  
               {//如果b字符串到头了 此时需要检查 b在a字符串是否为完整的单词  
                if((i!=j)&&At(a[i-j-1])||(i==j))//i==j时 b在a字符串的串首  如果不在串首,则i!=j 此时应检查a[i-j-1]  
                if(!a[i]||a[i]&&At(a[i]))//如果b在a字符串的串尾 则a[i]==0  如果不在末尾 则检验 a[i]  
                  return 1;//存储当前匹配的单词位置  
               }  
               else i-=j;  
                j=0;  
            }  
        }while(a[i-1]);  
           return 0;  
    }  

    怎样保证用户输入的日期必须是正确的?


    # include <stdio.h>  
    int gainint(int *p,int a,int b);  
    int GetDate(int a[]); 
    int main(){   
       int a[3],F;  
       F=GetDate(a);
       printf("%04d年%02d月%02d日\n%d",a[0],a[1],a[2],F);  
       return 0;  
    }  
    int GetDate(int a[]){
        char month[]={31,28,31,30,31,30,31,31,30,31,30,31};  
        int charge[6]={1,1,1,2016,12,0},i;
    	char zifu[][5]={"年份","月份","天数"};   
        for(i=0;i<3;i++)  
        {  
         printf("请输入%s(%d--%d):\n",zifu[i],charge[i],charge[i+3]);  
         gainint(&a[i],charge[i],charge[i+3]);  
         if(i==0)  month[1]=!(a[0]%4&&a[0]%100)||!(a[0]%400)?29:28;  
         if(i==1)  charge[5]=month[a[1]-1];  
        }  
      return a[0]*10000+a[1]*100+a[2];//把年月日拼成一个数字 
    }
    int gainint(int *p,int a,int b)//a为最小值b为最大值  
    {   
    do{  
    scanf("%d",p);  
    while(getchar()!='\n');    
    if(*p>b||*p<a)  
    printf("输入有误,请重新输入(%d--%d):",a,b);  
    }while(*p>b||*p<a);  
    
    return *p;
    }  

    展开全文
  • 如何判断一个对象是否是垃圾对象 垃圾回收算法 Minor GC和Full GC 垃圾收集器 集合继承体系 Collection 和 Collections区别。 如何通过jdbc访问数据库 JDBC处理事务采用什么方法 Statement和PreparedStatement...
  • 如何在程序判断出debug状态◆ 27 ◆当对话框中有一个滚动条是,当滚动滚动条时消息控制函数◆ 27 ◆将一个CWnd指针转换成一个控件ID(整数)注意用GetDlgItem()函数是从一个控件ID转换成一个CWnd指针◆ 28 ◆...
  •  符进行逐个判断,此处需要对字符编码有必要了解,比如汉字是由大于A0H 编码组成, 30H-39H为数字编码等;在实时调整屏幕背景颜色及其它字符颜色时, 为了使程序具有良好通用性, 使用了各种显示系统通用调色板...
  • java基础笔记

    2021-04-16 16:02:15
    OOM 后,其他线程是否能正常运行常见语法糖判断汉字sleep 和 waitjmap -histo pid 查看堆内存里实例数、占用内存大小和对应classPriorityQueue 可用作堆结构,默认为小顶堆LinkedHashMap 实现 LRU 缓存线程池...

    文章目录


    ThreadLocal 在并行流中会丢失上下文

    class ParallelProcessor<T> {
    
        public void process(List<T> dataList) {
            // 先校验参数,篇幅限制先省略不写
            dataList.parallelStream().forEach(entry -> {
                doIt();
            });
        }
    
        private void doIt() {
            String session = ContextHolder.get();
            // do something
        }
    }
    

    并行流的实现是 forkJoin 线程池,所以这时 get 会返回 null。

    class ParallelProcessor<T> {
    
        private String session;
    
        public ParallelProcessor(String session) {
            this.session = session;
        }
    
        public void process(List<T> dataList) {
            // 先校验参数,篇幅限制先省略不写
            dataList.parallelStream().forEach(entry -> {
                try {
                    ContextHolder.set(session);
                    // 业务处理
                    doIt();
                } catch (Exception e) {
                    // log it
                } finally {
                    ContextHolder.remove();
                }
            });
        }
    
        private void doIt() {
            String session = ContextHolder.get();
            // do something
        }
    }
    

    改成这样时也可能有问题,因为主线程也可能会参加调度,这样主线程的上下文就会被清除。

    延迟任务和时间轮算法

    Timer、DelayQueue 和 ScheduledThreadPool,它们都是基于优先队列实现的,O(logn) 的时间复杂度在任务数多的情况下频繁的入队出队对性能来说有损耗。因此适合于任务数不多的情况。

    Timer 是单线程的会有阻塞的风险,并且对异常没有做处理,一个任务出错 Timer 就挂了。而 ScheduledThreadPool 相比于 Timer 首先可以多线程来执行任务,并且线程池对异常做了处理,使得任务之间不会有影响。

    并且 Timer 和 ScheduledThreadPool 可以周期性执行任务。而 DelayQueue 就是个具有优先级的阻塞队列。

    对比而言时间轮更适合任务数很大的延时场景,它的任务插入和删除时间复杂度都为O(1)。对于延迟超过时间轮所能表示的范围有两种处理方式,一是通过增加一个轮数,Netty 就是这样实现的。二是多层次时间轮,Kakfa 是这样实现的。

    相比而言 Netty 的实现会有空推进的问题,而 Kafka 采用 DelayQueue 以槽为单位,利用空间换时间的思想解决了空推进的问题。

    可以看出延迟任务的实现都不是很精确的,并且或多或少都会有阻塞的情况,即使你异步执行,线程不够的情况下还是会阻塞。

    JVM 堆 OOM 后,其他线程是否能正常运行

    可以, oom 的线程会被结束掉,内存资源会释放。

    常见语法糖

    泛型(擦除)、枚举(转成静态常量)、内部类(持有外部类引用)、自动装箱拆箱、变长参数(传递数组)、增强for循环(退化为普通for循环,或者迭代器)、switch 支持字符串和枚举(转成 hashcode 和 equals 比较)、try-with-resource(要求变量实现AutoClosable接口,自动在finally中close)、字符串相加(能确定的直接转为相加结果,否则转为 StringBuilder 的 append 再 toString )

    判断汉字

    unicode范围:[\u4e00-\u9fa5]

    sleep 和 wait

    在这里插入图片描述

    jmap -histo pid 查看堆内存里实例数、占用内存大小和对应class

    PriorityQueue 可用作堆结构,默认为小顶堆

    LinkedHashMap 实现的 LRU 缓存

    LinkedHashMap<String,String> lrumap=new LinkedHashMap<String,String>(5,0.75f,true){
                @Override
                protected boolean removeEldestEntry(Map.Entry<String, String> eldest) {
                    return this.size()>5;
                }
            };
    

    构造函数参数为 initialCapacity,loadFactor,accessOrder。当设为 true 时,对 map 的get 操作会触发元素移动到链表最后,从而实现 LRU 。

    线程池自定义异常处理方法

    对于 submit() 形式提交的任务,会包装成futureTask,run() 方法里会吞掉异常,只有在调用 get() 时才会抛出

    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        // 被包装成 RunnableFuture 对象,然后准备添加到工作队列
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }
    
    • 在不需要返回结果时可以用 execute() 方法
    • 可以在定义 ThreadFactory 的时候调用setUncaughtExceptionHandler方法,自定义异常处理方法。例如:
    ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
                    .setNameFormat("judge-pool-%d")
                    .setUncaughtExceptionHandler((thread, throwable)-> logger.error("ThreadPool {} got exception", thread,throwable))
                    .build();
    

    这样,对于线程池中每条线程抛出的异常都会打下 error 日志,就不会看不到了。

    对象会进入到老年代中的几种情况

    • YGC时,To Survivor区不足以存放存活的对象,对象会直接进入到老年代。
    • 经过多次YGC后,如果存活对象的年龄达到了设定阈值,则会晋升到老年代中。
    • 动态年龄判定规则,To Survivor区中相同年龄的对象,如果其大小之和占到了 To Survivor区一半以上的空间,那么大于此年龄的对象会直接进入老年代,而不需要达到默认的分代年龄。
    • 大对象:由-XX:PretenureSizeThreshold启动参数控制,若对象大小大于此值,就会绕过新生代, 直接在老年代中分配。

    枚举中变量编译后用内部类实现

    字符串常量池

    • 字符串常量池本质就是一个哈希表
    • 字符串常量池中存储的是字符串实例的引用
    • 字符串常量池在被整个 JVM 共享
    • 在解析运行时常量池中的符号引用时,会去查询字符串常量池,确保运行时常量池中解析后的直接引用跟字符串常量池中的引用是一致的

    如何判断一个类为无用的类

    • 该类的所有实例被回收
    • 该类的 ClassLoader 被回收
    • 对应的 Class 对象没有被引用,也无法在任何地方通过反射访问类的方法

    类加载过程

    1. 加载。完成三件事:通过全类名获取二进制字节流、将字节流代表的静态数据结构转换为方法区的运行时数据结构、在堆中生成Class对象。
    2. 连接,又细分为 验证、准备、解析。
    • 验证就是确保文件合法性。
    • 准备阶段在方法区给类变量分配内存并设置初始值,final变量直接设置成代码中的值,其他类变量置零(对象类型为null)。
    • 解析是将符号引用转换成直接引用。符号引用包括:接口、类、字段、类方法、接口方法、方法类型、方法句柄和调用限定符。也就是通过方法名得到方法在内存中的指针的过程。
    1. 初始化,即执行 clinit 方法 (静态代码块)。JVM规范没有约束类加载的时机,但是以下几种情况必须初始化类,加载将在初始化之前执行。
    • new指令
    • Class.forName
    • 访问非final 的静态变量或者对其赋值
    • 访问静态方法
    • 触发子类初始化会先对父类初始化
    • 主类在启动时初始化

    类加载器和双亲委派

    • BootstrapClassLoader 顶级加载类,由C++实现,加载 %javahome/lib 下的jar包
    • ExtensionClassLoader 扩展类加载器,由java实现,加载 %javahome/lib/ext 下的jar包
    • AppClassLoader,由java实现,加载当前应用 classpath 下的jar 包

    加载一个类时,先看是否加载过,如果没有,会先委派给父加载器,因此所有请求都会到 BootstrapClassLoader,如果父加载器不能处理,再由子加载器处理。这里的父子并不代表继承,而是优先度。

    委派机制的好处是避免类重复加载。

    BIO、NIO

    传统Java IO都是同步阻塞,即 BIO。 Socket.read() 和 Socket.write() 方法都是阻塞的。
    NIO中客户端和服务端对应的套接字实现是 SocketChannel 和 ServerSocketChannel 。
    NIO 中核心api 是 Channel 、Selector 、Buffer

    重写equals 方法时,必须重写hashcode方法

    首先,有以下一些规则:

    1. 如果两个对象相等,则 hashcode 一定也是相同的
    2. 两个对象相等,对两个对象分别调用 equals 方法都返回 true
    3. 两个对象有相同的 hashcode 值,它们也不一定是相等的(哈希碰撞)
    4. hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则默认是对两个对象的内存地址做哈希算法,所以即使两个对象equal,他们的hashcode也不相同,这就违背了上面的规则。
    5. 因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖,而且两个方法判定的属性要相同。

    正确的 equals 姿势

    反例:

    String a=null;
    a.equals("123");  // npe
    

    正例:

    String a=null;
    "123".equals(a);  // false
    Objects.equals("123",a);  // false
    

    BigDecimal 的正确构造

    应该用 new BigDecimal (String) 防止精度丢失。
    对比:

    Double a = 0.1;
    String b = "0.1";
    BigDecimal x = new BigDecimal(a);
    BigDecimal y = new BigDecimal(b);
    System.out.println(x);  //0.1000000000000000055511151231257827021181583404541015625
    System.out.println(y);  //0.1
    System.out.println(x.equals(y));  // false
    

    基本数据类型和包装数据类型的使用标准

    • POJO 类属性必须用包装数据类型
    • RPC 方法的返回值和参数必须用包装数据类型
    • 局部变量建议用基本数据类型

    POJO类属性用包装数据类型,是为了提醒要赋初值;同时数据库查询时,如果列的值为null,用基本数据类型接收有可能会空指针;并且 null 和 0 在特定场景下的含义不同。

    RPC方法用包装类型接收返回值是为了判别异常情况,如调用失败,会返回null。

    Arrays.asList 踩坑记录

    场景:

    List<Integer> myList = Arrays.asList(1, 2, 3);
    myList.add(4);//运行时报错:UnsupportedOperationException
    

    在这里插入图片描述
    原因:此处返回的是 Arrays 类中的静态内部类 ArrayList,这个类中并没有重写add、remove等方法,调用的是父类 AbstractList 中的对应方法。
    在这里插入图片描述

    正例:

    //方法1
    List<Integer> myList = new ArrayList<>(Arrays.asList(1, 2, 3));
    //方法2
    Arrays.stream(new int[]{1,2,3}).boxed().collect(Collectors.toList());
    

    非静态代码块(构造代码块)的作用

    构造代码块是用来给所有实例的共性部分做初始化的,而构造函数是给特定类型的实例做初始化,因为构造函数可以重载,可以以不同的方式初始化。

    RandomAccess 接口 和 List 循环

    这个接口是一个标识,实现这个接口的类具有随机访问功能。

    binarySearch()方法中,它要判断传入的list 是否 RamdomAccess 的实例,如果是,调用indexedBinarySearch()方法,如果不是,那么调用iteratorBinarySearch()方法

        public static <T>
        int binarySearch(List<? extends Comparable<? super T>> list, T key) {
            if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
                return Collections.indexedBinarySearch(list, key);
            else
                return Collections.iteratorBinarySearch(list, key);
        }
    

    下面再总结一下 list 的遍历方式选择:

    • 实现了 RandomAccess 接口的list,优先选择普通 for 循环 ,其次 foreach,因为for循环使用下标访问, foreach 使用 iterator 。
    • 未实现 RandomAccess接口的list,优先选择 iterator 遍历,大size的数据,千万不要使用普通for循环,因为每次访问都要从头开始遍历到对应的位置。

    Java 对象不使用时,需要赋值为 null 吗

    代码离开变量作用域时,栈里还存在对堆中的对象的引用。除非之后设为 null 或者声明新变量来复用失效的变量的 slot

    public static void main(String[] args) {
            if (true) {
            byte[] placeHolder = new byte[64 * 1024 * 1024];
            System.out.println(placeHolder.length / 1024);
            }
            int replacer = 1;
            System.gc();
            }
    

    在这里插入图片描述

    线程池最佳实践

    • 使用 ThreadPoolExecutor 的构造函数声明线程池
    • 不同业务用不同线程池
    • 线程池命名
    • 合理设置线程数量

    9. 大量 if else 的优化

    • 用枚举方法
    • 工厂模式,工厂类里用map存放所有的实现
    • 策略模式,同样需要一个顶级类,实现类作为一个属性

    22. ConcurrentHashMap 在 JDK7 和 JDK8

    JDK 7:

    如何在保证高并发下线程安全的同时实现了性能提升?

    ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术。它使用了多个锁来控制对hash表的不同部分进行的修改。内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的HashTable,只要多个修改操作发生在不同的段上,它们就可以并发进行。可以理解为数据库的水平分库分表。

    在高并发下的情况下如何保证取得的元素是最新的?

    用于存储键值对数据的HashEntry 是volatile类型的。

    ConcurrentHashMap的弱一致性体现在迭代器,clear和get方法,原因在于没有加锁。

    JDK8:
    在这里插入图片描述
    JDK8相比与JDK7主要区别如下:

    • 取消了segment数组,直接用table保存数据,锁的粒度更小,减少并发冲突的概率。采用table数组元素作为锁,从而实现了对每一行数据进行加锁,进一步减少并发冲突的概率,并发控制使用Synchronized和CAS来操作。
    • 存储数据时采用了数组+ 链表+红黑树的形式。

    23. ArrayList 扩容机制

    默认扩一半,如果使用addAll() 方法,则取 oldCapacity + oldCapacity >>> 1 和 addAll 后的size 的最大值。

    24. Comparator 升序降序的记法

    Collections.sort(list, new Comparator<T>() {
                @Override
                public int compare(T o1, T o2) {
                    return o1.compareTo(o2); // 升序
                    return o2.compareTo(o1); // 降序
                }
            });
    

    如果o1<o2, o1.compareTo(o2) 返回-1, compare返回-1表示不需要交换顺序,则正好是升序
    如果o2<o1, o2.compareTo(o1) 返回-1,也不交换顺序,正好表示降序

    25. JDK8 JVM 内存空间变化

    JDK8之前,HotSpot的永久代(即方法区)在堆里。 JDK8中移除永久代,变成 元空间,位于直接内存中,好处是元空间内存溢出概率更小,只受物理内存限制。

    从JDK7 起,常量池从方法区中移出,在堆里单独开一片空间,包括字面量(字符串、基本类型、final变量等)和符号引用(类的全限定名、字段和方法的名称和描述符)

    26. HotSpot 对象创建过程

    1. 遇到new 指令时,判断常量池中是否有这个类的符号引用,以及该符号引用指向的类是否被加载、解析、初始化过,如果没有,则执行类加载过程
    2. 类加载完之后,就可知道对象需要的内存大小,在内存中划分一块空间,分配方式有两种:对于内存空间规整的(使用标记整理回收算法),使用碰撞指针,指针一侧是被使用的内存,另一侧是空闲,只要移动指针就可以。 对于内存空间零碎的(使用标记清除回收算法),使用空闲列表,虚拟机维护一个列表,记录哪些内存块可用,找一块够大的分配。
    • 补充:内存分配并发问题:通常采用两种方式解决。
    • CAS+失败重试
    • TLAB,每个线程在EDEN区中提前预留一块区域用于分配,不够再用CAS
    1. 初始化零值,将分配到的内存空间置零(不包括对象头)。保证实例的字段不赋初始值也能访问。
    2. 设置对象头,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的 GC 分代年龄等信息。 这些信息存放在对象头中。 另外,根据虚拟机当前运行状态的不同,如是否启用偏向锁等,对象头会有不同的设置方式。
    3. 执行init 方法,完成程序中对实例的初始化操作。

    27. HotSpot 对象内存布局

    Hotspot 虚拟机的对象头包括两部分信息,第一部分用于存储对象自身的运行时数据(哈希码、GC 分代年龄、锁状态标志等等),另一部分是类型指针,即对象指向它的类元数据的指针。

    实例数据部分是对象真正存储的有效信息,也是在程序中所定义的各种类型的字段内容。

    对齐填充部分不是必然存在的,仅仅起占位作用。 因为 Hotspot 虚拟机的自动内存管理系统要求对象起始地址必须是 8 字节的整数倍,而对象头部分正好是 8 字节的倍数(1 倍或 2 倍),因此,当对象实例数据部分没有对齐时,就需要通过对齐填充来补全。

    28. HotSpot 对象的访问定位

    1. 句柄:好处是有稳定的句柄地址
      在这里插入图片描述
    2. 直接指针:好处是访问快,少一次指针定位
      在这里插入图片描述

    JVM 可达性分析中可当做 GC Root 的对象

    • 虚拟机栈(栈帧中的本地变量表)中引用的对象。
    • 方法区中类静态属性引用的对象。
    • 方法区中常量引用的对象。
    • 本地方法栈中JNI(即一般说的 Native方法)引用的对象。

    JVM 几种垃圾收集器

    在这里插入图片描述
    在这里插入图片描述
    serial直到jdk7都是client模式下的默认收集器,在单cpu的client模式下适用。

    在这里插入图片描述
    parnew是serial的多线程版本,是许多server模式虚拟机的新生代收集器,因为可以和CMS收集器搭配使用。

    在这里插入图片描述
    parallel scavenge 和 parnew 大致相同。更注重整体的吞吐量,适用于后台计算而交互不多的场景。

    parallel old采用多线程和标记——整理。

    CMS收集器
    基于“标记一清除”算法实现的,整个过程分为4个步骤,包括初始标记( CMS initial mark)、并发标记( CMS concurrent mark)、重新标记( CMS remark)、并发清除( CMS concurrent sweep其中,初始标记、重新标记这两个步骤仍然需要“ Stop The World”。初始标记仅仅只是标记一下 GC Roots能直接关联到的对象,速度很快,并发标记阶段就是进行 GC Root Tracing的过程,而重新标记阶段则是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。由于整个过程中耗时最长的并发标记和并发清除过程,收集器线程都可以与用户线程一起工作。所以,从总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。
    在这里插入图片描述
    有以下3个明显的缺点:

    • CMS收集器对CPU资源非常敏感。在并发阶段,因为占用了一部分线程导致应用程序变慢,总吞吐量会降低。CMS默认启动的回收线程数是(CPU数量+3)/4。
    • CMS收集器无法处理浮动垃圾( Floating Garbage),由于CMS并发清理阶段用户线程还在运行着,伴随程序运行自然就还会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS无法在当次收集中处理掉它们,只好留待下一次GC时再清理掉。这一部分垃圾就称为“浮动垃圾”。也是由于在垃圾收集阶段用户线程还需要运行,那也就还需要预留有足够的内存空间给用户线程使用,因此CMS收集器需要预留一部分空间提供并发收集时的程序运作使用。要是CMS运行期间预留的内存无法满足程序需要,就会出现一次“ Concurrent Mode Failure”失败,这时虚拟机将临时启用 Serial old收集器来重新进行老年代的垃圾收集,这样停顿时间就很长了。
    • 收集结束时会有大量空间碎片产生。空间碎片过多时,将会给大对象分配带来很大麻烦,往往会出现老年代还有很大空间剩余,但是无法找到足够大的连续空间来分配当前对象,不得不提前触发一次 Full gc。为了解决这个问题,CMS收集器提供了一个-XX:+ Use CMSCompactAtFullCollection开关参数(默认就是开启的),用于在CMS收集器Full GC时开启内存碎片的合并整理过程,内存整理的过程是无法并发的。

    G1收集器
    在这里插入图片描述
    G1具备如下特点:

    • 并行与并发:G1能使用多个CPU来缩短Stop-The- World停顿的时间,部分其他收集器原本需要停顿Java线程执行的GC动作,G1收集器仍然可以通过并发的方式让Java程序继续执行。分代收集:采用不同的方式去处理新创建的对象和已经存活了一段时间的旧对象以获取更好的收集效果。
    • 空间整合: G1从整体来看是基于“标记一整理算法实现的收集器,从局部(两个 Region之间)上来看是基于“复制”算法实现的,这两种算法都意味着G1运作期间不会产生内存空间碎片。
    • 可预测的停顿:这是G1相对于CMS的另一大优势,G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒

    使用G1收集器时,Java堆的内存布局就与其他收集器有很大差别,它将整个Java堆划分为多个大小相等的独立区域( Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,它们都是一部分 Region(不需要连续)的集合。Gl收集器之所以能建立可预测的停顿时间模型,是因为它可以有计划地避免在整个Java堆中进行全区域的垃圾收集。Gl跟踪各个 Region里面的垃圾的价值,在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的 Region,保证在有限的时间内可以获取尽可能高的收集效率。

    在G1收集器中, Region之间的对象引用以及其他收集器中的新生代与老年代之间的对象引用,虚拟机都是使用 Remembered set来避免全堆扫描的。G1中每个 Region都有一个与之对应的 Remembered Set,虚拟机发现程序在对 Reference类型的数据进行写操作时,会产生一个 Write barrier暂时中断写操作,检查 Reference引用的对象是否处于不同的 Region之中(在分代的例子中就是检查是否老年代中的对象引用了新生代中的对象),如果是,便通过 CardTable把相关引用信息记录到被引用对象所属的 Region的 Remembered Set之中。当进行内存回收时,在GC根节点的枚举范围中加入 Remembered Set即可保证不对全堆扫描也不会有遗漏。

    如果不计算维护 Remembered Set的操作,Gl收集器的运作大致可划分为以下几个步骤:
    初始标记( Initial Marking)
    并发标记( Concurrent Marking)
    最终标记( Final Marking)
    筛选回收( Live Data Counting and Evacuation)
    初始标记阶段仅仅只是标记一下 GC Roots能直接关联到的对象,这阶段需要停顿线程,但耗时很短。并发标记阶段是从 GC Root开始对堆中对象进行可达性分析,找出存活的对象,这阶段耗时较长,但可与用户程序并发执行。而最终标记阶段则是为了修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录,虚拟机将这段时间对象变化记录在线程 Remembered Set Logs里面,最终标记阶段需要把 Remembered Set Logs的数据合并到 Remembered set中,这阶段需要停顿线程,但是可并行执行。最后在筛选回收阶段首先对各个 Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划。

    理解GC日志阅读

    例如以下两段典型的GC日志:
    在这里插入图片描述
    最前面的数字“33.125:”和“100.667:”代表了GC发生的时间,这个数字的含义是从Java虚拟机启动以来经过的秒数。
    GC日志开头的“[GC”和“[Full GC”说明了这次垃圾收集的停顿类型。如果有“Full”,说明这次GC是发生了 Stop-The-World的。新生代收集器在出现了分配担保失败之类的问题,也会导致Full GC。如果是调用 System gc()方法所触发的收集,那么在这里将显示“[Full GC( System)”。
    “[ DefNew”、“[ Tenured”、“[Perm”表示GC发生的区域,这里显示的区域名称与使用的GC收集器是密切相关的,例如上面样例所使用的 Serial 收集器中的新生代名为“ Default New Generation”,所以显示的是“[ DefNew"。如果是 ParDew收集器,新生代名称就会变为“[ ParNew”,意为“ Parallel New Generation”。如果采用 Parallel Scavenge收集器,那它配套的新生代称为“ PSYoung Gen”,老年代和永久代同理。
    后面方括号内部的含义是GC前该内存区域已使用容量→>GC后该内存区域已使用容量(该内存区域总容量),该区域GC耗时”而在方括号之外的“3324K->152K(11904K)”表示GC前Java堆已使用容量→>GC后Java堆已使用容量(Java堆总容量)

    Java IO 分类

    在这里插入图片描述

    5个分支以上推荐用 switch 性能高于 if

    JMM(Java 内存模型)通过控制主内存与每个线程的本地内存之间的交互,来提供内存可见性保证

    Java中的volatile关键字可以保证多线程操作共享变量的可见性以及禁止指令重排序,synchronized关键字不仅保证可见性,同时也保证了原子性(互斥性)。在更底层,JMM通过内存屏障来实现内存的可见性以及禁止重排序。为了程序员的方便理解,提出了happens-before,它更加的简单易懂,从而避免了程序员为了理解内存可见性而去学习复杂的重排序规则以及这些规则的具体实现方法。

    JMM和Java运行时内存区域的划分的区别和联系

    • 区别
      两者是不同的概念层次。JMM是抽象的,他是用来描述一组规则,通过这个规则来控制各个变量的访问方式,围绕原子性、有序性、可见性等展开的。而Java运行时内存的划分是具体的,是JVM运行Java程序时,必要的内存划分。
    • 联系
      都存在私有数据区域和共享数据区域。一般来说,JMM中的主内存属于共享数据区域,他是包含了堆和方法区;同样,JMM中的本地内存属于私有数据区域,包含了程序计数器、本地方法栈、虚拟机栈。

    Java线程间的通信

    • 锁同步
    • 等待——通知 机制
    • 信号量
    • 管道 (使用场景:使用管道多半与I/O流相关。当我们一个线程需要先另一个线程发送一个信息(比如字符串)或者文件等等时,就需要使用管道通信了。)

    Java中 happen-before 规则

    • 程序顺序规则:一个线程中的每一个操作,happens-before于该线程中的任意后续操作。
    • 监视器锁规则:对一个锁的解锁,happens-before于随后对这个锁的加锁。
    • volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的读。
    • 传递性:如果A happens-before B,且B happens-before C,那么A happens-before C。
    • start规则:如果线程A执行操作ThreadB.start()启动线程B,那么A线程的ThreadB.start()操作- happens-before于线程B中的任意操作、
    • join规则:如果线程A执行操作ThreadB.join()并成功返回,那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回。

    内存屏障

    硬件层面,内存屏障分两种:读屏障(Load Barrier)和写屏障(Store Barrier)。
    内存屏障有两个作用:

    • 阻止屏障两侧的指令重排序;
    • 强制把写缓冲区/高速缓存中的脏数据等写回主内存,或者让缓存中相应的数据失效。这里的缓存主要指的是CPU缓存,如L1,L2等

    编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。编译器选择了一个比较保守的JMM内存屏障插入策略,这样可以保证在任何处理器平台,任何程序中都能得到正确的volatile内存语义。这个策略是:

    • 在每个volatile写操作前插入一个StoreStore屏障;

    • 在每个volatile写操作后插入一个StoreLoad屏障;

    • 在每个volatile读操作后插入一个LoadLoad屏障;

    • 在每个volatile读操作后再插入一个LoadStore屏障。

    • LoadLoad屏障:对于这样的语句Load1; LoadLoad; Load2,在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。

    • StoreStore屏障:对于这样的语句Store1; StoreStore; Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。

    • LoadStore屏障:对于这样的语句Load1; LoadStore; Store2,在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。

    • StoreLoad屏障:对于这样的语句Store1; StoreLoad; Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。它的开销是四种屏障中最大的(冲刷写缓冲器,清空无效化队列)。在大多数处理器的实现中,这个屏障是个万能屏障,兼具其它三种内存屏障的功能

    再介绍一下volatile与普通变量的重排序规则:

    • 如果第一个操作是volatile读,那无论第二个操作是什么,都不能重排序;
    • 如果第二个操作是volatile写,那无论第一个操作是什么,都不能重排序;
    • 如果第一个操作是volatile写,第二个操作是volatile读,那不能重排序。

    volatile 和锁的区别

    volatile可以保证内存可见性且禁止重排序。

    在保证内存可见性这一点上,volatile有着与锁相同的内存语义,所以可以作为一个“轻量级”的锁来使用。但由于volatile仅仅保证对单个volatile变量的读/写具有原子性,而锁可以保证整个临界区代码的执行具有原子性。所以在功能上,锁比volatile更强大;在性能上,volatile更有优势。

    AQS 是什么

    AQS是AbstractQueuedSynchronizer的简称,即抽象队列同步器,从字面意思上理解:

    抽象:抽象类,只实现一些主要逻辑,有些方法由子类实现;
    队列:使用先进先出(FIFO)队列存储数据;
    同步:实现了同步的功能。

    AQS是一个用来构建锁和同步器的框架,使用AQS能简单且高效地构造出应用广泛的同步器,比如我们提到的ReentrantLock,Semaphore,ReentrantReadWriteLock,SynchronousQueue,FutureTask等等皆是基于AQS的。

    展开全文
  • delphi 开发经验技巧宝典源码

    热门讨论 2010-08-12 16:47:23
    0150 如何获得汉字的区位码 100 0151 通过区位码获取汉字 100 0152 根据ASCII码获得字母 101 0153 获得字母的ASCII码 101 5.2 字符中的转换 101 0154 如何将数字转换成字符串 101 0155 如何将字符串...
  • 0150 如何获得汉字的区位码 100 0151 通过区位码获取汉字 100 0152 根据ASCII码获得字母 101 0153 获得字母的ASCII码 101 5.2 字符中的转换 101 0154 如何将数字转换成字符串 101 0155 如何将字符串...
  • 0150 如何获得汉字的区位码 100 0151 通过区位码获取汉字 100 0152 根据ASCII码获得字母 101 0153 获得字母的ASCII码 101 5.2 字符中的转换 101 0154 如何将数字转换成字符串 101 0155 如何将字符串...
  • 0150 如何获得汉字的区位码 100 0151 通过区位码获取汉字 100 0152 根据ASCII码获得字母 101 0153 获得字母的ASCII码 101 5.2 字符中的转换 101 0154 如何将数字转换成字符串 101 0155 如何将字符串...
  • 0150 如何获得汉字的区位码 100 0151 通过区位码获取汉字 100 0152 根据ASCII码获得字母 101 0153 获得字母的ASCII码 101 5.2 字符中的转换 101 0154 如何将数字转换成字符串 101 0155 如何将字符串...
  • 0150 如何获得汉字的区位码 100 0151 通过区位码获取汉字 100 0152 根据ASCII码获得字母 101 0153 获得字母的ASCII码 101 5.2 字符中的转换 101 0154 如何将数字转换成字符串 101 0155 如何将字符串...
  • 例如,可以使用[Tensorflow Lite](https://www.tensorflow.org/lite)在移动端进行部署,本文[随后](#use_tflite)针对这一点进行了介绍,包括如何把模型转换成Tensorflow Lite格式和对其进行性能测试等。 一键...
  • C#编程经验技巧宝典

    热门讨论 2008-06-01 08:59:33
    值 52 <br>0069 求最大公约数 52 <br>0070 求最小公倍数 53 <br>0071 判断素数算法 53 <br>0072 如何判断一个数是否是完数 54 <br>0073 歌德巴赫猜想算法 54 <br>0074 八皇后...
  • java面试题典 java 面试题 经典

    热门讨论 2010-06-18 13:42:36
    31. 如何判断哪个session正在连结以及它们等待资源? 34 32. 描述什么是 redo logs 34 33. 如何进行强制LOG SWITCH? 34 34. 举出两个判断DDL改动方法? 34 35. Coalescing做了什么? 34 36. TEMPORARY tablespace和...
  • 定义 数组是一种排列有序数据结构,包含于数组中变量被称为数组元素, 它们都有相同类型。 数组声明 int [] array1 = new int[5]; int [,,] array3 = new int[10,20,30]; int [] array1 = new int[] {1,2,4}...
  • VC++常用功能实例

    2010-01-25 23:28:46
    如何在程序判断出debug状态◆ 27 ◆当对话框中有一个滚动条是,当滚动滚动条时消息控制函数◆ 27 ◆将一个CWnd指针转换成一个控件ID(整数)注意用GetDlgItem()函数是从一个控件ID转换成一个CWnd指针◆ 28 ◆...
  • java面试题

    2018-01-01 15:35:15
    72.6. Struts体系结构组件 69 72.7. struts如何实现国际化 70 72.8. struts2.0常用标签 71 72.9. action是单实例还是多实例,为什么? 73 72.10. Strutsvalidate框架是如何验证? 74 72.11. ...
  • Java程序员面试宝典pdf

    热门讨论 2013-02-21 13:06:13
    面试题098 如何创建TCP通信服务器端多线程模型 163 面试题099 用TCP通信模型创建一个Web服务器 165 面试题100 用UDP通信模型创建一个即时聊天软件 167 面试题101 如何使用Java访问Web站点 169 10.3 小结 171 第...
  • 面试题098 如何创建TCP通信服务器端多线程模型 163 面试题099 用TCP通信模型创建一个Web服务器 165 面试题100 用UDP通信模型创建一个即时聊天软件 167 面试题101 如何使用Java访问Web站点 169 10.3 小结 171...
  • 实例123 汉字区位码与汉字的相互转化 实例124 数字与字符串的相互转换 5.2 字符串查找与判断比较 实例125 在字符串中查找指定字符 实例126 在字符串中提取部分字符 实例127 利用IsNumeric函数判断是否为数字 ...
  • 实例123 汉字区位码与汉字的相互转化 实例124 数字与字符串的相互转换 5.2 字符串查找与判断比较 实例125 在字符串中查找指定字符 实例126 在字符串中提取部分字符 实例127 利用IsNumeric函数判断是否为数字 ...
  • JavaScript中如何判断一个字符串是否为合法日期 JavaServer Page(JSP)简介1 JavaServer Pages (JSP) 1.0简单介绍 ---III JavaServer PagesTM 白皮书 Java布局管理器深入讨论 Java代码编写一般性指导 JAVA...

空空如也

空空如也

1 2 3 4
收藏数 73
精华内容 29
关键字:

如何判断汉字的结构