Java中的多线程实现方式
Java中的多线程实现方式
在我们的开发的过程中,常常会碰到多线程的问题,对于多线程的实现方式主要有两种:实现Runnable接口、集成Thread类。对于这两种多线程实现的方式也是有一些差异的。网上针对此问题基本都是使用买票系统的例子,接下来我们就用代码来模拟下售票系统,实现2个售票点发售10张车票,一个售票点表示一个线程。
方案一
首先从最简单的做法开始,开两个Thread类进行售票。
测试代码如下:
public class ticketThread extends Thread {
private int ticket = 10;
public void run() {
for(int i = 0; i < 10; i++){
if(ticket > 0){
try {
sleep(1000);
System.out.println(Thread.currentThread().getName()
+ "卖票 ——>" + (ticket--) );
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
new ticketThread().start();
new ticketThread().start();
}
}
测试结果:
Thread-0卖票 ——>10
Thread-1卖票 ——>10
Thread-1卖票 ——>9
Thread-0卖票 ——>9
Thread-1卖票 ——>8
Thread-0卖票 ——>8
Thread-1卖票 ——>7
Thread-0卖票 ——>7
Thread-0卖票 ——>6
Thread-1卖票 ——>6
Thread-0卖票 ——>5
Thread-1卖票 ——>5
Thread-1卖票 ——>4
Thread-0卖票 ——>4
Thread-1卖票 ——>3
Thread-0卖票 ——>3
Thread-1卖票 ——>2
Thread-0卖票 ——>2
Thread-0卖票 ——>1
Thread-1卖票 ——>1
结论:
从上面的测试结果可以看出,两个线程各自卖了各自的10张票而不是去卖共同的10张票,这和我们的目标多个线程去处理同一个资源相差很多。我们创建了2个ticketThread
对象就等于创建了2个资源,每个资源有10张票,每个资源都在独自处理各自的资源。所以通过这个例子我们知道,在这个售票系统中,我们只能创建一个资源对象,但需要创建多个线程去处理同一个资源对象,并且每个线程上所运行的是相同的程序代码。
方案二
既然只能创建一个资源对象,那么我就只创建一个ticketThread
,在额外创建两个新的线程去实现售票。
测试代码:
public class ticketThread extends Thread {
private int ticket = 10;
public void run() {
for(int i = 0; i < 10; i++){
synchronized(this){
if(ticket > 0){
try {
sleep(1000);
System.out.println(Thread.currentThread().getName()
+ "卖票 ——>" + (this.ticket--) );
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
ticketThread t1 = new ticketThread();
new Thread(t1, "线程1").start();
new Thread(t1, "线程2").start();
}
}
测试结果:
线程1卖票 ——>10
线程1卖票 ——>9
线程2卖票 ——>8
线程2卖票 ——>7
线程2卖票 ——>6
线程1卖票 ——>5
线程1卖票 ——>4
线程2卖票 ——>3
线程2卖票 ——>2
线程2卖票 ——>1
结论:
这种情况下,我们实现了多个线程对同一个资源进行处理。这里我们创建新线程使用了
Thread(ThreadGroup groupOb, String threadName)
把ticketThread
作为参数传入新创建的线程。在这种情况下新创建的两个线程就去执行了ticketThread
中的run()
方法,这样一来就实现了两个线程对同一个资源进行处理。但就原理上来说,此方案与方案三是一样的。
方案三
使用Runnable
来实现多线程。
测试代码:
public class ticketThread implements Runnable {
private int ticket = 10;
public void run() {
for(int i = 0; i < 10; i++){
// synchronized(this){
if(ticket > 0){
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()
+ "卖票 ——>" + (ticket--) );
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
// }
}
public static void main(String[] args) {
// TODO Auto-generated method stub
ticketThread t1 = new ticketThread();
new Thread(t1, "线程1").start();
new Thread(t1, "线程2").start();
}
}
测试结果:
线程1卖票 ——>10
线程2卖票 ——>9
线程2卖票 ——>7
线程1卖票 ——>8
线程1卖票 ——>6
线程2卖票 ——>5
线程1卖票 ——>4
线程2卖票 ——>3
线程2卖票 ——>2
线程1卖票 ——>1
结论
在上面的测试代码中,我们创建了2个线程,每个县城调用的是同一个ticketThread
对象中的run()
方法,访问的是同一个对象的变量(ticket)
的实例,这个程序就完美的满足了我们的需求。
Runnable与Thread的区别与联系
- 一个类只能继承一个父类,这是使用
Thread
的局限性,而Runnable
是一个接口,只要实现这个接口就行了。所以在实际的开发过程中,是通过Runnable
接口来实现的,并且Runnable
更适合资源共享的实现。 - 使用
Runnable
可以比喵由于Java的单继承特性带来的局限性,我们经常碰到这样一种情况,即当我们要已经继承了某一个累的子类放入多线程中,由于一个类不能同时偶两个父类,所以不能用继承Thread类的方式,那么这个类就是只能采用Runnable接口的方式了。 - 实际上
Thread类
也是Runnbale
接口的子类。
public class Thread extends Object implements Runnable
- 使用
Runnable
对象时,Runnable
定义的子类没有start()
方法,只有Thread
类中才有,观察Thread
类,有一个构造方法public Thread(Runnable target)
,此构造方法接受Runanble
的子类实例,也就是说可以通过Thread
类来启动Runnable
实现多线程。