多线程__【多线程的概念】【创建线程】【线程的状态】


【基本概念】

线程,就是进程中的执行路径,一个进程中至少有一个线程,可以有多个线程,称为多线程

开启多个线程是为了同时运行多部分代码,使CPU更精明地工作提高CPU尤其是多核CPU的利用率。应用程序的执行是CPU在做快速的随机切换完成的,所以同时开启的线程太多会导致单个线程的执行效率低下

Java虚拟机允许应用程序并发运行多个执行线程,多线程的堆空间是共享的,栈空间是独立的,

finalize()方法,Object类的方法,运行垃圾回收器时会被调用

 gc() System类的方法,运行垃圾回收器,在方法中调用finalize()方法


代码1

class A extends Object
{
	public void finalize()
	{
		System.out.println("A");
	}
}
class Demo
{
	public static void main(String[] args )
	{
		new A();
		new A();
		new A();
		System.gc();//
		System.out.println("Hello World!");
	}
}
运行结果:


两次运行结果的不一致证明:

     1,主函数线程的结束并不意味着虚拟机的结束
     2,jvm虚拟机中有多条线程(
主线程、垃圾回收线程

     3,CPU多线程的执行具有随机性

单线程的局限性,当前任务执行不完就无法执行下一条任务,一旦进程发生等待或者死锁,程序就无法向下执行,多线程的出现解决了这一问题,随机分配CPU资源,当前线程执行不动,就执行另一条线程,提高程序响应


【创建线程对象】

Thread() 是java中用于创建线程对象的类,拥有多个构造函数,可以确定的是,每创建一个线程对象就new一次Thread类的构造函数

创建线程的目的是为了开启一条执行路径,多任务并发执行指定的代码所以需要继承和覆盖才能将指定代码定义在run方法中。jvm创建的主线程定义在主函数中;自定义的线程定义在Thread类的run方法中

Thread类用于描述线程,而线程是需要任务的,所以Thread类也是对任务的描述,这个任务通过Thread类中的run方法来体现,run方法就是封装自定义线程任务的函数,方法中定义就是线程要运行的任务代码。

 创建线程的两种方法,

继承Thread类

步骤:

继承thread类,重写run()方法

创建子类线程对象

创建空间启动线程

start()的功能:1使线程开始执行 2)虚拟机调用run方法

 【细节分析

调用Threadrunstart的区别

run方法仅仅是封装了要执行的任务,是线程体,并不能启动线程,如果而如果直接用Run方法,这只是主线程在调用一个含线程对象的方法而已,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,这样就没有达到写线程的目的。

start()方法会启动一个线程,开辟一条新的栈内存空间;此时线程处于就绪状态,并没有运行;在方法体中会调用Thread类的run方法,然后run方法被子类覆盖,最终才开始执行自定义任务

代码2调用run方法的运行结果:



获取线程名称的操作

getName(),获取线程对象的名称,只是获取对象的名字

currentThread()获取当前线程对象

currentThread().getName() 在一起才是获取当前线程对象的名字

Thread特有方法;如果要获得任何当前线程的名称,需要在前面加Thread;即Thread.currentThread().getName()


代码2

class A extends Thread
{
	String name;
	A(String name)//
	{
		super(name);//Thread中有含String参数的构造函数,直接覆盖就可以了,给线程命名
	}
	public void run()//覆盖Thread中的run方法
	{
		for (int x=0;x<5 ;x++ )
		{
			System.out.println(x+"...name: "+currentThread().getName());
		}
	}
}
class Demo
{
	public static void main(String[] args )
	{
		A a1 =new A("肥皂");
		A a2 =new A("xiaoqiang");
		a1.start();
		a2.start();
		System.out.println("Hello World!"+"......."+Thread.currentThread().getName());
	}
}

运行结果:多线程运行的随机性;主函数线程名称为 main



内存的特性是先进后出,弹栈压栈的过程就像是步枪的弹夹,在单线程中主函数先进栈,函数内方法后进栈,方法执行完毕后弹栈,主函数继续执行,最下面的hello world 输出语句在最后执行。多线程hello world先输出了,说明开辟线程就是开辟了一个新的执行路径,即新的栈内存空间

