您的位置:首页 > 编程语言 > Java开发

JAVA多线程之——线程的实现方式

2017-03-23 23:39 447 查看
内容转载:http://www.cnblogs.com/skywang12345/p/3479063.html

概要

本章我们学习多线程的两种常用实现方式:继承Thread类与实现Runnable接口。

此外我们还可以通过JUC(java.util.concurrent)中的线程池来实现。本章内容包括:

Thread与Runnable简介

Thread 是一个类,而这个类本身就实现了Runnable接口。它的声明如下:

public class Thread implements Runnable{}


Runnable 是一个接口。该接口中只包含了一个run()方法。它的声明如下:

public interface Runnable {
public abstract void run();
}


通过Runnable实现线程:

new Thread(new Runnable(){

@Override
public void run() {

}

});


Thread与Runnable的异同

1.Thread与Runnable都能实现多线程。

2.Thread是类,Runnable是接口。JAVA是单继承,所以Runnable具有更好的扩展性。而Thread本身就实现了Runnable接口。Runnable还可以用于“资源的共享”。即,多个线程都是基于某一个Runnable对象建立的,它们会共享Runnable对象上的资源。

Thread和Runnable的多线程示例

1. Thread的多线程示例

// ThreadTest.java 源码
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(this.getName()+" 卖票:ticket"+this.ticket--);
}
}
}
};

public class ThreadTest {
public static void main(String[] args) {
// 启动3个线程t1,t2,t3;每个线程各卖10张票!
MyThread t1=new MyThread();
MyThread t2=new MyThread();
MyThread t3=new MyThread();
t1.start();
t2.start();
t3.start();
}
}


运行结果:

Thread-0 卖票:ticket10
Thread-1 卖票:ticket10
Thread-2 卖票:ticket10
Thread-1 卖票:ticket9
Thread-0 卖票:ticket9
Thread-1 卖票:ticket8
Thread-2 卖票:ticket9
Thread-1 卖票:ticket7
Thread-0 卖票:ticket8
Thread-1 卖票:ticket6
Thread-2 卖票:ticket8
Thread-1 卖票:ticket5
Thread-0 卖票:ticket7
Thread-1 卖票:ticket4
Thread-2 卖票:ticket7
Thread-1 卖票:ticket3
Thread-0 卖票:ticket6
Thread-1 卖票:ticket2
Thread-2 卖票:ticket6
Thread-2 卖票:ticket5
Thread-2 卖票:ticket4
Thread-1 卖票:ticket1
Thread-0 卖票:ticket5
Thread-2 卖票:ticket3
Thread-0 卖票:ticket4
Thread-2 卖票:ticket2
Thread-0 卖票:ticket3
Thread-2 卖票:ticket1
Thread-0 卖票:ticket2
Thread-0 卖票:ticket1


通过Thread启动3个线程去卖票,会导致每个线程都卖出了10张票。

2. 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(Thread.currentThread().getName()+" 卖票:ticket"+this.ticket--);
}
}
}
};

public class RunnableTest {
public static void main(String[] args) {
MyThread mt=new MyThread();

// 启动3个线程t1,t2,t3(它们共用一个Runnable对象),这3个线程一共卖10张票!
Thread t1=new Thread(mt);
Thread t2=new Thread(mt);
Thread t3=new Thread(mt);
t1.start();
t2.start();
t3.start();
}
}


**这里代码会有几个问题:

1.就是MyThread 是普通类,而main方法为static方法,在静态方法中,必须将MyThread 声明为静态类。

2.上述代码的执行会导致线程资源竞争出问题。因为3个线程都共享一个对象mt,这样所有线程都竞争mt中的ticket的变量时,可能会出现线程票数为负数。因为当线程1获取最后一张票为1时。如果这个线程还没执行完毕卖票,那么另外一个线程2得到的票数也为1.当线程1卖完票,此时线程2已经获取的为1,此时实际票已经为0.那么得到的结果就会为-1.*

public class ThreadTest1 {
public static void main(String[] args) {
MyThread mt = new MyThread();
// 启动3个线程t1,t2,t3(它们共用一个Runnable对象),这3个线程一共卖10张票!
Thread t1 = new Thread(mt);
Thread t2 = new Thread(mt);
Thread t3 = new Thread(mt);
t1.start();
t2.start();
t3.start();
}

// RunnableTest.java 源码
static class MyThread implements Runnable {
private int ticket = 10;
Object obj = new Object();

public void run() {
//synchronized (obj) {
for (int i = 0; i < 10; i++) {
if (this.ticket > 0) {
try {
Thread.currentThread().sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ " 卖票:ticket" + this.ticket--);
// }
}
}
}

//}
};

}


