您的位置:首页 > 其它

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

2013-01-31 22:57 218 查看
进程是由一个或多个“单细胞”组成的,这些单细胞即为线程。线程是程序的控制单元,它控制着进程的执行。

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

我们先看程序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谢谢!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: