实现多线程的三种方法
方法一
继承Thread类
首先创建一个类继承Thread类
只有继承Thread才具备争抢资源的能力
package com.it04.Thread;
public class TestThread extends Thread{
}
线程对象要争抢资源,线程需要一个任务才能争抢资源,这个任务要放在方法中,这个方法必须是重写Thread类中的run方法,将线程的任务/逻辑写在run方法中。
package com.it04.TestThread;
public class TestThread extends Thread{
@Override
public void run() {
//打印1-10
for (int i = 1; i <=10 ; i++) {
System.out.println(i);
}
}
}
创建一个测试类
在测试类中创建一个主线程与子线程,让它们两互相争抢资源
package com.it04.TestThread;
//测试类
public class Test {
//main方法,程序的入口
public static void main(String[] args) {
//创建其他线程,与主线程争抢资源
//具体的线程对象:子线程
TestThread thread = new TestThread();
//调用run方法,执行子线程中的任务
thread.run();
//主线程也打印1-10
for (int i = 1; i <=10 ; i++) {
System.out.println("主线程---"+i);
}
}
}
此时并没有出现争抢资源的一个状态,是因为run方法不能直接调用,直接调用会被当做一个普通方法。
想要子线程真正起作用需要启动线程:
调用Thread类中的start()方法
package com.it04.TestThread;
//测试类
public class Test {
//main方法,程序的入口
public static void main(String[] args) {
//创建其他线程,与主线程争抢资源
//具体的线程对象:子线程
TestThread thread = new TestThread();
//调用run方法,执行子线程中的任务
// thread.run();
thread.start();
//主线程也打印1-10
for (int i = 1; i <=10 ; i++) {
System.out.println("主线程---"+i);
}
}
}
设置读取线程名字
用setName,getName方法来进行设置读取
package com.it04.TestThread;
//测试类
public class Test {
//main方法,程序的入口
public static void main(String[] args) {
//创建其他线程,与主线程争抢资源
//具体的线程对象:子线程
TestThread thread = new TestThread();
//调用run方法,执行子线程中的任务
// thread.run();
thread.setName("子线程");
thread.start();
//主线程也打印1-10
for (int i = 1; i <=10 ; i++) {
System.out.println("主线程---"+i);
}
}
}
package com.it04.TestThread;
public class TestThread extends Thread{
@Override
public void run() {
//打印1-10
for (int i = 1; i <=10 ; i++) {
System.out.println(this.getName()+i);
}
}
}
给主线程设置名字
使用Thread.currentThread.setName与getName
//主线程也打印1-10
Thread.currentThread().setName("主线程----");
for (int i = 1; i <=10 ; i++) {
System.out.println(Thread.currentThread().getName()+i);
案例:购买火车票
每个窗口都是一个线程对象:
创建一个类继承Thread类,重写run方法
每个对象执行的代码放入run方法中
package com.it04.BuyTicketThread;
public class BuyTicketThread extends Thread{
//创建一个有参构造器未窗口设置名字
public BuyTicketThread(String name) {
super(name);
}
//共10张票
//用static让多个对象共享10张票
static int ticketNum = 10;
@Override
public void run() {
//每个窗口有100人抢票
for (int i = 1; i <=100 ; i++) {
//对票进行判断,票数大于0才抢票
if (ticketNum > 0){
System.out.println("在"+this.getName()+"买到了北京到上海的第"+ticketNum--+"张票");
}
}
}
}
创建测试类
package com.it04.BuyTicketThread;
public class Test {
public static void main(String[] args) {
//多个窗口抢票:三个窗口三哥线程对象
BuyTicketThread t1 = new BuyTicketThread("窗口1");
t1.start();
BuyTicketThread t2 = new BuyTicketThread("窗口2");
t2.start();
BuyTicketThread t3 = new BuyTicketThread("窗口3");
t3.start();
}
}
方法二
实现Runnable接口
实现接口里面的run方法
package com.it04.test;
public class TestThread implements Runnable{
public void run() {
//打印1-10数字
for (int i = 1; i <=10 ; i++) {
//要获取线程名字,只能使用Thread.currentThread().getName()
System.out.println(Thread.currentThread().getName()+"----"+i);
}
}
}
创建测试类
package com.it04.test;
public class Test {
public static void main(String[] args) {
//创建子线程对象
TestThread thread = new TestThread();
//此时要将线程启动,但是TestThread并没有Thread方法
//就需要创建一个Thread对象,此时通过构造器将它们关联起来
//就可以使用start()方法了
//因为Thread这个构造器里面可以传入一个Runnable接口的一个实现类
//而thread刚好是实体类的一个对象
// public Thread(Runnable target) {
// init(null, target, "Thread-" + nextThreadNum(), 0);
// }
Thread thread1 = new Thread(thread,"子线程");
thread1.start();
//主线程打印1-10
for (int i = 1; i <=10 ; i++) {
System.out.println(Thread.currentThread().getName()+"-----"+i);
}
}
}
案例:购买火车票
创建一个类实现Runnable接口,实现run方法
package com.it04.test01;
public class ByTicketThread implements Runnable{
int ticketNum=10;
public void run() {
for (int i = 1; i <=100 ; i++) {
if (ticketNum > 0){
System.out.println("在"+Thread.currentThread().getName()+"买到了北京到上海的第"+ticketNum--+"张票");
}
}
}
}
创建测试类
package com.it04.test01;
public class Test {
public static void main(String[] args) {
//定义一个线程对象
ByTicketThread thread = new ByTicketThread();
//窗口1
Thread thread1 = new Thread(thread,"窗口1");
thread1.start();
//窗口2
Thread thread2 = new Thread(thread,"窗口2");
thread2.start();
//窗口3
Thread thread3 = new Thread(thread,"窗口3");
thread3.start();
}
}
在实际开发中,方式1继承Thread类,与方式2实现Runnable接口,用的多的应该是方式2
- 方式1 有java单继承的局限性,因为继承了Thread类,就不能在继承其他类了
- 方式2 共享资源的能力会比较强,不需要用static来修饰
方法三
实现Callable接口
对比第一种继承Thread和第二种实现Runnable接口创建线程的方式,,都需要一个run方法,但是这个run方法有它的不足
- 没有返回值,所以对象返回值的类型只能是void
- 不能抛出异常
基于上面的不足,在JDK1.5之后就出现了第三种创建线程的方式:实现Callable接口
package com.it04.test02;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class TestRandomNum implements Callable<Integer> {
/*
*
1.实现Callable接口,可以不带泛型,如果不带泛型,那么call方式的返回值就是Object类型
2.如果带泛型,那么call的返回值就是泛型对于的类型
3.call方法可以有返回值,可以抛出异常
V call() throws Exception;
*
* */
public Integer call() throws Exception {
//返回10以内的随机树
return new Random().nextInt(10);
}
}
class Test{
public static void main(String[] args) throws ExecutionException, InterruptedException {
//定义一个线程对象
TestRandomNum num = new TestRandomNum();
//需要使用FutureTask才能转换为Runnable接口
//因为FutureTask的父类RunnableFuture继承了Runnable接口
FutureTask ft = new FutureTask(num);
//由于Thread的构造器里面有一个可以传Runnable接口的实现类
Thread thread = new Thread(ft);
thread.start();
//获取线程的返回值
//使用ft里面的get方法来获取返回值
Object o = ft.get();
System.out.println(o);
}
}
实现Callable接口
优点:1 有返回值 2能抛出异常
缺点: 线程创建比较麻烦
线程的生命周期

版权声明:本文为Falling_stars_原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。