Java多线程技术初识——2. 线程的创建

下面通过具体的示例来进行多线程的讲解:

 

现有 100张火车票,分别有由 1~4 窗口来卖。

 

Java中提供了两种创建多线程的方式:

先用第一种方式来完成售票动作:

方式一:继承Thread类,覆盖run方法;

步骤:

1.     定义类覆盖Thread类;

2.     覆盖Thread类中的run方法;

3.     创建Thread类的子类对象创建线程对象;

4.     调用线程对象的start( )方法,开启线程。

代码实现:

package chapter1502;

public class Ticket extends Thread{
	//要卖的票
	private   int tickets = 100;
	private  String name ;
	//构造函数
	public Ticket(String n){
		name = n;
	}
	//线程任务
	public  void run(){
		while(true){
			if(tickets > 0)
				System.out.println(name + "  " + Thread.currentThread().getName() 
						+ "now tickets number is : " + tickets--);
		}
	}
}

package chapter1502;

public class SaleTicket {

	public static void main(String[] args) {
		
		//创建线程Thread子类对象
		Ticket t1 = new Ticket("小红");
		Ticket t2 = new Ticket("小明");
		Ticket t3 = new Ticket("小王");
		Ticket t4 = new Ticket("小李");
		//开启线程
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}


进过运行后,会发现出现了这样的问题:

  我们会发现,这100张票 被卖出去 了 4次,这样显然是不合理的。那么如何解决呢,我们可以将这100张票 标记为 static 完成数据共享,这样,就能保证 100张票 是被4 个窗口卖完,并且每张票只被卖出去了一次。

 

当然,Java还未我们提供了另外的一种创建线程的方式,他可以有效的解决这个问题:

方式二:

【线程的第二种创建方式】

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

2.     覆盖Runnable接口中的run方法。

             将线程要运行的代码存储到run()方法中。

3.     创建该接口的子类对象。

4.     通过Thread 类进行线程的创建,并将Runnable接口的子类作为Thread类的构造函数的实参进行传递。

5.     调用Thread类中的start()开启线程。

 

public class Ticket1 implements Runnable{
	//要卖的票
	private int tickets = 100;
	//线程任务
	public void run(){
		while(true){			
				if(tickets>0){
					System.out.println(Thread.currentThread().getName() + "    " + tickets--);
				}			
			}
		}
	}
}

package chapter1502;

public class SaleTicket2 {

	public static void main(String[] args) {
		//任务对象
		Ticket1 t1 = new Ticket1();
		//线程对象
		Thread td1 = new Thread(t1);
		Thread td2 = new Thread(t1);
		Thread td3 = new Thread(t1);
		Thread td4 = new Thread(t1);
		
		//开启线程
		td1.start();
		td2.start();
		td3.start();
		td4.start();
	}
}


那么这两种创建方式,运行结果不同的根源是什么呢?

在第一种方式中:

Ticket t1 = new Ticket("小红");

Ticket t2 = new Ticket("小明");

t1.start();

t2.start();

线程对象被创建了两次,并且每次创建出来后,都有各自的 100张票,当我们 开启线程后,实质上是 各自在卖自己的100张票。


第二种方式中:

Ticket1 t1 = new Ticket1();

       

Thread td1 = new Thread(t1);

Thread td2 = new Thread(t1);

       

td1.start();

td2.start();

 

线程对象同样被创建两次 , 但是,任务对象只被创建 1次,当开启线程后,实质上是 由两个线程来共同卖 这100张票。

 

至此,线程的两种创建方式介绍完毕。

 

 

 

这两种创建方式的区别:

第一种方式:继承自Thread类,这就会受到 Java 单继承这种局限性的影响,当我们如果再想扩展这个线程子类的功能时就会受到很大的限制。

第二种方式:实现 Runnable接口,则可以避免第一中创建方式的局限性。这样极大的降低了任务对象和 Thread对象的耦合性。

如下图所示:




下面介绍一下:这两种创建线程方式的小细节:

第一种创建方式的小细节:

 

【创建线程为什么要覆盖Thread并覆盖run方法?】

       线程要执行什么内容,线程自己有定义。这个定义就是run方法,这个方法中定义就是线程要运行的内容。但run()的内容并不是我们所需要的,只有覆盖Thread类中的run方法。覆盖之前需要先继承。

【注意】:

       主线程要运行的任务都在main函数中,自定义的线程要运行的任务都在run 方法中。run 方法就是专门用于存储线程任务代码的地方。

【如何让线程跑起来?】 

调用start( )方法,start( )方法做了两件事:

1.      开启线程让线程可以运行;

                        2.      调用run( )方法;

【调用run方法和调用start方法的区别】

1.      调用run ( )方法,仅仅是一般对象在调用对象中的方法,并没有开启线程,还是由主线程来完成run( )方法的运行。

2.      调用start( )方法,开启了一个线程(一个新的执行路径)这个线程去执行了run方法。

 

第二种创建方式的小细节:

【为什么要进行子类作为参数的传递呢?】

   让线程对象创建时,就要明确要运行哪个run方法,而这个run方法是需要被对象调用的,所以将run方法所属的对象传递给Thread类的构造函数。

 

回到主目录


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值