1.Thread
/**
- 继承Thread类创建线程类
- (1)定义Thread类的子类,重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。
- (2)创建Thread子类的实例,即创建了线程对象。
- (3)调用线程对象的start()方法来启动该线程。
*/
package thread;
public class Thread_Test extends Thread{
private int ticketnum = 10;
//重写run方法,run方法的方法体就是现场执行体
public void run() {
for(int i=0; i<20;i++){
if (this.ticketnum > 0) {
ticketnum--;
System.out.println("总共10张,卖掉1张,剩余" + ticketnum);
}
}
}
public static void main(String[] args)
{
new Thread_Test().start();
new Thread_Test().start();
new Thread_Test().start();
//实际上是卖掉了30张车票
}
}
输出:
总共10张,卖掉1张,剩余9
总共10张,卖掉1张,剩余8
总共10张,卖掉1张,剩余7
总共10张,卖掉1张,剩余6
总共10张,卖掉1张,剩余9
总共10张,卖掉1张,剩余8
总共10张,卖掉1张,剩余7
总共10张,卖掉1张,剩余6
总共10张,卖掉1张,剩余5
总共10张,卖掉1张,剩余5
总共10张,卖掉1张,剩余4
总共10张,卖掉1张,剩余9
总共10张,卖掉1张,剩余3
总共10张,卖掉1张,剩余2
总共10张,卖掉1张,剩余1
总共10张,卖掉1张,剩余0
总共10张,卖掉1张,剩余4
总共10张,卖掉1张,剩余3
总共10张,卖掉1张,剩余2
总共10张,卖掉1张,剩余1
总共10张,卖掉1张,剩余0
总共10张,卖掉1张,剩余8
总共10张,卖掉1张,剩余7
总共10张,卖掉1张,剩余6
总共10张,卖掉1张,剩余5
总共10张,卖掉1张,剩余4
总共10张,卖掉1张,剩余3
总共10张,卖掉1张,剩余2
总共10张,卖掉1张,剩余1
总共10张,卖掉1张,剩余0
2.Runnable
/**
- 通过Runnable接口创建线程类
- (1)定义runnable接口的实现类,重写该接口的run()方法,该run()方法的方法体是该线程的线程执行体。
- (2)创建Runnable实现类的实例,并依此实例作为Thread的Runnable target来创建Thread对象,
- 该Thread对象才是真正的线程对象。
- (3)调用线程对象的start()方法来启动该线程。
*/
package thread;
public class Runnable_Test implements Runnable {
private int ticketnum = 10;
public void run() {
for(int i=0; i<20;i++){
if (this.ticketnum > 0) {
ticketnum--;
System.out.println("总共10张,"+Thread.currentThread().getName()+"卖掉1张,剩余" + ticketnum);
}
}
}
public static void main(String[] args) {
/*因为Runnable_Test是New了一次,使用的是同一个Runnable_Test,
可以达到资源的共享。最终只卖出10张车票*/
Runnable_Test rtt = new Runnable_Test();
//使用三个线程调用
new Thread(rtt, "卖票线程1").start();
new Thread(rtt, "卖票线程2").start();
new Thread(rtt, "卖票线程3").start();
}
}
输出:
总共10张,卖票线程1卖掉1张,剩余8
总共10张,卖票线程3卖掉1张,剩余7
总共10张,卖票线程2卖掉1张,剩余8
总共10张,卖票线程3卖掉1张,剩余5
总共10张,卖票线程1卖掉1张,剩余6
总共10张,卖票线程3卖掉1张,剩余3
总共10张,卖票线程2卖掉1张,剩余4
总共10张,卖票线程3卖掉1张,剩余1
总共10张,卖票线程1卖掉1张,剩余2
总共10张,卖票线程2卖掉1张,剩余0
3.Callable
/**
- 通过Callable和Future创建线程
- (1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
- (2)创建Callable实现类的实例,创建FutureTask来包装Callable对象,
- 该FutureTask对象封装了该Callable对象的call()方法的返回值。
- (3)使用FutureTask对象作为Thread对象的Runnable target创建并启动新线程。
- (4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
*/
package thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Callable_Test implements Callable
private int ticketnum = 10;
@Override
public String call() throws Exception {
int t = 0;
for (int i = 0; i < 20; i++) {
if (this.ticketnum > 0) {
ticketnum--;
t++;
System.out.println(Thread.currentThread().getName() + "总共10张,卖掉1张,剩余" + ticketnum);
}
}
return t + "";
}
public static void main(String[] args) {
Callable_Test ctt = new Callable_Test();
FutureTask ft1 = new FutureTask<>(ctt);
FutureTask ft2 = new FutureTask<>(ctt);
FutureTask ft3 = new FutureTask<>(ctt);
Thread th1 = new Thread(ft1, "线程1");
Thread th2 = new Thread(ft2, "线程2");
Thread th3 = new Thread(ft3, "线程3");
th1.start();
th2.start();
th3.start();
try {
System.out.println(th1.getName() + "的返回值:" + ft1.get());
System.out.println(th2.getName() + "的返回值:" + ft2.get());
System.out.println(th3.getName() + "的返回值:" + ft3.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
输出:
线程2总共10张,卖掉1张,剩余7
线程2总共10张,卖掉1张,剩余6
线程2总共10张,卖掉1张,剩余5
线程2总共10张,卖掉1张,剩余4
线程2总共10张,卖掉1张,剩余3
线程2总共10张,卖掉1张,剩余2
线程1总共10张,卖掉1张,剩余7
线程3总共10张,卖掉1张,剩余7
线程1总共10张,卖掉1张,剩余0
线程2总共10张,卖掉1张,剩余1
线程1的返回值:2
线程2的返回值:7
线程3的返回值:1
创建线程的三种方式的对比:
采用实现Runnable、Callable接口的方式创见多线程时:
优势:
线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。
Callable有返回值
在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,
从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
劣势:
编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法。
使用继承Thread类的方式创建多线程时:
优势:
编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。
劣势:
线程类已经继承了Thread类,不能再继承其他父类。