Java多线程实现

 

Java实现多线程的两种途径

  • 继承Thread类
  • 实现Runnable接口(Callable)接口

一、继承Thread类

Thread类是一个支持多线程的功能类,只要有一个子类它就可以实现多线程的操作

class MyThread extends Thread{ //这就是一个多现成的操作类 
}   

所有程序的起点是main()方法,但是所有的线程一定要有自己的起点,那就是run()方法,也就是说在多线程主体类,每个主题类之中必须覆写Thread类中提供的run()方法。

public void run(){}

这个方法上没有返回值,表示线程一旦开始就要一直执行,不能够返回内容

class MyThread extends Thread{    //这就是一个多线程的操作类
	private String name;    //定义类中的属性
	public MyThread(String name) {    //定义构造方法
		this.name = name;
	}
	
	@Override
	public void run() {    //覆写run()方法作为线程的主体操作方法
		for(int x = 0; x < 200 ; x++) {
			System.out.println(this.name + "-->" + x);
		}
	}
}

本现场功能是进行循环的输出操作,所有的线程与进程是一样的,都必须轮流去抢占资源,多线程的执行应该是多个线程彼此交替执行,也就是说如果直接调用了run()方法,并不能直接启用线程,启用线程是Thread类中的start()方法

public void start() // 调用此方法执行的方法体是run()方法

 执行代码如下:

public class TestDemo {

	public static void main(String[] args) {
		MyThread mt1 = new MyThread("线程A");
		MyThread mt2 = new MyThread("线程B");
		MyThread mt3 = new MyThread("线程C");
		
		mt1.start();
		mt2.start();
		mt3.start();
	}
}

此时每一个线程对象交替执行

此样例的全部代码如下:

class MyThread extends Thread{//这就是一个多线程的操作类
	private String name;//定义类中的属性
	public MyThread(String name) {//定义构造方法
		this.name = name;
	}
	
	@Override
	public void run() {//覆写run()方法作为线程的主体操作方法
		for(int x = 0; x < 200 ; x++) {
			System.out.println(this.name + "-->" + x);
		}
	}
}

public class TestDemo {

	public static void main(String[] args) {
		MyThread mt1 = new MyThread("线程A");
		MyThread mt2 = new MyThread("线程B");
		MyThread mt3 = new MyThread("线程C");
		
		mt1.start();
		mt2.start();
		mt3.start();
	}
}

疑问?为什么多线程的启动不是调用run()而是调用start()?

使用Thread类的start()方法不仅仅要启动多线程的执行代码,还要根据不同的操作系统进行资源的分配

二、实现Runnable接口

虽然Thread类可以实现线程的主体类定义,但是它还有一个问题,Java具有单继承局限,正因为如此在任何情况下针对于类的继承都应该回避的问题,那么多线程也一样,为了解决单继承的限制,在Java里边专门提供了Runnable接口,此接口定义如下

@FunctionalInterface
public interface Runnable{
	public void run();
}

在接口里面任何的方法都是public定义的权限,不存在默认的权限

只需要让一个类实现Runnable接口即可,并且也需要覆写run()方法。

class MyThread implements Runnable{//这就是一个多线程的操作类
	private String name;//定义类中的属性
	public MyThread(String name) {//定义构造方法
		this.name = name;
	}
	
	@Override
	public void run() {//覆写run()方法作为线程的主体操作方法
		for(int x = 0; x < 200 ; x++) {
			System.out.println(this.name + "-->" + x);
		}
	}
}

与继承Thread类相比,此时的MyThread类在结构上是没有区别的,但是有一点是有严重区别的,继承了Thread类,那么可以直接继承run()方法,但是如果实现Runnable接口,并没有start()方法可以被继承

不管何种情况下,如果要启动多线程一定要依靠Thread类完成,在Thread类里面定义有以下的构造方法

  • 构造方法:public Thread(Runnable taget),接收Runnable接口对象;

范例:启动多线程

public class TestDemo {
	public static void main(String[] args) {
		MyThread mt1 = new MyThread("线程A");
		MyThread mt2 = new MyThread("线程B");
		MyThread mt3 = new MyThread("线程C");
		
		new Thread(mt1).start();
		new Thread(mt2).start();
		new Thread(mt3).start();
	}
}

此时就避免了单继承局限,在实际工作中使用Runnable接口是最实用的

三、多线程两种实现方式区别?(面试题)

