首页 > 编程语言 > 详细

Java 多线程

时间:2020-01-16 23:17:02      阅读:72      评论:0      收藏:0      [点我收藏+]


 

为了方便理解多线程的概念,我们先举一个例子:

     假如我们把公司看做是一个进程,那么人就是其中的线程。进程必须得有一个主线程,公司在创业初期往往可能出现一人打天下的现象,但是,至少得有一个人,公司才能运作。公司创业初期,业务还不算太多,往往就是老板一个人身兼数职,一天如果只有1、2趟活儿,应该还是忙得过来的。时间长了,随着业务的发展、口碑的建立,生意越来越兴隆,一个人肯定就忙不过来了。假设一天有5个活儿,老板一个人必须搬完A家才能搬B家,搬到黄昏估计也就搬到C家,D和E家都还在焦急地等待着呢。老板一个人要充当搬运工、司机、业务联系人、法人代表、出纳等众多角色,累死累活公司的规模也上不去,人手不够制约了公司的发展。那么怎么办,很简单,增加人手,然后这些人各司其职,同时工作,很块就处理完了公司的业务。而这些人手就是所谓的线程,开启了的线程可以并行运行

一,进程和线程的概念

       进程:是一个正在执行的程序。每一个进程都有一个执行顺序,该顺序是一个执行路径,或者叫做执行单元。

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

       (一个进程至少有一个线程)

