jdk一共提供了三种创建线程的方法, 分别是, 继承Thread类, 实现Runnable接口和实现Callable接口;其中Callable可以返回值,下面以购票为例, 对三种方法进行演示
Thread方式:
package ticketwiththread;
public class Ticket extends Thread {
static int i = 100;
public Ticket(String name){
super(name);
}
@Override
public void run() {
while (true) {
if (i > 0) {
try {
Thread.sleep(10L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "在售票"
+ i--);
}
}
}
}
package ticketwiththread;
public class Client {
public static void main(String[] args) {
Ticket t1 = new Ticket("窗口1");
Ticket t2 = new Ticket("窗口2");
Ticket t3 = new Ticket("窗口3");
t1.start();
t2.start();
t3.start();
}
}
运行结果(尾部截取)
窗口1在售票7
窗口3在售票6
窗口2在售票5
窗口3在售票4
窗口2在售票3
窗口1在售票2
窗口3在售票1
窗口1在售票0
窗口2在售票-1
出现线程安全问题,一共100张票,而结果出现了-1; 这时候需要加锁, 使用synchronize代码块, 如下例
实现Runnable接口
package ticketwithrunnable;
public class Ticket implements Runnable {
int i = 100;
@Override
public void run() {
while (true) {
synchronized (this) {
if (i > 0) {
try {
Thread.sleep(1L);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "在售票"
+ i--);
}
}
}
}
}
package ticketwithrunnable;
public class Client {
public static void main(String[] args) {
Ticket t = new Ticket();
Thread t1 = new Thread(t,"窗口1");
Thread t2 = new Thread(t,"窗口2");
Thread t3 = new Thread(t,"窗口3");
t1.start();
t2.start();
t3.start();
}
}
运行的结果:
窗口2在售票8
窗口2在售票7
窗口3在售票6
窗口2在售票5
窗口1在售票4
窗口3在售票3
窗口2在售票2
窗口2在售票1
加上synchronize块后,解决了并发问题, 另外, Thread方法和Runnable方法实现的方式不太一样 ; 注意区别;
在Thread的例子中, 票的数量使用static关键字修饰, 标识各个线程共享此变量, 而Runnable方式中, 只有一个ticket类;
一般情况下, 实现Runnable的方式比较常用, 主要因为java是单继承多实现的机制,实现接口方便一些,
第三种实现Callable接口
package threadcallable;
import java.util.concurrent.FutureTask;
public class Client {
public static void main(String[] args) {
Threadtest t = new Threadtest();
FutureTask<Integer> f = new FutureTask<Integer>(t);
new Thread(f).start();
try {
Integer integer = f.get();
System.out.println(integer);
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
}
package threadcallable;
import java.util.concurrent.Callable;
public class Threadtest implements Callable<Integer>{
@Override
public Integer call() throws Exception {
return 100;
}
}
结果:
100
注意到 , 实现Callable接口实现线程开启需要借助FutureTask类 , 获取方法也需要此类;
这里只演示获取返回值的情况, 卖票的案例与实现Runnable接口的方式相似;
play框架中的job底层使用的就是实现Callable接口的方式开启线程;