zoukankan      html  css  js  c++  java
  • Java中多线程的学习和使用(一)概述及创建方式

    转载请注明http://www.cnblogs.com/devtrees/p/4395477.html

    多线程的学习

    一.多线程概述

    (一).进程与线程

    在说多线程或者线程之前,让我们来了解一下更显而易见的进程概念。那什么是进程呢?

    进程就是正在进行中的程序。

    Windows操作系统中在任务栏处右击,弹出的菜单中可看见任务管理器,打开它,就可以看见当前运行的程序和进程列表。

     

    进程:是一个正在执行中的程序。

         每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。

    线程:就是进程中的一个独立的控制单元。

              线程在控制进程的执行。

    一个进程中至少有一个线程。

    无论是qq还是迅雷,只要他们启动程序,就会在内存中开辟一段空间,产生地址值,进程就是用于标注这段空间的,用于封装里面的控制单元。

    当出现’’java.lang.NoClassDefFoundError:com/sun/tools/javac/main’’错误的时候,证明java程序找不到tools.jar文件,也就是说环境变量有可能配置不对,或源文件丢失。

    Java VM启动时就会有一个进程java.exe,该进程中至少有一个线程负责java程序的执行,而且这个线程运行的代码存在于main方法中。该线程称之为主线程。

    扩展:其实更细节说明jvm,jvm不止一个线程,还有负责垃圾回收机制的线程。

    有多条执行路径的程序,我们就称之为多线程程序。

    多线程存在的意义。

    同时进行,提高效率。

    如何在我们的程序中自定义多线程程序。

    通过对api的查找,java已经提供了对线程这类事物的描述。

    Java.lang包下有一个Thread类,用于创建程序中的执行线程。就是用于描述控制单元这样d额一个对象。jVM允许应用程序并发地运行多个执行线程。

    创建方式:

    一种是将类声明称Thread的子类(继承Thread),该类应重写Thread类的run方法。

    步骤:

    1.自定义一个类继承Thread.

    2.复写Thread类中的run()方法.

    3.创建线程对象,调用start()方法启动线程。

    //自定义一个类继承Thread

    写个小demo:

     1 class Demo extends Thread{
     2 
     3          //复写Thread类中run()方法
     4 
     5          public void run(){
     6 
     7                    for (int x=0;x<60;x++) {
     8 
     9                             System.out.println("demo run"+x);
    10 
    11                    }
    12 
    13          }
    14 
    15 }
    16 
    17  
    18 
    19 class ThreadDemo{
    20 
    21          public static void main(String[] args) {
    22 
    23                    //创建对象调用方法
    24 
    25                    //创建自定义的线程对象实际上就是创建了线程
    26 
    27                    Demo d = new Demo();
    28 
    29                    //调用start()方法,启动线程;jvm调用线程对象的run()方法,执行内部代码
    30 
    31                    d.start();
    32 
    33                    for (int x=0;x<60;x++) {
    34 
    35                             System.out.println("hello world!"+x);
    36 
    37                    }
    38 
    39          }
    40 
    41 }

    执行结果:

     

    大家会看到执行的结果是交替进行的,并且还是不规则的交替打印。其原理是这样的:

     

    上图解释了,两个线程同时进行,那么不规则如何解释呢?

     

    也就是说:

    因为多个线程都在获取cpu的执行权,cpu执行到谁,谁就运行。明确一点,在某一个时刻,只能有一个程序在运行。(多核除外)cpu在做着快速的切换,以达到看上去是同时运行的效果。我们可以形象的吧多线程的运行形容为在互相抢夺cpu的执行权。

    这就是多线程的一个特性:随机性。

    谁抢到谁执行,至于执行多长时间,cpu说的算。

    接下来思考:我们为什么要复写这个run()方法呢?

    Thread类用于描述线程。该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是run()方法。

    解释:

    既然是用于描述线程,Thread类里面就会有很多功能用于操作线程。我们创建线程的目的其实是为了让线程执行一些代码,那么线程就需要在描述过程中定义这些代码存放的位置。线程要启动,要运行,要运行什么,这个什么在哪啊?线程提供了一个存储空间。这个空间就是run()方法。

    也就是说,Thread类中的run()方法,用于存储线程要运行的代码。同理,主线程要运行的代码,放在main()方法中,这个是jvm定义的。

    这是从run()方法本身说,那么从start()方法说,就是:

    我们在主线程main()方法中创建Thread类对象,也就是创建了一个线程,这个线程对象通过调用start()方法,将线程启动。而这个start()方法又会调用Thread类中的run()来运行里面的的代码,如果我们不复写run(),直接创建线程对象,直接start(),我们不会得到我们想要的结果,因为父类Thread类中的run()方法中可能什么也没有写,start()方法调用run()也是白调用。所以,我们需要复写run()方法,里面写上我们自定义的代码,这样,run()方法才有意义。我们自定义的线程类继承了Thread类,调用start()方法,而start()方法会调用run()父类方法,因为我们复写了父类中的方法,父类会找子类,所以实际会调用我们写的run(0方法,这样我们自定义的代码才会运行。

    因此,复写Thread类中的run()方法

    目的:将自定义代码存储在run()方法中,让线程运行。

    注意:在主线程中线程对象.start()方法和主线程中线程对象直接.run()方法的区别(面试)

    d.start();//开启线程并执行该线程的run()方法

    //d.run();仅仅是对象调用方法,在主线程中执行,而线程创建了,并没有运行。即还是单线程程序。

    小练习:

    创建两个线程,和主线程交替运行。

     1 class Test extends Thread{
     2 
     3                                                                                                                                                            private String name;
     4 
     5                                                                                                                                                                    Test(String name){
     6 
     7                                                                                                                                                                this.name = name;
     8 
     9                                                                                                                                                            }
    10 
    11                                                                                                                                                            public void run(){
    12 
    13                                                                                                                                                                for (int x=0;x<60;x++) {
    14 
    15                                                                                                                                                                         System.out.println(name + "test....run"+x);
    16 
    17                                                                                                                                                                }       
    18 
    19                                                                                                                                                            }
    20 
    21 }
    22 
    23  
    24 
    25 class ThreadDemo{
    26 
    27                                                                                                                                                            public static void main(String[] args) {
    28 
    29                                                                                                                                                                //创建对象调用方法
    30 
    31                                                                                                                                                                //创建自定义的线程对象实际上就是创建了线程
    32 
    33                                                                                                                                                                Demo d = new Demo();
    34 
    35                                                                                                                                                                //调用start()方法,启动线程;jvm调用线程对象的run()方法,执行内部代码
    36 
    37                                                                                                                                                                //d.start();//开启线程并执行该线程的run()方法
    38 
    39                                                                                                                                                                //d.run();仅仅是对象调用方法,在主线程中执行,而线程创建了,并没有运行。即还是单线程程序。
    40 
    41                                                                                                                                                                //测试小练习
    42 
    43                                                                                                                                                                Test t1 = new Test("one");
    44 
    45                                                                                                                                                                Test t2 = new Test("two");
    46 
    47                                                                                                                                                                t1.start();
    48 
    49                                                                                                                                                                t2.start();
    50 
    51                                                                                                                                                                for (int x=0;x<60;x++) {
    52 
    53                                                                                                                                                                        System.out.println("hello world!"+x);
    54 
    55                                                                                                                                                                }
    56 
    57                                                                                                                                                            }
    58 
    59 }                                                                                                                                                             

    线程运行状态(线程的生命周期)

    线程在运行过程中,有几种状态是咱们必须掌握的。只有掌握这几种状态,我们才知道线程是怎样运作的。

     

    获取线程对象以及名称

    线程也有自己的名称,怎么获取呢?

    找线程对象的方法吧。线程名称应该是定义在线程这类事物中。所以怎样获取这名称,是不是线程最熟悉?

    Void setName(String name)

    String getName()

    线程都有自己默认的名称:Thread-编号,该编号从0开始。

     1 class Test extends Thread{
     2 
     3                                                                                                                                                            private String name;
     4 
     5                                                                                                                                                            Test(String name){
     6 
     7                                                                                                                                                                this.name = name;
     8 
     9                                                                                                                                                            }
    10 
    11                                                                                                                                                            public void run(){
    12 
    13                                                                                                                                                                for (int x=0;x<60;x++) {
    14 
    15                                                                                                                                                                        System.out.println(this.getName() + "test....run"+x);
    16 
    17                                                                                                                                                                }       
    18 
    19                                                                                                                                                            }
    20 
    21 }
    22 
    23  
    24 
    25 class ThreadDemo{
    26 
    27                                                                                                                                                            public static void main(String[] args) {
    28 
    29                                                                                                                                                                //创建对象调用方法
    30 
    31                                                                                                                                                                //创建自定义的线程对象实际上就是创建了线程
    32 
    33                                                                                                                                                                Demo d = new Demo();
    34 
    35                                                                                                                                                                //调用start()方法,启动线程;jvm调用线程对象的run()方法,执行内部代码
    36 
    37                                                                                                                                                                //d.start();//开启线程并执行该线程的run()方法
    38 
    39                                                                                                                                                                //d.run();仅仅是对象调用方法,在主线程中执行,而线程创建了,并没有运行。即还是单线程程序。
    40 
    41                                                                                                                                                                //测试小练习
    42 
    43                                                                                                                                                                Test t1 = new Test("one");
    44 
    45                                                                                                                                                                Test t2 = new Test("two");
    46 
    47                                                                                                                                                                t1.start();
    48 
    49                                                                                                                                                                t2.start();
    50 
    51                                                                                                                                                                for (int x=0;x<60;x++) {
    52 
    53                                                                                                                                                                        System.out.println("hello world!"+x);
    54 
    55                                                                                                                                                                }
    56 
    57                                                                                                                                                            }
    58 
    59 }
     1 class Test extends Thread{
     2 
     3                                                                                                                                                            //private String name;
     4 
     5                                                                                                                                                            Test(String name){
     6 
     7                                                                                                                                                                //this.name = name;
     8 super(name);
     9                                                                                                                                                            }
    10 
    11                                                                                                                                                            public void run(){
    12 
    13                                                                                                                                                                for (int x=0;x<60;x++) {
    14 
    15                                                                                                                                                                        System.out.println(this.getName() + "test....run"+x);
    16 
    17                                                                                                                                                                }       
    18 
    19                                                                                                                                                            }
    20 
    21 }
    22 
    23  
    24 
    25 class ThreadDemo{
    26 
    27                                                                                                                                                            public static void main(String[] args) {
    28 
    29                                                                                                                                                                //创建对象调用方法
    30 
    31                                                                                                                                                                //创建自定义的线程对象实际上就是创建了线程
    32 
    33                                                                                                                                                                Demo d = new Demo();
    34 
    35                                                                                                                                                                //调用start()方法,启动线程;jvm调用线程对象的run()方法,执行内部代码
    36 
    37                                                                                                                                                                //d.start();//开启线程并执行该线程的run()方法
    38 
    39                                                                                                                                                                //d.run();仅仅是对象调用方法,在主线程中执行,而线程创建了,并没有运行。即还是单线程程序。
    40 
    41                                                                                                                                                                //测试小练习
    42 
    43                                                                                                                                                                Test t1 = new Test("one");
    44 
    45                                                                                                                                                                Test t2 = new Test("two");
    46 
    47                                                                                                                                                                t1.start();
    48 
    49                                                                                                                                                                t2.start();
    50 
    51                                                                                                                                                                for (int x=0;x<60;x++) {
    52 
    53                                                                                                                                                                        System.out.println("hello world!"+x);
    54 
    55                                                                                                                                                                }
    56 
    57                                                                                                                                                            }
    58 
    59 }

    这里博主本来有两个疑惑来着,后来经过博主苦思冥想,终于破解:

    1.第一个既然打印的this.getName(),为什么获取到的不是one,two?

    2.都已经super(name);了,为什么获取到的还是new对象时的name?

    回答:

    1.this是代表本类对象的引用,这个大家都知道,所以this.getName()是在本类中调用getName()方法,本类有吗?没有。那么他就会去父类Thread类中找,父类有吗?有,获取的是什么呢?由于我们没有创建父类的对象,更没有创建父类创建线程名称的构造函数,更没有调用父类setName()的方法,所以,父类的getName()方法,返回的是默认的setName()方法的值,也就是Thread-编号。

    2.super(name)是在构造方法里,调用的是父类的构造方法,而父类的该方法又被子类对象复写,所以,又会去找子类,所以相当于饶了父类这一个弯子。

    其实在1中也还有个小疑问没有解开,就是当set(),get()方法组合与构造函数同时操作同一事物时,哪一个会好使。

    Thread类中还有个静态方法:

    Static Thread currentThread()   返回对当前正在执行的线程对象。

    也可以获取当前线程的名称,而且比this更通用。

    总结一下:

    获取线程名称:

    Static Thread currentThread():获取当前的线程对象

    getName()

    设置线程名称

    setName()或者构造函数。

    小案例:

    需求:简单的卖票程序,多个窗口同时卖票。

    代码如下:

     1 class Ticket extends Thread{
     2 
     3                                                                                                                                                            private int tick = 100;
     4 
     5                                                                                                                                                            public void run(){
     6 
     7                                                                                                                                                                while(true){
     8 
     9                                                                                                                                                                         if(tick>0){
    10 
    11                                                                                                                                                                         System.out.println("sale:" + tick--);
    12 
    13                                                                                                                                                                         }
    14 
    15                                                                                                                                                                }
    16 
    17                                                                                                                                                            }
    18 
    19 }
    20 
    21 class TicketDemo{
    22 
    23                                                                                                                                                            public static void main(String[] args){
    24 
    25                                                                                                                                                                Ticket t1 = new Ticket();
    26 
    27                                                                                                                                                                Ticket t2 = new Ticket();
    28 
    29                                                                                                                                                                Ticket t3 = new Ticket();
    30 
    31                                                                                                                                                                Ticket t4 = new Ticket();
    32 
    33                                                                                                                                                                t1.start();
    34 
    35                                                                                                                                                                t2.start();
    36 
    37                                                                                                                                                                t3.start();
    38 
    39                                                                                                                                                                t4.start();
    40 
    41                                                                                                                                                            }
    42 
    43 }

    但结果我们发现,每一个线程都会从100打印到1,也就是说每一个线程都卖了100张票,怎么解决呢?

    让四个线程共享这100张票就可以了。

    既然是共享的,没有特有的数据参与运算,我们就可以把100设为静态就搞定了。

    但我们一般不这样,因为设为static后,它的生命周期过长。于是,我们就只创建一个对象,让它执行四次start()方法,结果发现也是可行的,只不过报了一个异常:

    Java.lang.IllegalThreadStateExceptionThread-0…sale:99

    这是无效的线程状态异常。已经运行的程序是不需要在开启的。那么显然,第一种创建方式,已经不行了,接下来我们来介绍一下第二种创建线程的方法。

    创建线程的另外一种方法是声明实现Runnable接口的类。然后实现run方法。然后可以创建该类的实例,在创建时,作为一个参数来传递并启动。

    Runnable接口里只有一个run()方法。

    Thread类有一个构造方法可以接受Runnable接口类型的对象。

     1 class Ticket implements Runnable{//extends Thread{
     2 
     3                                                                                                                                                            private int tick = 100;
     4 
     5                                                                                                                                                            public void run(){
     6 
     7                                                                                                                                                                while(true){
     8 
     9                                                                                                                                                                         if(tick>0){
    10 
    11                                                                                                                                                                         System.out.println("sale:" + tick--);
    12 
    13                                                                                                                                                                         }
    14 
    15                                                                                                                                                                }
    16 
    17                                                                                                                                                            }
    18 
    19 }
    20 
    21 class TicketDemo{
    22 
    23                                                                                                                                                            public static void main(String[] args){
    24 
    25                                                                                                                                                                // Ticket t1 = new Ticket();
    26 
    27                                                                                                                                                                // Ticket t2 = new Ticket();
    28 
    29                                                                                                                                                                // Ticket t3 = new Ticket();
    30 
    31                                                                                                                                                                // Ticket t4 = new Ticket();
    32 
    33                                                                                                                                                                // t1.start();
    34 
    35                                                                                                                                                                // t2.start();
    36 
    37                                                                                                                                                                // t3.start();
    38 
    39                                                                                                                                                                // t4.start();
    40 
    41                                                                                                                                                                Ticket t = new Ticket();//
    42 
    43                                                                                                                                                                //记住,开启线程的只能是Thread类或者Thread类子类的对象
    44 
    45                                                                                                                                                                Thread t1 = new Thread(t);//
    46 
    47                                                                                                                                                                Thread t2 = new Thread(t);
    48 
    49                                                                                                                                                                Thread t3 = new Thread(t);
    50 
    51                                                                                                                                                                Thread t4 = new Thread(t);
    52 
    53                                                                                                                                                                t1.start();
    54 
    55                                                                                                                                                                t2.start();
    56 
    57                                                                                                                                                                t3.start();
    58 
    59                                                                                                                                                                t4.start();
    60 
    61  
    62 
    63                                                                                                                                                            }
    64 
    65 }

    创建线程的第二种方法,实现Runable接口

    步骤:

    1.定义类实现Runnable接口

    2.覆盖Runnable接口中的run()方法

    3通过Thread类建立线程对象

    4将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数

    5调用Thread类的start方法开启线程(让其调用Runnable接口子类的run()方法)。

    其中复写Runnable接口中的run()方法我们前文已经分析过了,将线程要运行的代码存放在该run()方法中。

    那么,为什么要将Runnable接口的子类对象传递给Thread的构造函数?

    因为,自定义的run()方法所属的对象是Runnable接口的子类对象,所以要让线程去执行指定对象的run()方法。就必须明确该run()所属对象。

    那么,创建线程的第二种实现方式和第一种继承方式有什么区别呢?(面试)

    实现方式好处:避免了单继承的局限性。

    在定义线程时,建议使用实现方式。

    继承Thread线程代码存放在Thread子类run()方法中

    实现Runnable,线程代码存放在接口的子类的run()方法中。

  • 相关阅读:
    Nginx学习总结(一)
    zabbix3.4.8配置自动发现主机并监控
    Windows server 2012/2016系统安装zabbix3.2客户端
    CentOS7.6系统安装zabbix3.4.8客户端
    一个小爬虫的整体解决方案
    如何通过一个立方体搭建一栋楼
    用Scrapy框架开发的一个爬虫项目
    寻找替代imagemin更好的插件
    原生和es6复杂数组去重的方法
    javascript关于对象或者数组深克隆的写法
  • 原文地址:https://www.cnblogs.com/devtrees/p/4395477.html
Copyright © 2011-2022 走看看