多线程中的异常单条线程的异常并不影响其他线程的执行,只有进程中所有线程都终止了,JVM虚拟机进程才会退出

线程之间的运行互不影响

创建线程的第二种方法:实现Runnable()接口

场景:子类只能继承一个父类,多继承的局限性。通过Runnable接口来解决。

而Runnable接口只有一个抽象的run()方法,子类实现run方法只是封装了任务,并没有创建线程对象的能力,线程的创建还需要Thread类的构造函数来完成

Thread()类中构造函数参数列表为Runnable接口类型,即Thread(Runnable target);target指run方法被调用的对象

步骤:

要执行的任务所在类实现Runnable接口。

创建该类的对象。

将该对象作为Thread()类的参数传入,用来创建线程对象

调用start()方法启动线程

代码3

class A implements Runnable
{
	public void run()
	{
		show();
	}
	public void show()
	{
		for (int x=0;x<5 ;x++ )
		{
			System.out.println(x+"...name: "+Thread.currentThread().getName());
		}
	}
}

class Demo
{
	public static void main(String[] args)
	{
		A a= new A();
		Thread t1 = new Thread(a);//A实现了Runnable接口,所以a是属于Runnable接口类型的参数
		Thread t2 = new Thread(a);//明确线程要执行的任务
		t1.start();
		t2.start();
	}
}


个人总结:【ThreadRunnable的细节】

两种方法任务对象的传递过程:

继承Thread 方法创建线程的 任务传递过程比较简单,就是将任务直接封装在线程内,通过run方法的覆盖来执行。下面重点说一下Runnable接口方法

首先要认识Thread类,JDK源码中Thread实现了Runnable接口,及其run方法并在成员中有Runnable接口类型的引用

Thread类中有多个构造函数,它们的参数列表也各不相同,其中有一个构造函数Thread(Runnable target),将封装了任务的对象作为值来传入,然后通过Thread的构造函数“二次封装”,实现创建线程对象的目的创建的线程对象就是为任务对象服务的。

这两种方法最大的区别是

继承Thread方法每创建一次线程对象和任务对象是同一个对象,每创建一次线程就创建一次任务,只能一条线程执行一个任务,违背了多线程的初衷

实现Runnable接口的线程对象和任务对象是分开的,可以创建多个线程对象为同一个任务服务。所以Runnable方法比较常用


代码4

class Thread implements Runnable//Thread类实现了Runnable接口
{
	private Runnable target;//Runnable 接口类型的引用作为参数

	Thread ()//空参构造函数
	{}

	Thread (Runnable target)//实参构造函数
	{
		this.target=target;//将任务对象转化为线程对象
	}

	public void run()//无论哪种情况,Thread中run方法都会被覆盖
	{
		if(target!=null)
		target.run();//任务对象作为线程对象来调用本类方法
	}

	public void start()
	{
		//先开辟线程
		run();
	}
}

class Sub2 implements Runnable//重点看任务对象的传递过程
{
	public void run()
	{
		System.out.println("Hello Runnable");
	}
}
//		主函数中
//		Sub2 s = new Sub2();
//		Thread t1 = new Thread(s);
//		Thread t2 = new Thread(s);
//		t1.start();
//		t2.start();

class Sub extends Thread//继承的传递过程比较简单,就是简单的覆盖
{
	public void run()
	{
		System.out.println("Hello Thread");
	}
}
//	主函数中
//	Sub s1 = new Sub();
//	Sub s2 = new Sub();
//		s1.start()
//		s2.start()


线程的状态

图解:



new()  初始化,在堆内存创建对象空间

start()   就绪状态,开辟执行路径,即新的栈内存空间

sleep(time)休眠Thread类的静态方法,会抛出异常

wait() 等待

notify()  唤醒

stop()  线程终止后,只能被回收,不能被挂起


临时阻塞状态具备执行资格,还不具备执行权,单核CPU执行多线程任务时,一次只能执行一个,这是其他“同时”执行的线程就处于临时阻塞状态





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值