上一篇Java 多线程详解(一)讲解了线程的一些基本概念和多线程的实现方式。接下来将讲解实现多线程主要的两种方式的区别和多线程的常用操作方法等。
一、Thread类和Runnable接口实现多线程两种方式的区别
Thread类和Runnable接口都可以做为同一功能的方式来实现多线程,那么从Java的实际开发而言,肯定优先考虑使用Runnable接口,因为可以有效的避免单继承的局限,除了这个,这两种方式是否还有其他联系呢?
我们先来看Thread类的定义结构:
public class Thread extends Object implements Runnable
发现Thread类也是Runnable接口的子类,这样的话,上一篇中Runnable接口实现多线程的程序的结构就有了以下形式:
这时所表现出来的代码模式非常类似于代理设计模式,但是它并不是严格意义上代理设计模式。因为从严格来讲代理设计模式之中,代理主题所能够使用的方法依然是接口中定义的run()方法,而此处代理主题调用的是start()方法,所以只能够说形式上类似于代理设计模式,但本质上还是有差别的。
除了以上的联系之外,对于Runnable和Thread类还有一个区别:使用Runnable接口可以更加方便的表示出数据共享的概念。(这里的数据共享是指多个线程访问同一个资源的操作)
我们还是来看卖票程序:
首先,通过继承Thread类实现多线程卖票程序:
package com.wz.threaddemo;
class MyThread extends Thread { // 线程的主体类
private int ticket = 5; // 一共5张票
@Override
public void run() { // 线程的主方法
for (int x = 0; x < 20; x++) {
if (this.ticket > 0) {
System.out.println(this.getName()+"卖票,ticket = " + this.ticket--);
}
}
}
}
public class TestDemo {
public static void main(String[] args) throws Exception {
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
MyThread mt3 = new MyThread();
mt1.start();
mt2.start();
mt3.start();
}
}
运行结果:
Thread-2卖票,ticket = 5
Thread-1卖票,ticket = 5
Thread-0卖票,ticket = 5
Thread-1卖票,ticket = 4
Thread-2卖票,ticket = 4
Thread-1卖票,ticket = 3
Thread-0卖票,ticket = 4
Thread-0卖票,ticket = 3
Thread-0卖票,ticket = 2
Thread-0卖票,ticket = 1
Thread-1卖票,ticket = 2
Thread-1卖票,ticket = 1
Thread-2卖票,ticket = 3
Thread-2卖票,ticket = 2
Thread-2卖票,ticket = 1
本程序创建了三个Thread类的对象,并且分别调用了三次start()方法启动线程, 从程序输出结果可见,一共买出了15张票,也就是每一个线程对象各自卖各自的5张票。
此时的内存关系图如下:
然后再来看通过用Runnable接口来实现多线程卖票程序:
package com.wz.threaddemo;
class MyThread implements Runnable { // 线程的主体类
private int ticket = 5; // 一共5张票
@Override
public void run() { // 线程的主方法
for (int x = 0; x < 10; x++) {
if (this.ticket > 0) {
System.out.println("卖票,ticket = " + this.ticket--);
}
}
}
}
public class TestDemo {
public static void main(String[] args) throws Exception {
MyThread mt = new MyThread();
new Thread(mt).start();
new Thread(mt).start();
new Thread(mt).start();
}
}
运行结果:
卖票,ticket = 5
卖票,ticket = 3
卖票,ticket = 4
卖票,ticket = 1
卖票,ticket = 2
此时也属于三个线程对象,不同的是这三个对象都直接占用了同一个MyThread类对象,即这三个线程对象都直接访问了同一个数据资源。
以下是内存分配图:
要说明的是,使用继承Thread类实现多线程的方法也可以实现同样的功能,程序如下:
package com.wz.threaddemo;
class MyThread extends Thread { // 线程的主体类
private int ticket = 5; // 一共5张票
@Override
public void run() { // 线程的主方法
for (int x = 0; x < 10; x++) {
if (this.ticket > 0) {
System.out.println("卖票,ticket = " + this.ticket--);
}
}
}
}
public class TestDemo {
public static void main(String[] args) throws Exception {
MyThread mt = new MyThread();
new Thread(mt).start();
new Thread(mt).start();
new Thread(mt).start();
}
}
运行结果:
卖票,ticket = 5
卖票,ticket = 3
卖票,ticket = 4
卖票,ticket = 1
卖票,ticket = 2
使用继承Thread类实现多线程的方法也可以实现同样的功能,把MyThread对象交给另一个Thread对象来运行,其实和Runnable对象也是类似的,启动线程的是那个Thread对象,而MyThread只是被调用了run()方法而已。
小结:Thread类和Runnable接口实现多线程两种方式的联系和区别?
联系:
多线程的两种实现方式都需要一个线程的主类,这个类可以实现Runnable接口或继承Thread类,但不管使用何种方式都必须在子类之中覆写run()方法,此方法为线程的主方法。
区别:
(1)Thread类是Runnable接口的子类,使用Runnable接口实现多线程可以避免单继承局限;
(2)Runnable接口实现的多线程可以比Thread类实现的多线程更加清楚的描述数据共享的概念。代码能够被多个线程共享,代码与数据是独立的。
二、多线程的常用操作方法
1、currentThread()方法:返回代码段正在被哪个线程调用的信息。
public static Thread currentThread()
实例如下:
package com.wz.threaddemo;
class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread());
}
}
public class TestDemo {
public static void main(String[] args) throws Exception {
System.out.println(Thread.currentThread());
MyThread mt = new MyThread();
new Thread(mt).start();
new Thread(mt).start();
new Thread(mt).start();
}
}
运行结果:
Thread[main,5,main]
Thread[Thread-0,5,main]
Thread[Thread-1,5,main]
Thread[Thread-2,5,main]
2、setName()/getName()方法和getId()方法:
(1)setName()/getName()方法:设置/取得线程的名称;
public final void setName(String name)
public final void setName(String name)
(2)getId()方法:取得线程的唯一标识。
public final void setName(String name)
实例如下:
package com.wz.threaddemo;
class MyThread implements Runnable {
@Override
public void run() {
System.out.println();
}
}
public class TestDemo {
public static void main(String[] args) throws Exception {
MyThread mt = new MyThread();
Thread t1 = new Thread(mt);
t1.setName("线程1");
System.out.println(t1.getName() + ","+t1.getId());
Thread t2 = new Thread(mt);
t2.setName("线程2");
System.out.println(t2.getName() + ","+t2.getId());
Thread t3 = new Thread(mt);
t3.setName("线程3");
System.out.println(t3.getName() + ","+t3.getId());
}
}
运行结果:
线程1,10
线程2,11
线程3,12
3、setPriority(int p)/getPriority()方法
(1)setPriority(int p)方法:设置优先级
public final void setPriority(int newPriority)
(2)getPriority()方法:获取优先级
public final int getPriority()
优先级常量:
最高优先级:MAX_PRIORITY:
public static final int MAX_PRIORITY = 10;
默认优先级:NORM_PRIORITY
public static final int NORM_PRIORITY = 5;
最低优先级:MIN_PRIORITY
public static final int MIN_PRIORITY = 1;
4、sleep()方法:在指定的毫秒数内,让“正在执行的线程”休眠(暂停执行)
这里“正在执行的线程”是指
this.currentThread()
返回的线程。
public static void sleep(long millis)
throws InterruptedException
调用:
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
未完待续。。。