如上代码,在每次卖票之前进行线程休眠。得到结果如下:

Thread-2 卖票:ticket10
Thread-0 卖票:ticket9
Thread-1 卖票:ticket8
Thread-0 卖票:ticket7
Thread-2 卖票:ticket6
Thread-1 卖票:ticket5
Thread-2 卖票:ticket4
Thread-0 卖票:ticket3
Thread-1 卖票:ticket2
Thread-2 卖票:ticket1
Thread-0 卖票:ticket0
Thread-1 卖票:ticket-1


如果将ticket变量声明为volatile类型呢?一个变量声明为volatile的原则之一就是该变量计算的值要不依赖前面的值。此处不合适。后面会学习到这个变量此处不详细说明。那么将run方法同步呢?

public class ThreadTest1 {
public static void main(String[] args) {
MyThread mt = new MyThread();
// 启动3个线程t1,t2,t3(它们共用一个Runnable对象),这3个线程一共卖10张票!
Thread t1 = new Thread(mt);
Thread t2 = new Thread(mt);
Thread t3 = new Thread(mt);
t1.start();
t2.start();
t3.start();

}

// RunnableTest.java 源码
static class MyThread implements Runnable {
private int ticket = 20;
Object obj = new Object();

public synchronized void run() {
//synchronized (obj) {
for (int i = 0; i < 10; i++) {
if (this.ticket > 0) {
try {
Thread.currentThread().sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ " 卖票:ticket" + this.ticket--);
// }
}
}
}

//}
};

}


将run方法同步得到的结果为:

Thread-2 卖票:ticket20
Thread-2 卖票:ticket19
Thread-2 卖票:ticket18
Thread-2 卖票:ticket17
Thread-2 卖票:ticket16
Thread-2 卖票:ticket15
Thread-2 卖票:ticket14
Thread-2 卖票:ticket13
Thread-2 卖票:ticket12
Thread-2 卖票:ticket11
Thread-1 卖票:ticket10
Thread-1 卖票:ticket9
Thread-1 卖票:ticket8
Thread-1 卖票:ticket7
Thread-1 卖票:ticket6
Thread-1 卖票:ticket5
Thread-1 卖票:ticket4
Thread-1 卖票:ticket3
Thread-1 卖票:ticket2
Thread-1 卖票:ticket1


因为如果将整个run方法同步,那么就是锁住mt这个对象,当第一个线程获取了mt的锁之后,后面的线程由于都是共享mt同一个锁,所以,只能等线程1释放该对象的锁。那么,就会导致线程1卖完10张飘。再释放锁。接下来获取mt对象锁的线程再卖10张。这样就等于要一个一个的卖,而不能并发卖。同理声明一个对象obj,在锁住该对象,跟同步run方法运行的结果一致。那该如何呢?

import java.util.concurrent.atomic.AtomicInteger;

public class ThreadTest1 {
public static void main(String[] args) {
MyThread mt = new MyThread();
// 启动3个线程t1,t2,t3(它们共用一个Runnable对象),这3个线程一共卖10张票!
Thread t1 = new Thread(mt);
Thread t2 = new Thread(mt);
Thread t3 = new Thread(mt);
t1.start();
t2.start();
t3.start();
}

// RunnableTest.java 源码
static class MyThread implements Runnable {
private int  ticket = 20;
Object obj = new Object();
public void run() {
for (int i = 0; i < 10; i++) {
synchronized (obj) {
if (ticket > 0) {
try {
Thread.currentThread().sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ " 卖票:ticket" + this.ticket--);
}
}
}
}

//}
};

}


运行结果:

Thread-2 卖票:ticket20
Thread-2 卖票:ticket19
Thread-2 卖票:ticket18
Thread-2 卖票:ticket17
Thread-2 卖票:ticket16
Thread-0 卖票:ticket15
Thread-0 卖票:ticket14
Thread-0 卖票:ticket13
Thread-0 卖票:ticket12
Thread-0 卖票:ticket11
Thread-0 卖票:ticket10
Thread-1 卖票:ticket9
Thread-0 卖票:ticket8
Thread-0 卖票:ticket7
Thread-0 卖票:ticket6
Thread-0 卖票:ticket5
Thread-2 卖票:ticket4
Thread-2 卖票:ticket3
Thread-2 卖票:ticket2
Thread-2 卖票:ticket1


通过声明一个变量,然后在每次卖票判断之前进行加锁。则可以解决这问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