Thread 和Runnable的区别
如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。
main函数,实例化线程对象也有所不同,
extends Thread :t.start();
implements Runnable : new Thread(t).start();
总结:
实现implements Runnable接口比继承 extends Thread类所具有的优势:
1):适合多个相同的程序代码的线程去处理同一个资源
2):可以避免java中的单继承的限制(不能访问父类的私有成员?)
3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
4):线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类
示例
以卖票程序为例,通过Thread类完成:
class MyThread extends Thread{
private int ticket=10;
public void run(){
for(int i=0;i<20;i++){
if(this.ticket>0){
System.out.println("卖票:ticket"+this.ticket--);
}
}
}
};
下面通过三个线程对象,同时卖票:
public class ThreadTicket {
public static void main(String[] args) {
MyThread mt1=new MyThread();
MyThread mt2=new MyThread();
MyThread mt3=new MyThread();
mt1.start();//每个线程都各卖了10张,共卖了30张票
mt2.start();//但实际只有10张票,每个线程都卖自己的票
mt3.start();//没有达到资源共享
}
}
如果用Runnable就可以实现资源共享,下面看例子:
package org.demo.runnable;
class MyThread implements Runnable{
private int ticket=10;
public void run(){
for(int i=0;i<20;i++){
if(this.ticket>0){
System.out.println("卖票:ticket"+this.ticket--);
}
}
}
}
public class RunnableTicket {
public static void main(String[] args) {
MyThread mt=new MyThread();
new Thread(mt).start();//同一个mt,但是在Thread中就不可以,如果用同一
new Thread(mt).start();//个实例化对象mt,就会出现异常
new Thread(mt).start();
}
};
虽然现在程序中有三个线程,但是一共卖了10张票,也就是说使用Runnable实现多线程可以达到资源共享目的。
说法2
Thread类
//代码的运行结果与代码执行顺序或调用顺序是无关的
package ThreadAndRunnable;
public class MyThread extends Thread{
public MyThread(){
super();
System.out.println("当前线程的名字="+Thread.currentThread().getName());
}
@Override
public void run(){
System.out.println("当前线程的名字="+Thread.currentThread().getName());
}
package ThreadAndRunnable;
public class Run {
public static void main(String[] args) {
MyThread myThread=new MyThread();
myThread.start();
System.out.println("运行结束!");
}
}
(2)运行结果
当前线程的名字=main
运行结束!
当前线程的名字=Thread-0
(3)解释说明
main函数执行这个MyThread myThread=new MyThread();方法的时候,然后是直接调用MyThread()的构造方法,然后打印出“当前线程的名字=main”,后面先运行结束,然后在打印出Thread线程的run方法,也说明了代码的运行结果与代码的执行顺序或调用顺序是无关的。
Runnable
package Runnable;
public class myrunnable implements Runnable {
@Override
public void run(){
System.out.println("运行中!");
}
}
package Runnable;
public class Run {
public static void main(String[] args) {
Runnable runnable=new myrunnable();
Thread thread=new Thread(runnable);
thread.start();
System.out.println("运行结束!");
}
}
(2)运行结果
运行结束!
运行中!
(3)解释说明
Thread类中包括构造函数Thread(Runnable target)和Thread(Runnable target,String name),可以创建Runnable接口,说明构造函数支持传入一个Runnable接口的对象。
下面的图说明了。构造函数Thread(Runnable target)不光可以传入Runnable接口的对象,还可以传入一个Thread类的对象。这样的好处是可以完全将Thread对象中的run()方法交由其他线程进行调用。
三、比较
如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。
main函数,实例化线程对象也有所不同,
extends Thread :t.start();
implements Runnable : new Thread(t).start();
使用Runnable,增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类
为什么Java 允许实现多接口,却不允许多继承(C++允许)?
如果多继承,会出现语义不明。类c同时继承A,B,AB都有fun()方法,调用c.fun() 会不清楚究竟是调用谁的。
多接口呢?如果类c同时实现接口A,B ,而接口中的都是抽象方法();调用方法时不会出现不明确,毕竟接口中的都是抽象方法,而且 超类 ??的任何方法都需要在子类中覆盖而实现。所以调用接口的方法时,其实是调用自身。
public interface A {
void fun();
}
public interface B {
void fun();
}
public class Demo implements A,B {
@Override
public void fun() {
System.out.println("我自己来实现接口中的抽象方法"); //继承接口A,B。
但是方法的具体实现是自己本函数实现的。接口里面都是抽象方法。
}
public static void main(String[] args) {
Demo demo = new Demo();
demo.fun();
}
}
接口 和 抽象
这里就要再说一下 接口 和 抽象类的关系了:(之前一直没有总结)
看到上面,直到为什么接口可以被多实现,因为->里面的方法全部是抽象的!什么是抽象?就是final static 不能被调用,只能被override?
接口:比抽象类更抽象。是对象 动作的抽象。描述对象能做什么。接口中方法常用一些,成员变量用得少一些。
1.里面所有的方法都是抽象abstract的,没有任何实现,所有可变的东西都应该归属到实现类中Runnable()接口的run()方法,很明显体现。(start方法是Thread的);接口中的方法必须是public 的,要可以被重写,不然继承接口干什么。
2.接口中的成员变量只有一种类型,public static final ,所以可以直接省去修饰符。
-----------public 。保证所有实现类的共有。
-----------static :所有实现类都只有这一份,避免重名。如果实现类的第二个接口也同名,那么存储时候就会报错(是好事)。
-----------既然抽象类,肯定final,大家都用的,不能随便改。否则违反设计模式的OCP开闭原则->稳定灵活的系统。
3.不含构造器。
[修饰符] interface 接口名 [extends 父接口名列表]{
[public] [static] [final] 常量;
[public] [abstract] 方法;//没有方法体
}
public interface CalInterface {
final float PI=3.14159f;//定义用于表示圆周率的常量PI
float getArea(float r);//定义一个用于计算面积的方法getArea()
float getCircumference(float r);//定义一个用于计算周长的方法getCircumference()
}
一个类实现多个接口,同名变量的访问
interface X {
public static final String name="123";
}
interface Y {
public static final String name="456";
}
public class Z implements X,Y {
public static void main (String [] args){
System.out.println(X.name);//如果不定义为 static 的成员变量,如果实现的两个接口中有同名变量,则不能引用。
System.out.println(Y.name); //不能写做 Z t1 = new Z(); System.out.print(z.name);这时候会报错,定义为static 就是为了在编译时就能发现错误。
}
}
/*
* 运行结果:
* 123
* 456
* */
抽象类:是对象 根源的抽象。本质区别于别的类。描述对象是什么。
1.里面的方法,抽象或不抽象方法。抽象方法都放给子类来具体实现。
2.成员变量 可以是static ,也可以普通的成变。
3.可以包含构造器,并不创建对象,而是让子类可以调用构造器完成属于抽象类的初始化。
抽象类中的功能>>接口。但是订一起来代价高。而且java单继承局限了,得在这个类里面写出所有子类的共性。
//抽象类的声明
abstract class Animal {
String type;
String name;
int age;
int weight;
void eat() {
System.out.println("动物爱吃饭");
}
//抽象方法在抽象类中只能声明,不能具体实现
abstract void breath();
void sleep() {
System.out.println("动物在睡觉");
}
}
//由子类去继承父类抽象类
class tiger extends Animal{
@Override
//在此处实现抽象方法 breath()
void breath() {
System.out.println("老虎在呼吸");
}
}
public class first_for {
public static void main(String [] args){
// 错误,程序会报错
//报错原因:抽象类不能进行实例化操作
//Animal Tiger = new Animal();
//只能用子类进行实例化 *************66666666666
Animal Tiger = new tiger();
Tiger.breath();
}
}
参考链接
https://blog.csdn.net/dfshsdr/article/details/92432519