Java 实现多线程两种方法及其区别与联系
Java中实现多线程操作有两种方法:
1.继承Thread类;
如覆写Thread类中的run方法;
举例:
class MyThread extends Thread{
private String name;
public MyThread(String name) {
super();
this.name = name;
}
public void run() {
for (int i=0;i<3;i++) {
System.out.println(name+"运行, i="+i);
}
}
}
public class Threads {
public static void main(String[] args) {
MyThread mt1=new MyThread("A");
MyThread mt2=new MyThread("B");
mt1.run();
mt2.run();
}
}
输出结果:
A运行, i=0
A运行, i=1
A运行, i=2
B运行, i=0
B运行, i=1
B运行, i=2
输出结果可以看出,A与B是两个不同的进程,线程A先于B执行;如何实现A,B线程的同时进行、即多线程?
如果将最后的“mt1.run();mt2.run();”
改为“mt1.start();mt2.start();”
,多次执行后其中一组结果:
B运行, i=0
A运行, i=0
A运行, i=1
A运行, i=2
B运行, i=1
B运行, i=2
比较两组结果发现:现在两个线程对象是交错运行,即哪个对象抢到了CPU,哪个就先运行,此处的start()
方法,调用的是run()
方法定义的主体,Thread类的每个对象只能调用一次start()
方法;
2.实现Runnable接口;
首先明白接口是由全局变量和抽象方法构成,且访问权限均为public;
Runnable()接口只定义了一个抽象方法,即:public void run();
举例:
class myth implements Runnable{
private String name;
public myth(String name) {
super();
this.name = name;
}
public void run() {
for (int i=0;i<3;i++) {
System.out.println(name+"运行, i="+i);
}
}
}
public class testone{
public static void main(String[] args) {
myth mt1=new myth("A");
myth mt2=new myth("B");
Thread t1=new Thread(mt1);
Thread t2=new Thread(mt2);
t1.start();
t2.start();
}
}
输出结果:
A运行, i=0
B运行, i=0
B运行, i=1
B运行, i=2
A运行, i=1
A运行, i=2
此处的运行机制与Thread类中法start()方法一致,但Runnable()接口没有start()方法的定义,所以需要依靠Thread类完成,Thread类中提供了可以接受Runnable接口的子类实例对象的构造方法:
public Thread(Runnable target);
public Thread(Runnable target,String name);
Thread类中提供了可以接受Runnable的子类实例对象的构造方法,明白了两者之间的联系、两种实现多线程的方法之间存在什么区别呢?
通过一个例子来看一下:
1.继承Thread类:
class MyThreads extends Thread{
private int ticket=5;
public void run() {
for (int i=0;i<100;i++) {
if(ticket>0) {
System.out.println("买票:ticket="+ticket--);
}
}
}
}
public class ticket {
public static void main(String[] args) {
// TODO Auto-generated method stub
MyThreads m1=new MyThreads();
MyThreads m2=new MyThreads();
MyThreads m3=new MyThreads();
m1.start();
m2.start();
m3.start();
}
}
输出结果:
买票:ticket=5
买票:ticket=5
买票:ticket=4
买票:ticket=3
买票:ticket=2
买票:ticket=1
买票:ticket=5
买票:ticket=4
买票:ticket=4
买票:ticket=3
买票:ticket=3
买票:ticket=2
买票:ticket=1
买票:ticket=2
买票:ticket=1
2.实现Runnable接口:
class MyThreads implements Runnable{
private int ticket=5;
public void run() {
for (int i=0;i<100;i++) {
if(ticket>0) {
System.out.println("买票:ticket="+ticket--);
}
}
}
}
public class ticket {
public static void main(String[] args) {
// TODO Auto-generated method stub
MyThreads m1=new MyThreads();
new Thread(m1).start();
new Thread(m1).start();
new Thread(m1).start();
}
}
输出结果:
买票:ticket=5
买票:ticket=4
买票:ticket=3
买票:ticket=1
买票:ticket=2
比较实例输出结果,继承Thread类出现了票数总数的错误,而Runnable接口并没有出现,说明Runnable接口比Thread类在多线程共享资源更利于实现,可以得出,一个类继承了Thread类,则不适用于多线程共享资源,而实现Runnable接口,就更方便实现资源的共享;
实现Runnable接口相对于Thread类来说,有以下优势:
1.适合多个相同程序代码的线程去处理同一资源的情况;
2.可以避免由于Java的单继承特性带来的局限;
3.增强了程序的健壮性,代码能够被多个线程共享,代码与数据独立;