实现多线程基础的两种方式有:1.继承Thread类,2.实现Runnable接口。这两种方式一般都是适用于线程数量较少的情况,由于线程的创建和销毁会降低程序性能,所以当线程数量较多情况,建议使用线程池。
介绍
实现多线程基础方式。
方式一:实现Runnable接口。Runnable接口中只有一个方法Run()方法。
public interface Runnable {
public abstract void run();
}
方式二:继承Thread类。Thread类继承了Runnable接口。
public class Thread implements Runnable {
}
异同点
对于实现多线程的方式,有两点不同。
1.在java中只能继承一个类,但是可以实现多个接口,所以接口实现的Runnable的方式有很好的扩展性。
2.还有另外一点有争议的是,Runnable和Thread能否实现资源的共享,也就是是否共享一个对象实例(下面通过代码探讨)。
其实真正创建线程的方式只有一种,新建一个Thread对象(也就是new Thread())。而对于多线程实现方式一,通过实现Runnable接口的类,就是产生一个任务或者工作类,交给一个或者多个线程去完成这个任务(new Thread(任务).start())。上面说到Thread类是继承了Runnable接口的,而Thread就是线程类。所以通过继承Thread接口,重写run()方法,就是任务和线程绑定到了一起。
建议采用是实现Runnable接口的方式来实现多线程,这种方式只是产生了一个任务类,然后交给Thread线程去执行任务。将线程和任务进行了分离,达到了松耦合的目的。
代码实现
先看实现Runnable接口的方式。
public class ThreadTask implements Runnable {
private int ticket = 10;
public void run() {
for (int i = 0; i < 10; i++) {
if (this.ticket > 0) {
try {
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + "---卖票:ticket:" + (ticket--));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
ThreadTask thread = new ThreadTask();
new Thread(thread, "线程1").start();
new Thread(thread, "线程2").start();
}
}
执行结果:
线程2---卖票:ticket:10
线程1---卖票:ticket:9
线程1---卖票:ticket:8
线程2---卖票:ticket:7
线程2---卖票:ticket:6
线程1---卖票:ticket:5
线程1---卖票:ticket:4
线程2---卖票:ticket:3
线程1---卖票:ticket:2
线程2---卖票:ticket:1
线程1---卖票:ticket:0
上面说过实现Runnable接口的类,只是产生一个任务的类,在main()方法中我们先创建了一个任务类ThreadTask,
然后分别启动了两个线程去执行这个任务,所以两个线程合力完成了这个任务,也就是说资源被共享。
再看继承Thread类的方式
public class MyThread extends Thread {
private int ticket = 10;
@Override
public void run() {
for(int i = 0; i < 10; i++) {
if(this.ticket>0) {
try {
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+"---卖票:ticket:"+(ticket--));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
MyThread thread = new MyThread();
MyThread thread2 = new MyThread();
thread.start();
thread2.start();
}
}
执行结果
Thread-1---卖票:ticket:10
Thread-0---卖票:ticket:10
Thread-1---卖票:ticket:9
Thread-0---卖票:ticket:9
Thread-1---卖票:ticket:8
Thread-0---卖票:ticket:8
Thread-1---卖票:ticket:7
Thread-0---卖票:ticket:7
Thread-1---卖票:ticket:6
Thread-0---卖票:ticket:6
Thread-1---卖票:ticket:5
Thread-0---卖票:ticket:5
Thread-0---卖票:ticket:4
Thread-1---卖票:ticket:4
Thread-1---卖票:ticket:3
Thread-0---卖票:ticket:3
Thread-1---卖票:ticket:2
Thread-0---卖票:ticket:2
Thread-1---卖票:ticket:1
Thread-0---卖票:ticket:1
Thread就是个线程类,而且实现了Runnable接口。因为MyThread类继承Thread类,并重写run()(里面是任务),所以线程上绑定了任务。
MyThread类继承了Thread线程类,所以MyThread本身就是个线程类,可以通过start()方法直接启动线程。
下面这种不能说是错误的方式,但是以一种不是很合理的方式演示了继承Thread来实现多线程的方式。因为Thread实现了Runnable接口,而ThreadTask又继承Thread了,所以也可以说ThreadTask类是任务类,通过new Thread(任务).start()来执行任务也说的通,但是有一点,如果说将继承Thread类,比如上面的MyThread类,只是让这个类变成一个任务类,为什么不直接通过实现Runnable()的方式得到一个任务类。
所以按上面分析,实现Runnable接口的方式可以实现资源共享,因为多个线程合力完成一个任务,这个任务的所有资源可以被所有的线程共享到。而继承Thread类的方式不能实现资源共享,因为继承Thread的类本身就是一个线程类,并且线程上绑定了任务,一个线程类绑定一个任务类,本线程里面的资源只能被本线程使用,别的线程不能共享到该线程的任务上的资源。
public class ThreadTask extends Thread {
private int ticket = 10;
public void run() {
for (int i = 0; i < 10; i++) {
if (this.ticket > 0) {
try {
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + "---卖票:ticket:" + (ticket--));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
ThreadTask task = new ThreadTask();
new Thread(task,"线程1").start();
new Thread(task,"线程2").start();
}
}
执行结果:
线程1---卖票:ticket:10
线程2---卖票:ticket:9
线程1---卖票:ticket:8
线程2---卖票:ticket:7
线程2---卖票:ticket:6
线程1---卖票:ticket:5
线程1---卖票:ticket:4
线程2---卖票:ticket:3
线程1---卖票:ticket:2
线程2---卖票:ticket:1
当然,这个演示的例子有很多要改进。就拿实现Runnable接口的方法来看,存在多个线程并发抢夺资源的情况。
线程1如果将数据判断if(ticket>0)为真,此时线程2控制了资源。此时如果线程2将ticket数值修改为0,线程1依旧会进行下面的操作,造成多卖票的情况。改进如下:
public class ThreadTask implements Runnable {
private int ticket = 10;
public void run() {
for (int i = 0; i < 10; i++) {
synchronized (this) {
if (this.ticket > 0) {
try {
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + "卖票:ticket:" + (ticket--));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public static void main(String[] args) {
ThreadTask thread = new ThreadTask();
new Thread(thread, "线程1").start();
new Thread(thread, "线程2").start();
}
}