Java并发编程(二)-Thread类、Runnable接口与多线程原理

1、Thread线程类

Java使用 java.lang.Thread 类代表线程,所有的线程对象都必须是Thread类或其子类的实例。每个线程的作用是 完成一定的任务,实际上就是执行一段程序流即一段顺序执行的代码。Java使用线程执行体来代表这段程序流。
构造方法

  • public Thread() :分配一个新的线程对象。
  • public Thread(String name) :分配一个指定名字的新的线程对象。
  • public Thread(Runnable target):分配一个带有指定目标新的线程对象。
  • public Thread(Runnable target,String name) :分配一个带有指定目标新的线程对象并指定名字。
    常用方法
  • public String getName():获取当前线程名称。
  • public void start() :导致此线程开始执行; Java虚拟机调用此线程的run方法。
  • public void run() :此线程要执行的任务在此处定义代码。
  • public static void sleep(long millis) :使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
  • public static Thread currentThread() :返回对当前正在执行的线程对象的引用。
package com.zhukun;
class MyThread extends Thread { 
	public void run()
	{ 
		for (int i = 0; i < 20; i++) 
		{ 
			System.out.println("run:"+i); 
		}
	} 
}
class test { 
	public static void main(String[] args) 
	{ 
		//创建自定义线程对象 
		MyThread mt = new MyThread(); 
		//开启新线程 
		mt.start();
		for (int i = 0; i < 20; i++)
		{
			System.out.println("main:"+i); 
		}
	} 
}

在这里插入图片描述

2、多线程实现与原理分析

Java中通过继承Thread类来创建并启动多线程的步骤如下

  1. 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把 run()方法称为线程执行体。
  2. 创建Thread子类的实例,即创建了线程对象
  3. 调用线程对象的start()方法来启动该线程
package com.zhukun;
class MyThread extends Thread { 
	//定义指定线程名称的构造方法 
	public MyThread(String name) { 
		//调用父类的String参数的构造方法,指定线程的名称 
		super(name);
		}
	/*** 重写run方法,完成该线程执行的逻辑 */ 
	@Override 
	public void run()
	{ 
		for (int i = 0; i < 20; i++) 
		{ 
			System.out.println(getName()+":正在执行!"+i); 
		}
	} 
}
class test { 
	public static void main(String[] args) 
	{ 
		System.out.println("这里是main线程");
		//创建自定义线程对象 
		MyThread mt = new MyThread("小强"); 
		//开启新线程 
		mt.start();
		for (int i = 0; i < 20; i++)
		{
			System.out.println("旺财:"+i); 
		}
	} 
}
这里是main线程
旺财:0
旺财:1
小强:正在执行!0
小强:正在执行!1
小强:正在执行!2
旺财:2
小强:正在执行!3
旺财:3
小强:正在执行!4
旺财:4
小强:正在执行!5
旺财:5
小强:正在执行!6
小强:正在执行!7
小强:正在执行!8
小强:正在执行!9
旺财:6
小强:正在执行!10
旺财:7
旺财:8
旺财:9
旺财:10
旺财:11
旺财:12
旺财:13
旺财:14
旺财:15
小强:正在执行!11
旺财:16
小强:正在执行!12
旺财:17
旺财:18
旺财:19
小强:正在执行!13
小强:正在执行!14
小强:正在执行!15
小强:正在执行!16
小强:正在执行!17
小强:正在执行!18
小强:正在执行!19

流程图:
在这里插入图片描述
程序启动运行main时候,java虚拟机启动一个进程,主线程main在main()调用时候被创建。随着调用mt的对象的 start方法,另外一个新的线程也启动了,这样,整个应用就在多线程下运行。 通过这张图我们可以很清晰的看到多线程的执行流程,
那么为什么可以完成并发执行呢?
我们再来讲一讲原理。 多线程执行时,到底在内存中是如何运行的呢?以上个程序为例,进行图解说明:
多线程执行时,在栈内存中,其实每一个执行线程都会开辟一片自己所属的栈内存空间。进行方法的压栈和弹栈。
在这里插入图片描述
分析:先执行main()方法,将main()方法压栈执行,在main()里创建线程对象在堆中开辟对象地址,当线程对象执行start()方法时将start()方法压栈,执行时start()方法时弹栈并开辟新的栈空间,在新的栈空间中将线程的run()方法进行压栈执行。
此时CPU就有了选择的权利可以继续执行main()方法中的for循环,也可以执行新线程中的run()方法。也就是说:此时的两个线程:一个main()线程,一个新线程一起抢夺CPU的执行权(执行时间),谁抢到了谁就执行对应的代码。
因为多个线程是在不同的栈内空间,所以彼多个线程之间并不产生影响,也就是说如果一个线程出现异常挂了,其他线程仍可继续执行

3、实现Runnable接口创建线程

实现java.lang.Runnable 接口创建线程也是非常常见的一种方式,我们只需要重写run方法即可。
步骤如下:

  1. 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体
  2. 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正 的线程对象。
  3. 调用线程对象的start()方法来启动线程
class MyRunnable implements Runnable{ 
	@Override 
	public void run() { 
		for (int i = 0; i < 20; i++) 
		{ 
			System.out.println(Thread.currentThread().getName()+" "+i); 
		} 
	} 
}
class test { 
	public static void main(String[] args) 
	{ 
		//创建自定义线程对象 
		MyRunnable mr = new MyRunnable(); 
		//创建线程对象,mr作为Thread的target来创建Thread对象
		Thread t = new Thread(mr, "小强");
		t.start(); for (int i = 0; i < 20; i++) 
		{ 
			System.out.println("旺财 " + i); 
		}
	} 
}

通过实现Runnable接口,使得该类有了多线程类的特征。run()方法是多线程程序的一个执行目标。所有的多线程 代码都在run方法里面。Thread类实际上也是实现了Runnable接口的类。 在启动的多线程的时候,需要先通过Thread类的构造方法Thread(Runnable target) 构造出对象,然后调用Thread 对象的start()方法来运行多线程代码。 实际上所有的多线程代码都是通过运行Thread的start()方法来运行的。因此,不管是继承Thread类还是实现 Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的,熟悉Thread类的API是进行多线程 编程的基础。

Runnable对象仅仅作为Thread对象的target,Runnable实现类里包含的run()方法仅作为线程执行体。 而实际的线程对象依然是Thread实例,只是该Thread线程负责执行其target的run()方法

4、 Thread和Runnable的区别

如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。
总结:
实现Runnable接口比继承Thread类所具有的优势:

  1. 适合多个相同的程序代码的线程去共享同一个资源。
  2. 可以避免java中的单继承的局限性。
  3. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
  4. 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。

在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用 java命令执行一个类的时候,实际上都会启动一个JVM,每一个JVM其实在就是在操作系统中启动了一个进 程。

5、匿名内部类方式实现线程的创建

把子类继承父类,重写父类的方法,创建子类对象合一步完成;把实现类实现类接口,重写接口中的方法,创建实现类对象合一步完成

class test { 
	public static void main(String[] args) 
	{ 
		//new MyThread().start();
		new Thread(){ 
			//重写run方法
			public void run(){ 
				for (int i = 0; i < 20; i++) 
				{ 
					System.out.println(Thread.currentThread().getName()+"小强"+i); 
				} 				
			} 
		}.start();; 
		//线程接口Runnable
		Runnable r = new Runnable()
		{ 
			public void run()
			{ 
				for (int i = 0; i < 20; i++) 
				{ 
					System.out.println(Thread.currentThread().getName()+"小张"+i); 
				} 
			} 
		};
		new Thread(r).start();	
	}
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值