使用Runnable接口与Thread类相比,解决了单继承的定义局限,所以不管后面区别是什么,这里下了死定义——如果要使用一定使用Runnable接口。

使用Runnable接口可以比Thread类能够更好的描述出数据共享这一概念。此时的数据共享是多个线程访问统一资源的概念 。

观察:

class MyThread extends Thread{//这就是一个多线程的操作类
	private int ticket = 10;
	
	@Override
	public void run() {//覆写run()方法作为线程的主体操作方法
		for(int x = 0; x < 100 ; x++) {
			if(this.ticket > 0) {
				System.out.println("卖票,ticket = " + this.ticket --);
			}
		}
	}
}

public class TestDemo {
	public static void main(String[] args) {
		MyThread mt1 = new MyThread();
		MyThread mt2 = new MyThread();
		MyThread mt3 = new MyThread();
		
		mt1.start();
		mt2.start();
		mt3.start();
	}
}

本程序声明了三个MyThread类的对象,并且分别调用了三次start()方法,启动线程对象,发现每个线程对象都在卖各自的10张票,此时并不存在数据共享这一概念

范例:利用Runable来实现

class MyThread implements Runnable{//这就是一个多线程的操作类
	private int ticket = 10;
	@Override
	public void run() {//覆写run()方法作为线程的主体操作方法
		for(int x = 0; x < 100 ; x++) {
			if(this.ticket > 0) {
				System.out.println("卖票,ticket = " + this.ticket --);
			}
		}
	}
}

public class TestDemo {
	public static void main(String[] args) {
		MyThread mt = new MyThread();
		
		new Thread(mt).start();
		new Thread(mt).start();
		new Thread(mt).start();
	}
}

虽然也是三个线程对象,可以唯一的区别是这三个线程对象都直接占用了同一个MyThread类的对象引用,也就是说这三个线程对象访问的是一个数据资源。

面试题:请解释Thread类与Runnable接口实现多线程的区别?(面试题:请解释多线程两种实现的区别?)

  • Thread类是Runnable接口的子类,使用Runnable接口实现多线程可以避免单继承局限;
  • Runnable使用的多线程可以比Thread类实现的多线程更加清楚的描述数据共享的概念:

面试题:请写出多线程两种实现操作。

把Thread类继承的方式和Runnable接口实现的方式都写出来。

四、第三种实现方式(理解)

使用Runnable接口实现的多线程可以避免单继承局限,的确很好,但是有一个问题,Runnable接口里边的run()方法不能返回操作结果。为了解决这样的矛盾,提供了新的接口Callable接口。

@FunctionalInterface
public interface Callable<V>{
	public V call() throws Exception;
}

call()方法执行完线程的主题功能之后可以返回一个结果,而返回结果的类型由Callable接口上的泛型决定。

范例:定义一个线程主体类

class MyThread implements Callable<String>{//这就是一个多线程的操作类
	private int ticket = 10;
	@Override
	public String call() throws Exception {
		for(int x = 0 ; x <100 ; x++) {
			if(this.ticket > 0) {
				System.out.println("卖票,ticket = " + this.ticket --);
			}
		}
		return "票已卖光!";
	}
}

此时观察Thread类里边并没有直接支持Callable接口的多线程应用

在FutureTask类里边定义有如下构造方法:public FutureTask(Callable<V> callable)

接受的目的只有一个,那么就是取得call()方法的返回结果。

public class TestDemo {
	public static void main(String[] args) throws Exception {
		MyThread mt1 = new MyThread();
		MyThread mt2 = new MyThread();
		FutureTask<String> task1 = new FutureTask<>(mt1);//目的是为了取得call的返回结果
		FutureTask<String> task2 = new FutureTask<>(mt2);//目的是为了取得call的返回结果
		//FutureTask是Runnable接口子类,所以可以使用Thread类的构造来接收task对象
		new Thread(task1).start();//启动多线程
		new Thread(task2).start();//启动多线程
		//多线程执行完毕后可以取得内容,依靠FutureTask的父类接口Future中的get()方法完成
		System.out.println("A线程返回的结果:" + task1.get());
		System.out.println("B线程返回的结果:" + task2.get());
	}
}

最麻烦的问题在于需要接收返回值信息,并且又要与原始的多线程实现靠拢

 

本文章是观看李兴华老师的视频所记笔记,仅供个人学习使用,如有问题请联系本人删除

视频链接Java8编程开发入门-第38章-Java多线程实现(李兴华)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值