java(多线程)的实现与Thread类、runnable接口详细分解

什么是线程?

线程是程序中执行的线程。 Java虚拟机允许应用程序同时执行多个执行线程。 每个线程都有优先权。
具有较高优先级的线程优先于优先级较低的线程执行。 每个线程可能也可能不会被标记为守护程序。
当在一些线程中运行的代码创建一个新的Thread对象时,新线程的优先级最初设置为等于创建线程的优先级,并且当且仅当创建线程是守护进程时才是守护线程。

当Java虚拟机启动时,通常有一个非守护进程线程(通常调用某个指定类的名称为main的方法)。
Java虚拟机将继续执行线程,直到发生以下任一情况:

该exit类的方法Runtime已经被调用并且安全管理器允许退出操作发生。
不是守护进程线程的所有线程都已经死亡,无论是从调用返回到run方法还是抛出异常,传播超出了run方法。
java中主方法是单线程

方式一:
创建一个新的执行线程有两种方法。 一个是将一个类声明为一个Thread的子类。 这个子类应该重写run类的方法Thread 。 然后可以分配并启动子类的实例。

package jicheng;
//一个是将一个类声明为一个Thread的子类。 
//这个子类应该重写run类的方法Thread 。 然后可以分配并启动子类的实例。
public class fuzhi extends Thread{
	@Override
	public void run() {
		// TODO Auto-generated method stub
		for (int i = 0; i < 100; i++) {
			System.out.println(" "+ i);
		}
	}
	public static void main(String[] args) {
//		创建线程实例
		fuzhi set = new fuzhi();
//		设置线程名字
		set.setName("老王");		
//		启动线程
		set.start();
//		start方法会调用run()方法(线程调用了一个run()方法)
	}
}
```java
package shipin;

public class DuoXianCheng extends Thread{
	@Override
	public void run() {
		// TODO Auto-generated method stub
		super.run();
		//输出当前线程的名字
		System.out.println("显示当前的线程名是:"+ Thread.currentThread().getName());
		for (int i = 0; i < 1000; i++) {
			System.out.println("线程run方法中为:"+ i);
		}
	}
	public static void main(String[] args) {
		System.out.println("线程main方法中为:"+ Thread.currentThread().getName());
		//Thread代表线程
		DuoXianCheng str = new DuoXianCheng();
		str.start();
		for (int i = 0; i < 1000; i++) {
			System.out.println("线程main方法中为:"+ i);
		}
	}
		
}

方式二:

Runnable接口应由任何类实现,其实例将由线程执行。 该类必须定义一个无参数的方法,称为run 。
该接口旨在为希望在活动时执行代码的对象提供一个通用协议。 例如, Runnable由类Thread实现。
活跃的只是意味着一个线程已经启动,还没有被停止。

此外, Runnable提供了一个类是活动的手段,而不是子类化Thread 。
实现Runnable类Thread通过实例化一个Thread实例并将其自身作为目标来运行,而无需子类化Thread 。
在大多数情况下,如果您仅计划覆盖run()方法,而不使用其他Thread方法,则应使用Runnable接口。
这是重要的,因为类不应该被子类化,除非程序员打算修改或增强类的基本行为。

创建线程的另一种方法是声明一个实现Runnable接口的类。 那个类然后实现了run方法。 然后可以分配类的实例,在创建Thread时作为参数传递,并启动。

package shipin;

public class DuoXianCheng implements Runnable{

	@Override
	public void run() {
		for (int i = 0; i < 1000; i++) {
			System.out.println("run方法中的循环"+i);
		}
	}
	public static void main(String[] args) {
		DuoXianCheng rd = new DuoXianCheng();
		Thread td = new Thread(rd);
		td.start();
		for (int i = 0; i < 1000; i++) {
			System.out.println("main方法中的循环"+i);
		}
	}
		
}

问题如下:

1.Runnable为什么不可以直接run

如果问出这个问题,代表您对Java的线程基础知识以及多线程理念还不太熟悉或者说并没有真正理解,这里做简单阐述。

多线程原理:相当于玩游戏机,只有一个游戏机(cpu),可是有很多人要玩,于是,start是排队!等CPU选中你就是轮到你,你就run(),当CPU的运行的时间片执行完,这个线程就继续排队,等待下一次的run()。

调用start()后,线程会被放到等待队列,等待CPU调度,并不一定要马上开始执行,只是将这个线程置于可动行状态。然后通过JVM,线程Thread会调用run()方法,执行本线程的线程体。先调用start后调用run,这么麻烦,为了不直接调用run?就是为了实现多线程的优点,没这个start不行。

1.start()方法来启动线程,真正实现了多线程运行。这时无需等待run方法体代码执行完毕,可以直接继续执行下面的代码;通过调用Thread类的start()方法来启动一个线程,
这时此线程是处于就绪状态, 并没有运行。 然后通过此Thread类调用方法run()来完成其运行操作的,
这里方法run()称为线程体,它包含了要执行的这个线程的内容, Run方法运行结束, 此线程终止。然后CPU再调度其它线程
2.run()方法当作普通方法的方式调用。程序还是要顺序执行,要等待run方法体执行完毕后,才可继续执行下面的代码; 程序中只有主线程——这一个线程, 其程序执行路径还是只有一条, 这样就没有达到写线程的目的

解释到这里,相信各位看官心里有种“了然大明白”的感觉,runnable其实相对于一个task,并不具有线程的概念,如果你直接去调用runnable的run,其实就是相当于直接在主线程中执行了一个函数而已,并未开启线程去执行,所以显而易见,如果你在代码中直接通过这种方式run了一个runnable,明显您的程序的主线程就直接悲催了,各种资源不足现象的崩溃日志会接踵而至,而且遇到这种问题的时候,如果没有一定的研发经验和坚持,很有可能采取错误的解决策略。

2.Runnable和Thread相比优点有:

(1)由于Java不允许多继承,因此实现了Runnable接口可以再继承其他类,但是Thread明显不可以
(2)Runnable可以实现多个相同的程序代码的线程去共享同一个资源,而Thread并不是不可以,而是相比于Runnable来说,不太适合,具体原因文章中有。
2.Runnable为什么不可以直接run 阐述文章中已有,Runnable其实相对于一个Task,并不具有线程的概念,如果你直接去调用Runnable的run,其实就是相当于直接在主线程中执行了一个函数而已,并未开启线程去执行,带来的“灾害”文章中有。

此文部分内容摘自简书
如果还有其他的问题,请评论区留言,共同学习,共同进步!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Ctrl精

面试很多问题,积攒不容易

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

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

打赏作者

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

抵扣说明:

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

余额充值