黑马程序员 学习日记(九)

来源:互联网 发布:部落冲突 女巫升级数据 编辑:程序博客网 时间:2024/05/06 04:50
---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------

Java中的多线程

每一个正在运行的程序称为进程,进程的一个最小运行单元就是线程,也是进程执行命令的一个途径,此途径可以是多个。开头先简单的用自己的语言去概括一下线程,多线程说的就是我们只启动一个程序,但是让它同时做多个事情,至少表面看起来是在处理多个任务。JVM本身在启动时就会启动至少两个线程,一个main主线程,一个垃圾回收机制线程。

1.创建线程

那我们光是看着它自动启动线程是不是不够爽,是不是也得自己来启动一个线程玩玩看呢?想要创建线程,使用Java提供的Thread类,下面是官方文档里提供的内容

创建新执行线程有两种方法。一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。接下来可以分配并启动该子类的实例。例如,计算大于某一规定值的质数的线程可以写成:


     class PrimeThread extends Thread {         long minPrime;         PrimeThread(long minPrime) {             this.minPrime = minPrime;         }          public void run() {             // compute primes larger than minPrime              . . .         }     } 

然后,下列代码会创建并启动一个线程:

     PrimeThread p = new PrimeThread(143);     p.start();

创建线程的另一种方法是声明实现 Runnable 接口的类。该类然后实现 run 方法。然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递并启动。采用这种风格的同一个例子如下所示:


     class PrimeRun implements Runnable {         long minPrime;         PrimeRun(long minPrime) {             this.minPrime = minPrime;         }          public void run() {             // compute primes larger than minPrime              . . .         }     } 

然后,下列代码会创建并启动一个线程:

     PrimeRun p = new PrimeRun(143);     new Thread(p).start(); 

每个线程都有一个标识名,多个线程可以同名。如果线程创建时没有指定标识名,就会为其生成一个新名称。

官方文档给出的内容已经非常详细彻底,我觉得我自己的语言再好也说不出比这个更多的内容了,所以这里就直接借用一下。

2.线程的运行

run()方法里面放着线程要执行的代码,主线程要运行的代码放在main方法里。
Thread t = new Thread() ;t.start() ;
如果是一个没有覆盖run()方法的线程将会什么都不做,而直接去调用run(),就相当于普通类的调用,仍然是主线程在运行。
class ThreadDemo extends Thread{public void run(){for (int i=0; i<20; i++){System.out.println("another thread is running!");}}}public class Demo {        public static void main(String[] args) {                ThreadDemo td = new ThreadDemo() ;                td.run();                                for(int i=0; i<20; i++)                {                System.out.println("main thread is running");                }        }}
以上代码,无论执行多少次,输出结果都如下所示:
another thread is running!another thread is running!another thread is running!another thread is running!..another thread is running!main thread is runningmain thread is running..main thread is running
而如果是用start()方法去启动线程,两个线程会争抢CPU资源,打印会无顺序的交替进行,且每次运行的顺序都不同。
运行多个线程的时候体会更明显。

class ThreadDemo extends Thread{private String name ;public ThreadDemo(String name){this.name = name ;}public void run(){for (int i=0; i<20; i++){System.out.println(name+ " thread is running!..." + i);}}}public class Demo {        public static void main(String[] args) {                ThreadDemo td1 = new ThreadDemo("first") ;                ThreadDemo td2 = new ThreadDemo("second") ;                td1.start();                td2.start();                                for(int i=0; i<20; i++)                {                System.out.println("main thread is running");                }        }
部分输出结果:
main thread is running
first thread is running!...0
first thread is running!...1
second thread is running!...0
second thread is running!...1
second thread is running!...2

因为线程的顺序是随机的,大家运行的结果跟我的不一样很正常,能看到三个线程交替运行了就足够了。
其实Thread类本身提供了一个获取线程对象和名称的方法getName(),把打印方法内的name替换为this.getName()就自动打印每一个线程的名字,默认的名字是Thread-编号,编号从0开始。
但你也许仍然想自己命名线程的名字,Thread提供了一个构造方法,所以我们可以这么做:
class ThreadDemo extends Thread{public ThreadDemo(String name){super() ;  //实现父类的构造方法}public void run(){for (int i=0; i<20; i++){System.out.println(this.getName()+ " thread is running!..." + i);}}}public class Demo {        public static void main(String[] args) {                ThreadDemo td1 = new ThreadDemo("first") ;                ThreadDemo td2 = new ThreadDemo("second") ;                td1.start();                td2.start();                                for(int i=0; i<20; i++)                {                System.out.println("main thread is running");                }        }
这样就保持了自定义的线程名。

3.关于线程的生命周期

创建线程——运行——(冻结)——结束
线程会经历这几个状态的转换,冻结并不一定会进行,没有人为的操作,自然过程没有冻结。当你想控制一个线程,运行线程一段时间后停一会,你将有两种选择,一个是sleep(),另一个是wait()。
sleep(time)就是睡眠,这个睡眠是定时的,后面加一个毫秒单位的时间(想睡眠5秒就写5000),时间到了自然就醒来了继续运行线程。
wait()就是明确的命令暂停,没有你的指示这个线程就会一直等下去,需要notify()方法去唤醒。所以wait()和notify()是一对好基友,你最好不要残忍的将他们分离。
run()方法结束了这个线程就结束了,正常是无需你额外的操作,但你非要再它运行一半或者它在死循环时枪毙它,执行stop()就可以立即结束线程。
以上都是说的理想状态的运行,实际上我们的电脑一直都在处理多个任务。你的线程经常需要排队。打个比喻,中午时间去食堂打饭,发现人很多,你就开始排队,排到你可以打一个菜,想打第二个菜食堂大妈告诉你再排一次队,这时候你很郁闷,不排了!坐在一边等,等一会发现人少了,赶紧去打饭吧!
线程遇到阻塞但是没有放弃打饭的权利在排队,这个状态没有在运行也不属于冻结更不是结束,处于一个临时状态,冻结就相当于我不排队了,你们谁爱排谁去我放弃了在一边等待,就告诉CPU我不找你 你也别找我。

总结

关于线程我能总结的就这么多,大家可以通过练习模拟火车票售票过程深入理解线程的概念(假设有四个窗口同时售票,票源唯一,现在卖的是同一天同一趟的列车,有一个窗口卖出一个座,其他三个窗口就不能再卖这个座),火车票模拟好了,线程也就都掌握了。
写学习日记,越来越感受到好处,一个是自己进行了复习,一个是有利于自己的编辑总结能力,还有以后忘记了回过头来可以看看,毕竟自己写的东西看起来更舒服。
大家互相学习互相进步
---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------
0 0