精华内容
下载资源
问答
  • 二里面有几个九
    千次阅读
    2018-08-16 22:55:36

        如果回答是:一个遍历,将所有为1的位加起来,基本上要被Pass了!

        因为你一旦将这个十进制的数转成二进制你就已经输了,二进制遍历的时间复杂度是O(n),下面介绍一种更高效的位移算法,其计算步骤是:如果一个正整数x与x-1做按位与运算的结果>0,那么就说明这个正整数x的二进制里面包含了一个1,因此可以循环计算,直到x<=0,执行计算的次数就是1的个数。

        举个例子:11的二进制是:00001011,11-1=10的二进制:00001010,11&10=00001010=10,此时计算了1次;10(00001010)&9(00001001)=8(00001000),此时计算了2次,8(00001000)&7(00000111)=0,此时计算了3次。因此11的二进制里有3个1。发现规律了么,11与10做与操作,正好能抵消一个二进制的1,因为二进制的进位是2,因此一位上面要么是0,要么是1,如果是1+1那么该位就变为0且向前一位进1;反过来,11的二进制是00001011,11-1的二进制是00001010,做与操作,正好把最低位(最右边)第一个1抵消了,依次类推,运算几次就有几个1。

        原理:x与x-1做按位&操作,将右侧向左第一个二进制的1抵消,因此运算了几次,x的二进制表示就有几个1。

        时间复杂度:可以看到,该算法的运算次数只跟x中包含了几个1有关,如果x中包含了10个1,那么算法就运算了10次,最坏情况下,例如2^n-1二进制表示全是1,时间复杂度为O(n)。

       java实现如下:

    public int count1(int value) {
        int count = 0;
        while(value > 0) {
            value = value & (value-1);
            count++;
        }
        return count;
    }

     

     

    更多相关内容
  • python中如何判断一个数组中有几个不相同的数 输入 1 2 2 3 3 2 7 8 9 输出 6
  • UTF-8编码占几个字节?

    万次阅读 多人点赞 2019-01-29 16:18:00
    占2字节的:带附加符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文及它拿字母则需要二个字节编码 占3字节的:基本等同于GBK,含21000多汉字 占4字节的:中日韩超大字符集...

    占2个字节的:带有附加符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文及它拿字母则需要二个字节编码

    占3个字节的:基本等同于GBK,含21000多个汉字

    占4个字节的:中日韩超大字符集里面的汉字,有5万多个

    一个utf8数字占1个字节

    一个utf8英文字母占1个字节

    少数是汉字每个占用3个字节,多数占用4个字节。

     

    占用3个字节的范围

     

     
    1. U+2E80 - U+2EF3 : 0xE2 0xBA 0x80 - 0xE2 0xBB 0xB3 共 115 个

    2. U+2F00 - U+2FD5 : 0xE2 0xBC 0x80 - 0xE2 0xBF 0x95 共 213 个

    3. U+3005 - U+3029 : 0xE3 0x80 0x85 - 0xE3 0x80 0xA9 共 36 个

    4. U+3038 - U+4DB5 : 0xE3 0x80 0xB8 - 0xE4 0xB6 0xB5 共 7549 个

    5. U+4E00 - U+FA6A : 0xE4 0xB8 0x80 - 0xEF 0xA9 0xAA 共 44138 个

    6. U+FA70 - U+FAD9 : 0xEF 0xA9 0xB0 - 0xEF 0xAB 0x99 共 105 个

     

    合计: 52156 个

    占用4个字节的范围

     

    U+20000 - U+2FA1D : 0xF0 0xA0 0x80 0x80 - 0xF0 0xAF 0xA8 0x9D      共 64029 个

     

    合计: 64029 个

     

    延伸:

     

    1. ASCII码

    我们知道,在计算机内部,所有的信息最终都表示为一个二进制的字符串。每一个二进制位(bit)有0和1两种状态,因此八个二进制位就可以组合出256种状态,这被称为一个字节(byte)。也就是说,一个字节一共可以用来表示256种不同的状态,每一个状态对应一个符号,就是256个符号,从0000000到11111111。

    上个世纪60年代,美国制定了一套字符编码,对英语字符与二进制位之间的关系,做了统一规定。这被称为ASCII码,一直沿用至今。

    ASCII码一共规定了128个字符的编码,比如空格"SPACE"是32(二进制00100000),大写的字母A是65(二进制01000001)。这128个符号(包括32个不能打印出来的控制符号),只占用了一个字节的后面7位,最前面的1位统一规定为0。

    2、非ASCII编码

    英语用128个符号编码就够了,但是用来表示其他语言,128个符号是不够的。比如,在法语中,字母上方有注音符号,它就无法用ASCII码表示。于是,一些欧洲国家就决定,利用字节中闲置的最高位编入新的符号。比如,法语中的é的编码为130(二进制10000010)。这样一来,这些欧洲国家使用的编码体系,可以表示最多256个符号。

    但是,这里又出现了新的问题。不同的国家有不同的字母,因此,哪怕它们都使用256个符号的编码方式,代表的字母却不一样。比如,130在法语编码中代表了é,在希伯来语编码中却代表了字母Gimel (ג),在俄语编码中又会代表另一个符号。但是不管怎样,所有这些编码方式中,0--127表示的符号是一样的,不一样的只是128--255的这一段。

    至于亚洲国家的文字,使用的符号就更多了,汉字就多达10万左右。一个字节只能表示256种符号,肯定是不够的,就必须使用多个字节表达一个符号。比如,简体中文常见的编码方式是GB2312,使用两个字节表示一个汉字,所以理论上最多可以表示256x256=65536个符号。

    中文编码的问题需要专文讨论,这篇笔记不涉及。这里只指出,虽然都是用多个字节表示一个符号,但是GB类的汉字编码与后文的Unicode和UTF-8是毫无关系的。

    3.Unicode

    正如上一节所说,世界上存在着多种编码方式,同一个二进制数字可以被解释成不同的符号。因此,要想打开一个文本文件,就必须知道它的编码方式,否则用错误的编码方式解读,就会出现乱码。为什么电子邮件常常出现乱码?就是因为发信人和收信人使用的编码方式不一样。

    可以想象,如果有一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,那么乱码问题就会消失。这就是Unicode,就像它的名字都表示的,这是一种所有符号的编码。

    Unicode当然是一个很大的集合,现在的规模可以容纳100多万个符号。每个符号的编码都不一样,比如,U+0639表示阿拉伯字母Ain,U+0041表示英语的大写字母A,U+4E25表示汉字"严"。具体的符号对应表,可以查询unicode.org,或者专门的汉字对应表

    4. Unicode的问题

    需要注意的是,Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。

    比如,汉字"严"的unicode是十六进制数4E25,转换成二进制数足足有15位(100111000100101),也就是说这个符号的表示至少需要2个字节。表示其他更大的符号,可能需要3个字节或者4个字节,甚至更多。

    这里就有两个严重的问题,第一个问题是,如何才能区别Unicode和ASCII?计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?第二个问题是,我们已经知道,英文字母只用一个字节表示就够了,如果Unicode统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储来说是极大的浪费,文本文件的大小会因此大出二三倍,这是无法接受的。

    它们造成的结果是:1)出现了Unicode的多种存储方式,也就是说有许多种不同的二进制格式,可以用来表示Unicode。2)Unicode在很长一段时间内无法推广,直到互联网的出现。

    5.UTF-8

    互联网的普及,强烈要求出现一种统一的编码方式。UTF-8就是在互联网上使用最广的一种Unicode的实现方式。其他实现方式还包括UTF-16(字符用两个字节或四个字节表示)和UTF-32(字符用四个字节表示),不过在互联网上基本不用。重复一遍,这里的关系是,UTF-8是Unicode的实现方式之一。

    UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。

    UTF-8的编码规则很简单,只有二条:

    1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。

    2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。

    下表总结了编码规则,字母x表示可用编码的位。

    Unicode符号范围 | UTF-8编码方式
    (十六进制) | (二进制)
    --------------------+---------------------------------------------
    0000 0000-0000 007F | 0xxxxxxx
    0000 0080-0000 07FF | 110xxxxx 10xxxxxx
    0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
    0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

    跟据上表,解读UTF-8编码非常简单。如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。

    下面,还是以汉字"严"为例,演示如何实现UTF-8编码。

    已知"严"的unicode是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800-0000 FFFF),因此"严"的UTF-8编码需要三个字节,即格式是"1110xxxx 10xxxxxx 10xxxxxx"。然后,从"严"的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,"严"的UTF-8编码是"11100100 10111000 10100101",转换成十六进制就是E4B8A5。

    6. Little endian和Big endian

     

    上一节已经提到,Unicode码可以采用UCS-2格式直接存储。以汉字"严"为例,Unicode码是4E25,需要用两个字节存储,一个字节是4E,另一个字节是25。存储的时候,4E在前,25在后,就是Big endian方式;25在前,4E在后,就是Little endian方式。

    这两个古怪的名称来自英国作家斯威夫特的《格列佛游记》。在该书中,小人国里爆发了内战,战争起因是人们争论,吃鸡蛋时究竟是从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开。为了这件事情,前后爆发了六次战争,一个皇帝送了命,另一个皇帝丢了王位。

    因此,第一个字节在前,就是"大头方式"(Big endian),第二个字节在前就是"小头方式"(Little endian)。

    那么很自然的,就会出现一个问题:计算机怎么知道某一个文件到底采用哪一种方式编码?

    Unicode规范中定义,每一个文件的最前面分别加入一个表示编码顺序的字符,这个字符的名字叫做"零宽度非换行空格"(ZERO WIDTH NO-BREAK SPACE),用FEFF表示。这正好是两个字节,而且FF比FE大1。

    如果一个文本文件的头两个字节是FE FF,就表示该文件采用大头方式;如果头两个字节是FF FE,就表示该文件采用小头方式。

     

    参考文献:

    http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html

    http://blog.csdn.net/chummyhe89/article/details/7777613

    展开全文
  • UTF-8中文一个汉字占几个字节?

    千次阅读 2019-10-12 11:28:05
     10、最后,要回答你的问题,常规来看,中文汉字在utf-8中到底占几个字节,一般是3个字节,最常见的编码方式是1110xxxx 10xxxxxx 10xxxxxx。 ---------------------------------------------- 参考: ...

    英文字母和中文汉字在不同字符集编码下的字节数

    英文字母:

    字节数 : 1;编码:GB2312

    字节数 : 1;编码:GBK

    字节数 : 1;编码:GB18030

    字节数 : 1;编码:ISO-8859-1

    字节数 : 1;编码:UTF-8

    字节数 : 4;编码:UTF-16

    字节数 : 2;编码:UTF-16BE

    字节数 : 2;编码:UTF-16LE

     

    中文汉字:

    字节数 : 2;编码:GB2312

    字节数 : 2;编码:GBK

    字节数 : 2;编码:GB18030

    字节数 : 1;编码:ISO-8859-1

    字节数 : 3;编码:UTF-8

    字节数 : 4;编码:UTF-16

    字节数 : 2;编码:UTF-16BE

    字节数 : 2;编码:UTF-16LE

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

    这是个好问题,可以当作一个笔试题。先从字符编码讲起。

    1、美国人首先对其英文字符进行了编码,也就是最早的ascii码,用一个字节的低7位来表示英文的128个字符,高1位统一为0;

    2、后来欧洲人发现尼玛你这128位哪够用,比如我高贵的法国人字母上面的还有注音符,这个怎么区分,得,把高1位编进来吧,这样欧洲普遍使用一个全字节进行编码,最多可表示256位。欧美人就是喜欢直来直去,字符少,编码用得位数少;

    3、但是即使位数少,不同国家地区用不同的字符编码,虽然0--127表示的符号是一样的,但是128--255这一段的解释完全乱套了,即使2进制完全一样,表示的字符完全不一样,比如135在法语,希伯来语,俄语编码中完全是不同的符号;

    4、更麻烦的是,尼玛这电脑高科技传到中国后,中国人发现我们有10万多个汉字,你们欧美这256字塞牙缝都不够。于是就发明了GB2312这些汉字编码,典型的用2个字节来表示绝大部分的常用汉字,最多可以表示65536个汉字字符,这样就不难理解有些汉字你在新华字典里查得到,但是电脑上如果不处理一下你是显示不出来的了吧。

    5、这下各用各的字符集编码,这世界咋统一?俄国人发封email给中国人,两边字符集编码不同,尼玛显示都是乱码啊。为了统一,于是就发明了unicode,将世界上所有的符号都纳入其中,每一个符号都给予一个独一无二的编码,现在unicode可以容纳100多万个符号,每个符号的编码都不一样,这下可统一了,所有语言都可以互通,一个网页页面里可以同时显示各国文字。

    6、然而,unicode虽然统一了全世界字符的二进制编码,但没有规定如何存储啊,亲。x86和amd体系结构的电脑小端序和大端序都分不清,别提计算机如何识别到底是unicode还是acsii了。如果Unicode统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,文本文件的大小会因此大出二三倍,这对于存储来说是极大的浪费。这样导致一个后果:出现了Unicode的多种存储方式。

    7、互联网的兴起,网页上要显示各种字符,必须统一啊,亲。utf-8就是Unicode最重要的实现方式之一。另外还有utf-16utf-32等。UTF-8不是固定字长编码的,而是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。这是种比较巧妙的设计,如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。

    8、注意unicode的字符编码和utf-8的存储编码表示是不同的,例如"严"字的Unicode码是4E25,UTF-8编码是E4B8A5,这个7里面解释了的,UTF-8编码不仅考虑了编码,还考虑了存储,E4B8A5是在存储识别编码的基础上塞进了4E25。 9、UTF-8 使用一至四个字节为每个字符编码。128 个 ASCII 字符(Unicode 范围由 U+0000 至 U+007F)只需一个字节,带有变音符号的拉丁文、希腊文、西里尔字母、亚美尼亚语希伯来文阿拉伯文、叙利亚文及马尔代夫语(Unicode 范围由 U+0080 至 U+07FF)需要二个字节,其他基本多文种平面(BMP)中的字符(CJK属于此类-Qieqie注)使用三个字节,其他 Unicode 辅助平面的字符使用四字节编码。  10、最后,要回答你的问题,常规来看,中文汉字在utf-8中到底占几个字节,一般是3个字节,最常见的编码方式是1110xxxx 10xxxxxx 10xxxxxx。

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

    参考:

    https://blog.csdn.net/u010737354/article/details/52456570

    https://www.cnblogs.com/lancidie/archive/2013/04/12/3017241.html

    展开全文
  • 这篇博客 介绍关于多线程基础篇的知识:Thread类 的常见构造方法和属性、和线程相关的几个重要操作 以及 Java 线程中的几种状态~

    ☕导航小助手☕

        🍱写在前面

            🧇一、Thread类的常见构造方法

            🍚二、Thread 的几个常见属性

            🍛三、和线程相关的几个重要的操作

                        🍞🍞3.1 启动线程 - start()

                        🍣🍣3.2 中断线程

                        🍤🍤3.3 等待线程 - join()

                        🥩🥩3.4 获取到线程引用

                        🧀🧀3.5 休眠线程 - sleep()

            🍜四、线程的状态

                        🍰🍰4.1 Java 线程中的基本状态

                        🥡🥡4.2 线程之间的状态是如何转换的


    写在前面

    这篇博客,仍然来介绍关于多线程基础篇的知识~

    其主要介绍的内容是:Thread类 的常见构造方法和属性、和线程相关的几个重要操作 以及 Java 线程中的几种状态~

    下面,正文开始 ......

    一、Thread类的常见构造方法

    构造方法说明
    Thread()创建线程对象
    Thread(Runnable target)使用 Runnable对象 创建线程
    Thread(String name)创建线程对象,并命名
    Thread(Runnable target)使用 Runnable对象 创建线程,并命名
    【了解】Thread(ThreadGroup group,Runnable target)
    线程可以被用来分组管理,分好的组即为线程组,这个目前我们了解即可

    Thread():

    默认无参构造方法,如 :Thread t1 = new Thread();


    Thread(Runnable target):

    使用 Runnable 创建一个任务,再把 任务 放到线程里面,

    如 Thread t2 = new Thread(new MyThread());


    Thread(String name):

    给线程起一个名字,线程 在操作系统内核里 没有名字,只有一个身份标识~

    但是 Java中 为了让程序猿 调试的时候方便理解,这个线程是谁~

    就在 JVM 里给对应的 Thread对象 加了个名字(JVM中的 Thread对象 和 操作系统内核里面的线程 一一对应)~

    这个名字对于程序的执行没影响,但是对于程序猿调试来说还是挺有用的~

    如果不手动设置,也会有默认的名字,形如 Thread-0、Thread-1、......


    Thread(Runnable target):

    使用 Runnable对象 创建线程,并命名:

    package thread;
    
    public class Demo7 {
        public static void main(String[] args) {
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        System.out.println("hello thread");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            },("我的线程"));
            t.start();
    
            while(true){
                System.out.println("hello main");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    程序运行,我们可以找到 jconsole.exe 去看一看:

    二、Thread 的几个常见属性

    属性获取方法
    IDgetId()
    名称getName()
    状态getState()
    优先级getPriority()
    是否后台进程isDaemon()
    是否存活isAlive()
    是否被中断isInterrupted()

    getId():获取到的是 线程在 JVM 中的身份标识~

    线程中的身份标识是有好几个的:内核的 PCB 上有标识;到了用户态线程库里 也有标识(pthread,操作系统提供的线程库);到了 JVM 里又有一个标识(JVM Thread类 底层也是调用操作系统的 pthread 库) ~

    三个标识各不相同,但是目的都是作为身份的区分~

    由于 介绍的是 Java程序,所以我们只需要知道 JVM 中的身份标识即可~


    getName():

    在 Thread 构造方法里,自己所起的名字~


    getState():

    PCB 里面有状态,此处得到的状态是 JVM 里面设立的状态体系,这个状态比操作系统内置的状态要更丰富一些~


    getPriority():

    获取到优先级~


    isDaemon():

    daemon 称为 "守护线程",也可以理解为 "后台线程"~

    类似于 手机app,打开一个app,此时这个app就来到了 "前台",当按到 "回到菜单" 这样的按钮,此时app就切换到 "后台"~
    线程也分成 前台线程 和 后台线程(这个是可以自己来设置的),创建线程出来默认为是 前台线程,前台线程 会阻止进程结束;换句话说,进程会保证所有的前台线程都执行完了 才会退出~

    当然,main 这个线程就是一个前台线程~

    后台线程不会阻止进程结束,所以 进程退出的时候 不关后台进程是否执行完 就退出了~

    如:

    package thread;
    
    public class Demo7 {
        public static void main(String[] args) {
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        System.out.println("hello thread");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            },("我的线程"));
            t.start();
        }
    }

    执行结果:

    分析:

    由于该线程是一个前台线程,所以需要等待 其运行结束,进程才会结束,所以会一直执行下去~

    再如:我们可以把他手动设置成 后台线程:

    package thread;
    
    public class Demo7 {
        public static void main(String[] args) {
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        System.out.println("hello thread");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            },("我的线程"));
            
            //手动设置成 后台线程
            t.setDaemon(true);
    
            t.start();
    
        }
    }
    

    运行结果:

    分析:

    通过 setDaemon(true) 可以把线程设置为后台线程,等到主线程执行完,进程就结束了~

    需要注意的是,先要 设置线程,然后再启动线程~


    示例:

    package thread;
    
    public class Demo8 {
        public static void main(String[] args) {
            Thread t = new Thread(() ->{
                while (true) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"我的线程");
    
            t.start();
    
            System.out.println("线程Id:" + t.getId());
            System.out.println("线程名称:" + t.getName());
            System.out.println("线程状态:" + t.getState());
            System.out.println("线程优先级:" + t.getPriority());
            System.out.println("是否后台线程(true/false):" + t.isDaemon());
            System.out.println("是否存活:" + t.isAlive());
        }
    }
    

     运行结果:

    这些操作都是获取到的是 一瞬间的状态,而不是持续性的状态~ 

    三、和线程相关的几个重要的操作

    3.1 启动线程 - start()

    创建 Thread实例,并没有真的在操作系统内核里创建出线程(仅仅只是在安排任务,"跑步时 的 '各就各位,预备' ")!!!

    而是 调用 start,才是真正创建出线程("发令枪响")!!!

    这个在上面和上一篇的博客也提到过,这里就不做过多演示了~

    3.2 中断线程

    怎么让线程执行结束,其实方法很简单:只要让线程的 入口方法 执行完了,线程就随之结束了~

    主线程 的入口方法,就可以视为 mian方法~

    入口方法:其实就是 上一篇博客所介绍的 创建线程 的代码~

    🚪上一篇博客的传送门🚪

    所谓的 "中断线程",就是让线程尽快把 入口方法执行结束~


    方法一:直接自己定义 标志位 来区分线程是否要结束~

    package thread;
    
    public class Demo9 {
        //用一个布尔变量来表示 线程是否要结束
        //这个变量是一个成员变量,而不是局部变量
        private static boolean isQuit =  false;
        public static void main(String[] args) throws InterruptedException {
            Thread t = new Thread(() -> {
                while (!isQuit) {
                    System.out.println("线程运行中......");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("新线程执行结束!");
            });
            t.start();
    
            Thread.sleep(5000);
            System.out.println("控制新线程退出!");
            isQuit = true;
        }
    }
    

    运行结果:

    这个代码中,控制线程结束,主要是这个线程里有一个循环,这个循环执行完,就算结束了~

    很多时候创建线程都是 让线程完成一些比较复杂的任务,往往都有一些循环,正是因为有循环,执行的时间才可能比较长;如果线程本身执行的很快,刷的一下就结束了,那么也就没有提前控制它的必要了~ 


    方法二:使用 Thread类 中自带的标志位~ 

    这种方法是可行的~

    Thread 其实内置了一个标志位,不需要咱们去手动创建标志位

    Thread.currentThread().isInterrupted()
    
    --currentThread() 是 Thread类的静态方法,获取到当前线程的实例,这个方法中有一个线程会调用它
    --线程1 调用这个方法,就能返回线程1 的 Thread对象;
    --线程2 调用这个方法,就能返回线程2 的 Thread对象~
    --类似于 JavaSE 里面的 this~
    
    --isInterrupted() 为判定内置的标志,可以获取到标志位的值,为 true 表示线程要被中断~
    package thread;
    
    public class Demo10 {
        public static void main(String[] args) throws InterruptedException {
            Thread t = new Thread(() ->{
                while (!Thread.currentThread().isInterrupted()) {
                    System.out.println("线程运行中......");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            t.start();
    
            Thread.sleep(5000);
            System.out.println("控制新线程退出!");
            t.interrupt();
        }
    }

    运行结果:

    说明:

    调用了 interrupt,产生了一个异常~

    异常虽然出现了,但是线程仍然在继续运行~ 

    注意理解 interrupt方法 的行为:

    1. 如果 t 线程 没有处在阻塞状态,此时 interrupt 就会修改内置的标志位~
    2. 如果 t 线程 正在处于阻塞状态,此时 interrupt 就让线程内部产生阻塞的方法,例如 sleep 抛出异常~

    上述循环代码中,正好异常被捕获了~

    而捕获之后,啥也没有干 只是打印了一个调用栈:

      

    而如果 把上述代码中的  e.printStackTrace(); 给注释掉,那么就啥也不打印,运行结果调用栈也不打印了,直接忽略异常了~

    正是因为这样的捕获操作,程序员就可以自行控制线程的退出行为了:

    即 在里面可以自主的微操作了~

     


    当然,除了 Thread.currentThread().isInterrupted(),Thread类还自带了一个静态方法 interrupted() 也可以访问标志位~

    使用 Thread.interrupted() 即可~

    1. 如果当前线程处于运行的状态,就是修改标志位
    2. 如果当前线程处于阻塞的状态,则是触发一个异常,线程内部就会通过这个异常被唤醒

    这里也不做过多叙述了,一般掌握一个就可以了~

    3.3 等待线程 - join()

    我们已经知道,线程之间的执行顺序完全是随机的,看系统的调度~

    一般情况下写代码,其实是比较讨厌这种随机性的,更需要能够让顺序给确定下来~

    join() 就是一种确定线程执行顺序的 辅助手段~

    如:咱们不可以确定两个线程的开始执行顺序,但是可以通过 join() 来控制两个线程的结束顺序~

    如:上一篇博客曾举了一个例子:

     private static void concurrency() {
            //concurrency 的意思是 "并发"
            long begin = System.currentTimeMillis();
            Thread t1 = new Thread(() ->{
                int a = 0;
                for(long i = 0; i < count; i++) {
                    a++;
                }
            });
    
            Thread t2 = new Thread(() ->{
                int a = 0;
                for(long i = 0; i < count; i++) {
                    a++;
                }
            });
    
            t1.start();
            t2.start();
    
            try {
                t1.join();
                t2.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            long end = System.currentTimeMillis();
            System.out.println("多线程并发执行的时间:" + (end-begin) + "毫秒");
    
        }

    我们知道,main、t1、t2 三个线程是随机调度执行的~

    但是此处的需求是,希望让 t1、t2 都执行完了之后,main再进行计时,来统计执行时间~

    就是希望 t1、t2 先执行完,main 后执行完~

    咱们无法干预 调度器的行为(调度器还是该咋随机咋随机),但是可以让 main 线程进行等待~

    就在 main 里分别调用了 t1.join() 和 t2.join() ~

    t1.join() —> main阻塞,等待 t1 执行完~

    t2.join() —> main阻塞,等待 t2 执行完~ 

    当 t1、t2 都执行完了以后,main 解除阻塞,然后才能继续往下执行,才能执行完~

    main 阻塞了,就不参与调度了,但是 t1、t2 仍然参与调度,它们的执行还是会 随机调度、交替执行的~ 


    main 有点特殊,不太方便 join()~

    一般情况下,想让谁阻塞,谁就调用 join() 即可 ~

    如:要实现让 t2 等待 t1,main 等待 t2,就可以 main 去调用 t2.join(),t2 调用 t1.join() 即可~

    如:

    package thread;
    
    public class Demo11 {
        private static Thread t1 = null;
        private static Thread t2 = null;
    
        public static void main(String[] args) throws InterruptedException {
            System.out.println("main begin");
            t1 = new Thread(() -> {
                System.out.println("t1 begin");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t1 end");
            });
            t1.start();
    
            t2 = new Thread(() -> {
                System.out.println("t2 begin");
                try {
                    t1.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t2 end");
            });
            t2.start();
    
            t2.join();
            System.out.println("main end");
        }
    }
    

    运行结果:


    如:

    package thread;
    //控制 main 先运行 t1,t1 执行完 再执行 t2
    public class Demo12 {
        public static void main(String[] args) throws InterruptedException {
            System.out.println("main begin");
            Thread t1 = new Thread(() -> {
                System.out.println("t1 begin");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t1 end");
            });
            t1.start();
    
            //等待 t1 结束
            t1.join();
    
            Thread t2 = new Thread(() -> {
                System.out.println("t2 begin");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t2 end");
            });
    
            t2.start();
            t2.join();
            System.out.println("main end");
        }
    }
    

    运行结果:


    join() 的行为:

    1. 如果被等待的线程 还没执行完,就阻塞等待~
    2. 如果被等待的线程 已经执行完了,就直接返回~

    当然,join() 还有其他的版本~

    方法说明
    public void join()
    等待线程结束(死等)
    public void join(long millis)
    等待线程结束,最多等 millis 毫秒(设定了等待的最大时间)
    public void join(long millis, int nanos)
    同理,但可以更高精度(设定了等待的最大时间)

    在实际的开发过程中,在一般情况下,都不会使用 "死等" 的方式~

    因为 "死等" 的方式有风险~

    万一代码出了 bug 没控制好,就很容易让服务器 "卡死",无法继续工作~

    更多的情况下是 等待的时候预期好最多等多久,超过时间了就需要做出一些措施~

    3.4 获取到线程引用

    为了对线程进行操作,就需要获取到线程的引用~

    这里的操作,就是指:线程等待、线程中断、获取各种线程的属性~

    如果是 继承 Thread类,重写 run方法,可以直接在 run 中使用 this 即可获取到线程的实例~

    但是如果是 Runnable 或者 Lambda,this 就不行了(指向的就不是 Thread实例)~

    更通用的方法是,使用 Thread.currentThread() ,currentThread() 是一个静态方法,其特点是 哪个线程来调用这个方法,得到的线程就是哪个实例~

    3.5 休眠线程 - sleep()

    sleep() 能够让线程休眠一会儿~

    前面已经所介绍了 sleep() 的使用方法,现在来画图介绍一下 sleep() 到底是如何使用的~


     注意:实际上应该是 双向链表 连接,不过为了简单,所以画的就是 单向链表 了~

    注意:

    如果写了一个 sleep(1000),那么也不一定 就会在1000ms 之后就上 CPU 运行~

    1000ms 之后只是把这个 PCB 放回了就绪队列!!!至于说这个线程啥时候执行,还得看调度器的心情~

    因此,sleep(1000) 意味着休眠时间不小于 1000ms,实际的休眠时间会略大于 1000ms,这个误差精度在 10ms 以下~ 

    四、线程的状态

    4.1 Java 线程中的基本状态

    操作系统中 进程的状态 有 阻塞状态、就绪状态、执行状态~

    而在 Java/JVM里 线程中也有一些状态,更是对此做出了一些细分~

    New:安排了工作,还未开始行动,创建了 Thread对象,但是还没有调用 start方法,系统内核里面没有线程~

    Runnable:就绪状态,包含了两个意思:

    1. 正在 CPU上运行
    2. 还没有在 CPU 上运行,但是已经准备好了

    Blocked:等待锁~

    Waiting:线程中调用了 wait方法~

    Time Waiting:线程中通过 sleep方法 进入的阻塞~

    Terminated:工作完成了,系统里面的线程已经执行完毕,销毁了(相当于线程的 run方法 执行完了),但是 Thread对象 还在~

     Blocked、Waiting、Time Waiting 三种状态都是 阻塞状态,只不过是产生阻塞状态的原因不一样,Java里面使用不同的状态来表示了~


    package thread;
    
    public class Demo13 {
        public static void main(String[] args) throws InterruptedException {
            Thread t = new Thread(() -> {
                    System.out.println("hello thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
            });
    
            //在 t.start() 之前获取,获取到的是 线程还未创建的状态
            System.out.println(t.getState());
    
            t.start();
            t.join();
    
            //在 t.join() 之后获取,获取到的是线程已经结束之后的状态
            System.out.println(t.getState());
        }
    }
    

    运行结果:

    4.2 线程之间的状态是如何转换的

    主干道是:New => Runnable => Terminated ~

    在 Runnable状态 会根据特定的代码进入支线任务,这些 "支线任务" 都是 "阻塞状态"~

    这三种 "阻塞状态",进入的方式不一样,同时阻塞的时间也不同,被唤醒的方式也不同~

    如:sleep() 等到时间到了自动唤醒,至于 wait() 和 synchronized() 是如何唤醒的以后会介绍的~

    这一篇的博客就到此为止了,下一篇博客将会介绍到 多线程安全的问题 ~

    如果感觉这一篇博客对你有帮助的话,可以一键三连走一波,非常非常感谢啦 ~

    展开全文
  • 数仓建设几个关键问题

    千次阅读 2019-04-06 18:46:32
    数仓的职责 以我当前的认知,数仓应该至少以下几个职责: 数据整合 统一口径 下面详细说说这两点。 数据整合
  • 2、wiki里面对字长的范围也很多、8位、9位、12位、16位、27位等等都是不同系统下的字长 https://en.wikipedia.org/wiki/Word_(computer_architecture)#Size_families 还是说一个字等于几个字节跟字长是没有...
  • 一、五大内存分区 在C++中,...里面的变量通常是局部变量、函数参数等。 堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一new就要对应一delete。如果程序员没有释放...
  • 判断一整型数据有几位 从键盘输入一整型数据(int型),用switch语句和循环语句编写程序判断该整数共有位,并输出包含各个数字的个数。例如,从键盘输入整数16644,该整数共有5位,其中11,26,24。 程序...
  • 分享几个我觉得好用的导航网站

    千次阅读 2022-01-19 21:46:36
    一、爱达杂货铺... 这里面有可多资源网站,很多都是免费的。 、完美导航https://www.9eip.com/ 三、快导航网https://www.hifast.cn/ 我觉得这里面的程序员导航和二次元导航都挺方便的。 ...
  • 宫格一共多少种解法

    千次阅读 2019-08-26 20:07:41
    今天同事和我讨论了九宫格的回溯问题,说实话,我之前玩过一款游戏叫“九阴真经”,里面有一个副本第关开启就要解一个九宫格。我一直认为很多种解法,至少得百八十种吧。但是用回溯解完之后,发现,我太天真了...
  • 级C:维数组

    千次阅读 2021-11-08 20:49:24
    ①数组两中括号[][],第一可有可无,第二个必须里面的内容为常量表达式,也就是不能改变的。 当数组中每元素带下标时,称这样的数组为维数组。 在C语言中,维数组的定义语句形式如下所示:(可以...
  • #Python# 维数组的定义、使用的种方法

    万次阅读 多人点赞 2019-05-16 16:20:36
    #Python# 维数组的定义、使用的种方法通常的困扰1、先介绍最简单的一种方式2、再介绍最常见的一种方式3、最后介绍最暴力直观的一种方式 通常的困扰 之前对Python里面创建、使用维数组也很懵,于是也上网查了...
  • 求1*2*3*......*100的积中末尾有几个0

    万次阅读 2014-02-24 09:14:47
     连乘积的末尾有几个0?  答案是两个0。其中,从因数10得到1个0,从因数2和5相乘又得到1个0,共计两个。  刚好两个0?会不会再多几个呢?  如果不相信,可以把乘积计算出来,结果得到  原式=3628800。你看,...
  • 每一个二进制位(bit)0和1两种状态,因此八个二进制位就可以组合出256种状态,这被称为一字节(byte)。也就是说,一字节一共可以用来表示256种不同的状态,每一状态对应一符号,就是256符号,从...
  • 历久而新,我的新书《第行代码》已出版!

    万次阅读 多人点赞 2016-11-23 09:49:08
    《第行代码》中的内容我相信很多人都会非常感兴趣,因为里面确实加入了很多很新的知识。略粗概括一下的话,新增内容就包括Android Studio、Gradle、Material Design、运行...除此之外,还加入了几个比较完整的项目
  • 16进制几个字符是一个字节

    万次阅读 2016-07-29 15:49:16
    16进制 6e69 占用几个字节   16进制中,两个字符是一个字节,一个字符的话,是一个字节。   例如:A是一个字节,AA是一个字节,AAA是两个字节,AAAA是两个字节。   不过,16进制能够出现的字符(不区分大小...
  • [摘要]高层的住宅层好,这是很多的购房者在买房子的过程中会纠结不已的事情。住高了就觉得眩晕,住低了就觉得太吵闹,高层住宅到底哪些楼层是好呢?高层住宅的楼层选择要考虑到采光、视野等因素。 高层的...
  • 链接暴露: 前面几个问题大家可能都很好理解,一看到这个的小伙伴可能会比较疑惑,啥是链接暴露呀? 相信是个开发同学都对这个画面一点都不陌生吧,懂点行的仔都可以打开谷歌的开发者模式,然后看看你的网页代码...
  • 如 173 + 286 = 459 且 将 286 + 173 = 459 视为同一种,问满足这样条件的情况有几种 问题解法: 将9个不重复数字填入到9个位置并且满足等式成立条件,可以看做求9个数组的全排列,只要全排列满足 前3数字 + 中间3...
  • 十进制数转化为进制数的两种方法

    万次阅读 多人点赞 2020-12-29 21:14:52
    我们先把进制各位的位权列在表格里面。(我们如何知道要列多少位出来呢?其实我们就是要列到比150小并且最接近150的那一位,也就是列到128就可以了。) 128 64 32 16 8 4 2 1 ...
  • 计算机级题目是题库原题吗,计算机级题目是随机抽的吗2021-04-2011:07:30来源: 作者:wangziyangoffice的话选择题不用看的,三道操作题都是原题。学完选择题要花很多时间的,...考级刷历年题库是很必要的事...
  • 元宵节要到了,给大家猜几个谜语

    万次阅读 2020-02-07 16:07:32
    1.一直和党心连心(打一字) 忠 2、 永久太平(打一中国地名) 长宁 3、 贸易协定(打一数学名词) 交换律 4、 直上重霄(打一成语) 平步青云 5、 环境幽静,生活安逸。...9、 拍一巴掌(打一中国地名)...
  • 原标题:数控程序仿真软件VT打不开的几个问题解决方案VT作为一款实体切削模拟验证软件,用来验证我们编程软件后处理出来NC程序的正确性。尤其在多轴程序验证方面应用广泛。然而在安装问题上,网友们会遇到一些问题,...
  • 时候队伍是一条线,只有横排的同学,例如第一名叫小明、第二个叫小黄、第三叫小绿一共同学。 在数组中,是从0开始数数,第一为数组名加一下标0,如数组a写成a[0]指代第一小明,a[1]则表示小
  • 从N数中取出任意数,求和为指定值的解,并输出对应的数,思想就是进制思想,从N数选任意2^N 种可能,数组里面的每一元素可能被选中,可能不被选中,1代表选中,0代表不选中。假如N=3,哪么就2^3=8种可能...
  • 学会了如何在一figure里面绘制多图像 学会了如何在matlab中绘制图像、对目标区域填充颜色并命名标题 学会了怎么使用matlab的integral2计算二重积分 学会了怎么在matlab中声明(定义)一函数句柄,或者说...
  • Redis 作为一高性能的 key-value 数据库,在项目中我们经常会用到。尤其需要知道一些前缀的key值,那我们怎么去查看呢?通常情况下,Redis 中的数据都是海量的,我们访问Redis中的数据时,一定要做到心中有数,...
  • 计算机基础(一):进制详解

    万次阅读 多人点赞 2020-07-19 11:50:44
    进制 我们都知道,计算机的底层都是使用进制数据进行数据流...CPU 和 内存使用IC电子元件作为基本单元,IC电子元件不同种形状,但是其内部的组成单元称为一个个的引脚。人说CPU 和 内存内部都是超大规模集成

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 659,810
精华内容 263,924
热门标签
关键字:

二里面有几个九