二,自定义线程的两种方式:一种是继续Thread类,另外一种是实现Runable接口。

   第一种:继承Thread类

           步骤:       1.定义类继承Thread类

            2.重写Thread类中的run方法

                                   为什么要重写run()方法? 将新定义的线程所要执行的代码存储在run()方法中,因为Thread类是用来描述线程的,而用于存储该线程运行时的代码的功能,就是由run()方法实现的,所以一般将新建的线程所要执行的代码,都放到run()方法中。注意主线程运行时的代码是存储在main()方法中的。(一般新建立的线程调用的start()方法,是由父类继承下来的,而start()方法中调用的run()方法,是被Thread子类重写过的方法。

         3.调用线程的start()方法(该方法的作用:启动线程,并调用run方法)

 

 1 class Demo extends Thread//1.继承Thread类
 2 {
 3     public void run() { //2.重写run方法
 4         for(int i=0;i<10;i++){
 5             System.out.println("run..."+i);
 6         }
 7     }
 8 }
 9 public class TreadDemo {
10     public static void main(String[] args) {//main函数也是一个线程
11         Demo d=new Demo();//创建一个线程
12         d.start();//3.调用start方法
13         for(int i=0;i<20;i++){
14             System.out.println("main...."+i);
15         }
16     }
17 }

 

运行结果部分图:

技术分享图片

 

 

 运行结果分析:

     主线程(要执行代码存储在main()函数中)开始执行,当运行到这一句Demo d=new Demo();   会新创建一个线程2,接着主线程调用新线程的start()方法,而start()方法内部又会区调用run()方法(这个新线程要执行的代码存储在run()方法中),此时新线程也开启了。而这一块打印结果为什么是main和run 的交替?因为当线程2开启后,只能说明线程2具备了运行条件,不一定立马就有cup的执行权,所以打印的结果先是main.....i  ,这时线程2突然抢到了cup执行权,于是也进行了打印输出run.....i ,接着cup执行权又被主线程强走,然后打印main.....i ,所以打印结果就是他们的交替。下图是对上述代码执行过程的分析,要注意的是一个进程在开始至少有一个线程,而对于上面这个代码,刚开始的这个主线程就是由main()函数开启的。而且只要进程中还有线程未执行完毕,该进程就不会结束。

技术分享图片

          说到这一块可能就有人迷惑,那既然调用start()方法时,该方法会接着调用run()方法,那为什么不能直接调用run()方法来执行呢?要注意start()方法的作用不止调用run()方法,还用启动线程的作用。

先举一个直接调用run()方法的例子:

1    public static void main(String[] args) {//main函数也是一个线程
2         Demo d=new Demo();//创建一个线程
3         d.run();//3.调用start方法
4         for(int i=0;i<20;i++){
5             System.out.println("main...."+i);
6         }
7     }

      run()方法中的代码与上个例子中的相同。

 执行结果:

技术分享图片

 

 

     不论运行多少次,都会发现结果和上图都是一样的。这和线程的随机性明显不符,为什么呢??(线程的随机性指的是多个线程的执行结果不唯一,谁抢到cup执行权谁执行)

      因为当你直接调用run()方法时,虽然通过该句Demo d=new Demo()已创建新线程,但是并没有启动新线程!! 所以当主线程执行到这一句d.run(),因为新线程没有开启,所以run()方法中的内容是在主线程中执行了的,所以只有当run打印完,才会轮到main打印。 

 

     要注意,人们平时看到的多个线程在“同时”执行,其实在底层并不是多个线程同时一块执行的,而是通过快速的交替使用cup来执行自己的任务,因为其交替的速度非常快,快到人眼是感觉不到的,所以使我们在表面上看去,以为是多个线程在同时执行。这也就是为什么当我们电脑打开的程序也多时,电脑就会越卡。

补充:可通过Thread的getName()方法获得新线程的默认名字。 新线程的默认名字格式:Thread-0   编号从0开始。

//两种获得线程名字的方法
this
.getName(); Thread.currentThread().getName();

Thread.currentThread()//可获得当前线程对象

    那如何给新线程自定义名字呢?通过查资料,我们得知Thread有一个带参构造函数,所以我们可以直接在新建线程时,直接将名字赋给它。

      Demo d=new Demo("one");//创建一个线程

              要注意我们既然要使用它的带参构造函数,那么我们在子类中就必须定义一个带参构造函数。

 1 class Demo extends Thread//1.继承Thread类
 2 {
 3     Demo(String name){ //定义一个带参构造函数。
 4         super(name);
 5     }
 6     public void run() { //2.重写run方法
 7         for(int i=0;i<10;i++){
 8             System.out.println(this.getName()+"run..."+i);
 9         }
10     }
11 }
12 public class TreadDemo {
13     public static void main(String[] args) {//main函数也是一个线程
14         Demo d=new Demo("one");//创建一个线程
15         d.start();//3.调用start方法d.run()
16         for(int i=0;i<20;i++){
17             System.out.println("main...."+i);
18         }
19     }
20 }

 

 

   第二种:实现Runable接口(其实Thread也是实现Runnable接口的

       步骤:    1.定义类实现Runnable接口

                      2.重写Runnable接口中的run()方法

                          目的:将线程执行的代码存储在run()方法中

      3.通过Thread类建立线程对象

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

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

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

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

 

    实现方式和继承方式的区别:

       1,实现方式避免了单继承的局限性。(因为一个类只能继承一个类,当继承了Thread类就无法在继承其他类,但因为实现多个接口,所以就可以继承其他类和实现其他接口。)

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

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

 1  class Demo implements Runnable{// 1.定义类实现Runnable接口 
 2      public void run() { //2.重写run方法 3          for(int i=0;i<100;i++){
 4              System.out.println(Thread.currentThread().getName()+"run..."+i);
 5     }
 6  }
 7  public class TreadDemo {
 8      public static void main(String[] args) {//main函数也是一个线程
 9          Demo d=new Demo();
10          Thread t1=new Thread(d);//3.通过Thread类建立线程对象     4.将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数
11          Thread t2=new Thread(d);
12          t1.start();//5.调用Thread类的start方法开启线程并调用Runnable接口子类的run()方法
13          t2.start();
14          for(int i=0;i<200;i++){
15              System.out.println("main...."+i);
16         }
17     }
18 }

 

Java 多线程

原文:https://www.cnblogs.com/ljl150/p/12203098.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!