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

---------------------- 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 running
main 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值