结合程序学习多线程的创建

        进程是由一个或多个“单细胞”组成的,这些单细胞即为线程。线程是程序的控制单元,它控制着进程的执行。

        使用计算机的人都知道,正在进行中的程序就是进程,提到进程,首先要了解线程。下面我们结合简单的程序来学习线程。

我们先看程序1

publicclass ThreadDemo {

      publicstaticvoid main(String[] args) {

            for(int y=0;y<40000;y++)

             {

                    System.out.println("Welcome to my blog."+y);

             }

      }

}

        我们启动任务管理器,然后编译运行这段程序,可以看到有个java.exe的进程一闪而过,这是怎么回事呢?

        进程的启动要在内存中分配空间,Java虚拟机启动的时候会有一个进程java.exe创建,执行完就消失了。这个程序这么简单,大家会不会以为这是个单线程呢?其实不然,该进程中至少有1个线程,负责java程序的执行,而且该线程运行的代码存在于main()方法中,称之为主线程。其次,java有一大特点就是它有垃圾回收机制,自动回收释放内存,大家看到进程的消失,在JVM中同时有负责垃圾回收机制的线程产生。所以这样一个简单的程序也不是单线程。

Windows本身是多任务的操作系统,我们看上去各程序是在同时运行,实际上是CPU快速地在各个进程间切换,这也是平常我们程序窗口开得越多,计算机运行越慢的原理。

        到这里我们问下,线程是谁生的呢?从哪儿来的啊?我们知道进程是Windows系统创建的,线程是依赖于进程存在的。

线程的创建有两种方法:一是继承Thead类,二是通过实现Runable接口。

我们看程序2

class XianChengextends Thread{

      publicvoid run()

      {

            for(int x=0;x<40;x++)

             {

                    System.out.println("XianCheng is run."+x);

             }

      }

}

publicclass ThreadForBlog {

      publicstaticvoid main(String[] args)

      {

             XianCheng xc =new XianCheng();

             xc.start();

            for(int y=0;y<40;y++)

             {

                    System.out.println("Welcome to my blog."+y);

             }

      }

}

        这是通过自定义代码并继承Thread类的方式来创建一个线程的。具体步骤可以总结为如下三步:

1、定义一个类并继承父类Thread

2、覆写Thread类中的run()方法;

3、调用start()方法来启动线程。

        什么是Thread类呢?它是Java中定义的对线程描述的类,具体定义在java.lang包。Java虚拟机允许应用程序并发地运行多个执行线程。

        我们运行这个程序,发现运行结果每次都不同。因为线程的运行是有优先级的,高优先级线程的执行优先于低优先级线程。这里每个线程都在获取CPU的执行权,CPU执行到谁,谁就先运行。

看到这里,提两个问题:一、为何要覆写run()方法?

在主函数中我们添加如下代码:

   Threadt =new Thread();//创建一个线程

t.start();//启动线程

        运行,看到没结果,是因为start()方法调用的run()方法,查阅API文档,run()方法中啥都没有。

        主线程的代码存在于main()方法中,这是虚拟机定义的,如果都定义在main方法中,那就只有一个主线程了。Thread类用于描述线程,线程要运行的话,是想让某些代码具有同时运行的效果,即多线程,Thread类中义了一个run()方法,用于存储线程要运行的代码。所以,覆写run方法主要是为了将自定义的代码存储在run()方法中,让子线程运行。

二、run()方法和start()方法有区别吗?

程序2中,我们修改代码如下:

   XianChengxc =new XianCheng();

      //xc.start();

xc.run();

        运行,我们看结果,运行N多遍都只有一种结果。这是为啥捏?是因为我们创建了一个线程,并未开启线程,仅仅是一般的对象调用,调用子类方法,执行结束后返回调用处,接着往下执行,不是我们想看到的多线程。事实上,start()方法是开启线程,并调用执行该线程的run()方法,而run()方法仅仅是封存了线程要运行的代码。二者功能是不同的。

我们看程序3

class TicketDemoextends Thread {

      privatestaticintticket = 100;

      publicvoid run() {

            while(true) {

                   if (ticket>0) {

                    System.out.println(Thread.currentThread().getName() +"tickets are saling : " +ticket--);

                    }

             }

      }

}

publicclass TicketsSaleDemo {

      publicstaticvoid main(String[] args) {            

            // TODO Auto-generated method stub

             TicketDemot1 = new TicketDemo();

            t1.start();

            t1.start();

            t1.start();

            t1.start();

      }

}

这是一个简单的卖票程序,我们编译运行,有结果但是有如下错误提示:

Exception in thread "main"java.lang.IllegalThreadStateException

      at java.lang.Thread.start(Unknown Source)

        这里我们为了仿真多个窗口卖票,创建一个票的对象t1后,多次调用start()方法,来启动线程,但是线程是有运行状态的,这里不细讲,已经运行的线程不需要多次开启,否则会提示线程状态出问题了,像这里的报错。

        另外大家都知道Java是单继承机制,那我们创建一个子类继承自Thread类,那肯定就不能再有其他的“父亲”了,这个情况是不符合应用的,那我们此时就可以通过第二种创建线程的方法,实现Runable接口来创建线程。

我们看程序4

class TicketDemoimplements Runnable {

      privatestaticintticket = 100;

      publicvoid run() {

            while(true) {

                   if (ticket>0) {

                    System.out.println(Thread.currentThread().getName() +"tickets are saling : " +ticket--);

                    }

             }

      }

}

publicclass TicketsSaleDemo {

      publicstaticvoid main(String[] args) {            

            // TODO Auto-generated method stub

            TicketDemo td = newTicketDemo();

             Thread t1 =new Thread(td);

             Thread t2 =new Thread(td);

             Thread t3 =new Thread(td);

             Thread t4 =new Thread(td);

             t1.start();

             t2.start();

             t3.start();

             t4.start();

      }

}

        这是通过创建一个子类并实现Runable接口的方式来创建一个线程的。具体步骤可以总结为如下五步:

1、定义一个类并实现Runnable接口;

2、覆写Runnable接口中的run()方法;

3、通过Thread类建立线程对象;

4、Runnable接口的子类对象传递给Thread类的构造函数;

5、调用Thread类的start()方法开启线程。

        我们运行类,看到了多线程卖票的结果,也解决了只能继承Thread类的局限性。

        从步骤上我们能看到实现接口的方式要将Runnable接口的子类对象传递给Thread类才能生效,这是因为自定义的run方法所属的对象是Runnable接口的子类对象,所以要让线程运行指定对象的run方法,就必须明确该run方法的所属对象。

        综上二种方法,了解实现接口方式和继承方式的区别,更方便对创建线程驾驭自如,二者的主要区别就是run方法的位置,一个放在Thread子类中,一个放在接口的子类中,这点一定要分清楚。至于应用,请关注下回分解。O(_)O谢谢!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山西茄子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值