多线程
多线程的实现(重点)
继承Thread类
-
自定义线程类继承Thread类
-
重写run()方法,编写线程执行体
public class TestThread extendx Thread{ @Override public void run() {} }
-
创建线程对象,调用start()方法启动线程
public static void main(String[] args) { //创建一个线程对象 TestThread testThread = new TestThread(); //调用start()方法开启线程 testThread.start(); }
实现Runnable接口
-
定义MyRunnable类实现Runnable接口
-
实现run()方法,编写线程执行体
public class MyRunnable implements Runnable{ @Override public void run() {} }
-
创建线程对象,调用start()方法启动线程
public static void main(String[] args) { //创建runnable接口的实现类对象 TestThread3 testThread3 = new TestThread3(); //创建线程对象,通过线程对象来启动我们的线程,代理 // Thread thread = new Thread(testThread3); // thread.start(); //简化 new Thread(testThread3).start(); }
实现Callable接口(了解即可)
-
实现Callable接口,需要返回值类型
-
重写call方法,需要抛出异常
public class TextCallable2 implements Callable<Boolean> { @Override public Boolean call() throws Exception {} return true; }
-
创建目标对象
-
创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
-
提交执行:Future result1 = ser.submit(t1);
-
获取结果:boolean r1 = result1.get();
-
关闭服务:ser.shutdownNow();
public static void main(String[] args) throws ExecutionException, InterruptedException { //创建目标对象 TextCallable2 t1 = new TextCallable2(); TextCallable2 t2 = new TextCallable2(); //创建执行服务 ExecutorService ser = Executors.newFixedThreadPool(2); //提交执行 Future<Boolean> f1 = ser.submit(t1); Future<Boolean> f2 = ser.submit(t2); //获取结果 boolean b1 = f1.get(); boolean b2 = f2.get(); //关闭服务 ser.shutdownNow(); }
总结
-
继承Thread类
- 子类继承Thread类具备多线程能力
- 启动线程:子类对象 . start()
- 不建议使用: 避免OPP单继承局限性
-
实现Runnable接口
- 实现接口Runnable具有多线程能力
- 启动线程:传入目标对象+Thread对象 . start()
- 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
-
实现Callable接口
- 了解即可
Lambda表达式的推导
推导过程
1. 实现类
public class TestLambda {
//主方法
public static void main(String[] args) {
ILike like = new Like();
like.Lambda();
}
}
// 定义一个函数式接口
interface ILike{
void Lambda();
}
// 实现类
class Like implements ILike{
@Override
public void Lambda() {
System.out.println("i like lambda");
}
}
- 静态内部类
public class TestLambda {
// 静态内部类
static class Like implements ILike{
@Override
public void Lambda() {
System.out.println("i like lambda");
}
}
// 主方法
public static void main(String[] args) {
ILike like = new Like();
like.Lambda();
}
}
// 定义一个函数式接口
interface ILike{
void Lambda();
}
- 局部内部类
public class TestLambda {
//主方法
public static void main(String[] args) {
// 局部内部类
class Like implements ILike{
@Override
public void Lambda() {
System.out.println("i like lambda");
}
}
ILike like = new Like();
like.Lambda();
}
}
// 定义一个函数式接口
interface ILike{
void Lambda();
}
- 匿名内部类
public class TestLambda {
//主方法
public static void main(String[] args) {
// 匿名内部类: 没有类的名称,必须借助接口或者父类
ILike like = new ILike() {
@Override
public void Lambda() {
System.out.println("i like lambda");
}
};
like.Lambda();
}
}
// 定义一个函数式接口
interface ILike{
void Lambda();
}
- lambda表达式
public class TestLambda {
//主方法
public static void main(String[] args) {
// lambda表达式
ILike like =()-> {
System.out.println("i like lambda");
};
like.Lambda();
}
}
// 定义一个函数式接口
interface ILike{
void Lambda();
}
5.1 lambda表达式有参数
public class TestLambda {
//主方法
public static void main(String[] args) {
// lambda表达式
ILike like =(int a)-> {
System.out.println("i like lambda --> " + a);
};
like.Lambda(520);
}
}
// 定义一个函数式接口
interface ILike{
void Lambda(int a);
}
5.2. lambda简化 :
-
去掉括号
只有一个参数时能简化去掉括号 和 参数类型
ILike like =a -> { System.out.println("i like lambda"+a); }; like.Lambda(520);
如果有多个参数,不能去掉括号
ILike like = (a,b,c) -> { System.out.println("i like lambda"+a); }; like.Lambda(520,true,"man");
-
去掉大括号
只有一行代码的情况下才能简化成为一行.
ILike like =(a,b,c)-> System.out.println("i like lambda" + a);
like.Lambda(520,true,"man");
如果有多行,那么就用代码块包裹。
ILike like =(a,b,c)-> {
System.out.println("i like lambda --> " + a);
System.out.println("i like lambda too --> " + b);
};
like.Lambda(520,true,"man");
总结
- 使用lambda表达式的前提是接口为函数式接口
- lambda表达式只能有一行代码的情况下才能简化成为一行,如果有多行,那么就用代码块包裹
- 多个参数也可以去掉参数类型,要去掉就都去掉,必须加上括号
线程状态
停止线程
-
不推荐使用JDK提供的stop()、destroy()方法。【已废弃】
-
推荐线程自己停止下来
-
推荐使用一个标志位进行终止变量,当flag = false,则终止线程运行
public class TestStop implements Runnable{ // 1.设置一个标志位 private boolean flag = true; @Override public void run() { // 2.线程体使用该标识 while (flag){ System.out.println("run ... Thread"); } } // 3. 设置一个公开的方法停止线程,转换标志位 public void tingzhi(){ this.flag = false; }
线程休眠
- sleep(时间) 指定当前线程阻塞的毫秒值
- sleep存在异常InterruptedException
- sleep时间达到后线程进入就绪状态
- sleep可以模拟网络延时,倒计时等
- 每一个对象都有一个锁,sleep不会释放锁
网络延时
// 模拟网络延时 : 放大问题的发生性。
public class TestSleep implements Runnable{
//票数
private int ticketNums = 10;
@Override
public void run() {
while (true) {
if (ticketNums <= 0){
break;
}
//模拟延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("模拟延迟有问题");
}
System.out.println(Thread.currentThread().getName()+"-->拿到了第" + ticketNums-- + "票");
}
}
public static void main(String[] args) {
TestSleep testsleep = new TestSleep();
new Thread(testsleep,"学生").start();
new Thread(testsleep,"老师").start();
new Thread(testsleep,"黄牛").start();
}
}
倒计时
//模拟倒计时。。。
public class TestSleep2 {
public static void main(String[] args) {
try {
tenDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void tenDown() throws InterruptedException {
int num = 10;
while (true){
Thread.sleep(1000);
System.out.println(num--);
if(num <= 0){
break;
}
}
}
}
打印当前系统时间
public static void main(String[] args) {
//打印当前系统时间
Date startTime = new Date(System.currentTimeMillis());//获取系统当前时间
while (true){
try {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
startTime = new Date(System.currentTimeMillis());//更新当前时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
线程礼让
-
礼让线程,让当前正在执行的线程暂停,但不阻塞
-
将线程从运行状态转为就绪状态
-
让CPU重新调度,礼让不一定成功! 看CPU心情
// 礼让线程 // 礼让不一定成功,看CPU心情 public class TestYield { public static void main(String[] args) { MyYield myYield = new MyYield(); new Thread(myYield,"A").start(); new Thread(myYield,"B").start(); } } class MyYield implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"线程开始执行"); Thread.yield();//礼让 System.out.println(Thread.currentThread().getName()+"线程停止执行"); } }
线程强入
-
Join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞
-
可以想象成插队
//Join方法(插队) public class TestJoin implements Runnable { @Override public void run() { try { Thread.sleep(100);// 让VIP晚来一会儿 } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 200; i++) { System.out.println(Thread.currentThread().getName()+"我要先执行"+i+"次"); } } public static void main(String[] args) throws InterruptedException { //启动我们的线程 TestJoin testJoin = new TestJoin(); Thread thread = new Thread(testJoin, "VIP:"); thread.start(); //主线程 for (int i = 0; i < 100; i++) { if (i == 50){ thread.join();//插队 } System.out.println("正在执行"+i+"次"); } } }
线程状态的观测
- NEW: 尚未启动的线程处于此状态。
- RUNNABLE: 在Java虚拟机中执行的线程处于此状态
- BLOCKED: 被阻塞等待监视器锁定的线程处于此状态
- WAITING: 正在等待另一个线程执行特定动作的线程处于此状态
- TIMED_WAITING: 正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
- TERMINATED:已退出的线程处于此状态。
- 线程中断或者结束,一旦进入死亡状态,就不能再次启动
//观察测试线程的状态
public class TestState {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
for (int i = 0; i <= 5; i++) {
try {
Thread.sleep(1000);
System.out.println(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("");
});
//观察状态
Thread.State state = thread.getState();
System.out.println(state); //NEW
//观察启动后
thread.start();//启动线程
state = thread.getState();//观察
System.out.println(state);//RUNNABLE
while (state != Thread.State.TERMINATED){//只要线程不终止,就一直输出状态
Thread.sleep(100);
state = thread.getState();//更新线程状态
System.out.println(state);//输出状态
}
}
}
线程优先级
- 线程的优先级用数字来表示,范围从1~10.
- Thread.MIN_PRIORITY = 1;
- Thread.MAX_PRIORITY = 10;
- Thread.NORM_PRIORITY = 5;
- 优先级小于0 或者 大于10,会报错
- 使用一下方式改变或获取优先级
- getPriority() . setPriority(int xxx)
- 优先级的设定建议在start()调度前
- 优先级低只是意味着获取调度的概率低,并不是优先级低就不会被调度。这都是看CPU的调度
//测试线程的优先级
public class TestPriority {
public static void main(String[] args) {
//主线程默认优先级
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
MyPriority myPriority = new MyPriority();
Thread t1 = new Thread(myPriority);
Thread t2 = new Thread(myPriority);
Thread t3 = new Thread(myPriority);
Thread t4 = new Thread(myPriority);
Thread t5 = new Thread(myPriority);
Thread t6 = new Thread(myPriority);
//先设置优先级,在启动
t1.start();
t2.setPriority(1);
t2.start();
t3.setPriority(4);
t3.start();
t4.setPriority(Thread.MAX_PRIORITY);//MAX_PRIORITY=10
t4.start();
t5.setPriority(8);
t5.start();
t6.setPriority(6);
t6.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-->"+ Thread.currentThread().getPriority());
}
}
守护线程
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
- 如,后台记录操作日志,监控内存,垃圾回收等待…
//测试守护线程
public class TestDaemon {
public static void main(String[] args) {
Bodyguard bodyguard = new Bodyguard();
Me me = new Me();
Thread thread = new Thread(bodyguard);
//开启守护线程
thread.setDaemon(true);// 默认是false表示是用户线程,正常的线程都是用户线程。。。
thread.start();// 守护线程启动
new Thread(me).start();// 我 用户线程启动...
}
}
// 卫士
class Bodyguard implements Runnable{
@Override
public void run() {
while (true){
System.out.println("我将守护你");
}
}
}
//我
class Me implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("我已存活"+i+"年");
}
System.out.println("==== goodbye world ====");
}
}
线程同步(重点)
synchronized 方法 和 synchronized块
- synchronized 方法
同步方法: public synchronized void method (int arg){}
缺点:消耗资源
/不安全买票
//线程不安全,有负数,有买到同一张票
public class UnsafeBuyTicket {
public static void main(String[] args) {
BuiTicket buiTicket = new BuiTicket();
new Thread(buiTicket,"张三").start();
new Thread(buiTicket,"李四").start();
new Thread(buiTicket,"王五").start();
}
}
class BuiTicket implements Runnable{
//票
private int ticketNums = 10;
boolean flog = true;
@Override
public void run() {
//买票
while (flog){
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//synchronized 同步方法,锁的是this
private synchronized void buy() throws InterruptedException {
if(ticketNums <= 0){
flog = false;
return;
}
//模拟演示
Thread.sleep(1000);
//买票
System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
}
}
- synchronized块
同步块: synchronized(Obj){ }
//不安全取钱
public class UnsafeBank {
public static void main(String[] args) {
Account account = new Account(1000,"存款");
Drawing you = new Drawing(account,50,"你");
Drawing hem = new Drawing(account,100,"她");
you.start();
hem.start();
}
}
//账户
class Account{
int money; //余额
String name;//卡名
public Account(int money, String name){
this.money = money;
this.name = name;
}
}
//银行:模拟取款
class Drawing extends Thread{
Account account;//账户
//取了多少钱
int drawingMoney;
//现在
int nowMoney;
public Drawing(Account account,int drawingMoney,String name){
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
//取钱
@Override
public void run() {
//synchronized块
//锁的对象就是变化的量,需要增删改的对象
synchronized (account){
//判断有没有钱
if (account.money - drawingMoney < 0){
System.out.println(Thread.currentThread().getName() + "钱不够,取不了");
return;
}
//模拟延时
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卡内余额 = 余额 - 你取的钱
account.money = account.money - drawingMoney;
//你手里的钱
nowMoney = nowMoney + drawingMoney;
System.out.println(account.name + "余额" + account.money);
System.out.println(this.getName()+"手里的钱:" + nowMoney);
}
}
}
//不安全集合
public class UnsafeList {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
new Thread(()-> {
//synchronized块
synchronized (list) {
list.add(Thread.currentThread().getName());
}
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
JUC安全类型的集合
public class TestJUC {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
for (int i = 0; i < 100000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
死锁
-
产生死锁的四个必要条件:
- 互斥:在一段时间内某资源仅为一进程所占用
- 请求与保持:当进程因请求资源而阻塞时,对已获得的资源保持不放
- 不可剥夺:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放
- 循环等待:在发生死锁时,必然存在一个进程—资源的环形链
-
预防和破坏死锁
- 资源一次性分配:如果某进程所需的资源能够一次分配完则分配,否则不分配
- 申请新资源,必须先释放所拥有的的其他资源,若还需要此资源,需向系统重新申请
- 申请新资源失败,释放所拥有的的所有资源
- 对资源进行编号,申请编号小的资源必须先释放所拥有的编号大的资源
- 当产生死锁时,强制结束一些进程
public class DeadLock {
public static void main(String[] args) {
MakeUp g1 = new MakeUp(0,"张三");
MakeUp g2 = new MakeUp(1,"李四");
g1.start();
g2.start();
}
}
// 口红
class Lipstick{
}
// 镜子
class Mirror{
}
class MakeUp extends Thread{
//需要的资源只有一份,用static来保证只有一份
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice; // 选择
String girlName; // 使用化妆品的人
public MakeUp(int choice, String girlName) {
this.choice = choice;
this.girlName = girlName;
}
@Override
public void run() {
// 化妆
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//化妆,互相持有对方的锁,就是需要拿到对方的资源
private void makeup() throws InterruptedException {
if (choice == 0){
synchronized (lipstick){ //获得口红的锁
System.out.println(this.girlName+"获得口红的锁");
Thread.sleep(3000);
synchronized (mirror){// 一秒钟后想获得镜子
System.out.println(this.girlName + "获得镜子的锁");
}
}
}else {
synchronized (mirror){//获得镜子的锁
System.out.println(this.girlName+"获得镜子的锁");
Thread.sleep(6000);
synchronized (mirror){// 一秒钟后想获得口红
System.out.println(this.girlName + "获得口红的锁");
}
}
}
}
}
解决死锁:
//化妆,互相持有对方的锁,就是需要拿到对方的资源
private void makeup() throws InterruptedException {
if (choice == 0){
synchronized (lipstick){ //获得口红的锁
System.out.println(this.girlName+"获得口红的锁");
Thread.sleep(1000);
}
synchronized (mirror){// 一秒钟后想获得镜子
System.out.println(this.girlName + "获得镜子的锁");
}
}else {
synchronized (mirror){//获得镜子的锁
System.out.println(this.girlName+"获得镜子的锁");
Thread.sleep(2000);
}
synchronized (mirror){// 一秒钟后想获得口红
System.out.println(this.girlName + "获得口红的锁");
}
}
}
Lock(锁)
- 比较常用的是ReenTrantLock,可以显示加锁、释放锁。
class A{
//定义look值
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
//加锁
lock.lock();
try {
// 保证线程安全的代码;
}
finally {
//解锁
lock.unlock();
//如果同步代码有异常,要将unlock()写入finally语句块
}
}
}
- synchronized 和 Lock 的对比
- Lock是显式锁(手动开启和关闭锁),synchronized是隐式锁(出了作用域自动释放)
- Lock只有代码块锁,synchronized有代码块锁和方法锁
- Lock的性能更好,而且提供更多的子类(扩展性)
- 优先使用顺序:
- Lock > 同步代码块 > 同步方法
线程通信问题
生产者—消费者问题
方法名 | 作用 |
---|---|
wait( ) | 表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁。 |
wait( long tomeout ) | 指定等待的毫秒数 |
notify( ) | 唤醒一个处于等待状态的线程 |
notifyAll( ) | 唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度 |
解决方法1:利用缓冲区解决(管程法)
- 生产者:负责生产数据的模块(可能是方法,对象,线程,进程)
- 消费者:负责处理数据的模块(可能是方法,对象,线程,进程)
- 缓冲区:消费者不能直接使用生产者的数据,他们之间有个 “缓冲区”
生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据
//测试: 生产者消费者问题 --> 利用缓冲区解决:管程法
// 生产者,消费者,产品,缓冲区
public class TestPC {
public static void main(String[] args) {
SynContainer container = new SynContainer();
new Productor(container).start();
new Consumer(container).start();
}
}
//生产者
class Productor extends Thread{
SynContainer container;
public Productor(SynContainer container) {
this.container = container;
}
//生产
@Override
public void run() {
for (int i = 0; i < 100; i++) {
container.push(new Chicken(i));
System.out.println("生产了" +i+ "只鸡");
}
}
}
//消费者
class Consumer extends Thread{
SynContainer container;
public Consumer(SynContainer container) {
this.container = container;
}
//消费
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了"+container.pop().id+"只鸡");
}
}
}
//产品
class Chicken{
int id; //产品编号
public Chicken(int id){
this.id = id;
}
}
//缓冲区
class SynContainer{
//需要一个容器大小
Chicken[] chickens = new Chicken[10];
//容器计数器
int count = 0;
//生产者放入产品
public synchronized void push(Chicken chicken){
//如果容器满了,就需要等待消费者消费
if(count == chickens.length){
//通知消费者消费,生产者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果没有满,我们继续丢入产品
chickens[count] = chicken;
count++;
//可以通知消费者幸福了
this.notifyAll();
}
//消费者消费产品
public synchronized Chicken pop(){
//判断能否消费
if(count == 0){
//等待生产者生产,消费者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果可以消费
count--;
Chicken chicken = chickens[count];
//通知生产者生产
this.notifyAll();
return chicken;
}
}
解决方法2:信号灯法,标志位解决
//测试生产者和消费者问题2:信号灯法,标志位解决
public class TestPC2 {
public static void main(String[] args) {
TV tv = new TV();
new Player(tv).start();
new Watcher(tv).start();
}
}
//生产者 --> 演员
class Player extends Thread{
TV tv;
public Player(TV tv){
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if(i%2 == 0){
this.tv.play("节目播放中");
}else {
this.tv.play("广告播放中");
}
}
}
}
//消费者 --> 观众
class Watcher extends Thread{
TV tv;
public Watcher(TV tv){
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
tv.watch();
}
}
}
//产品 --> 节目
class TV{
//演员表演,观众等待 T
//观众观看,演员等待 F
String voice; // 表演的节目
boolean flag = true;
//表演
public synchronized void play(String voice){
if(!flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("演员表演了: " + voice);
//通知观众观看
this.notify();//通知唤醒
this.voice = voice;
this.flag = !this.flag;
}
//观看
public synchronized void watch(){
if(flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观众观看了:"+voice);
//通知演员表演
this.notifyAll();
this.flag = !this.flag;
}
}
线程池
-
思路:
- 提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。
-
好处:
- 提高响应速度
- 降低资源消耗
- 便于资源管理(…)
-
ExecutorService 和 Executors
-
ExecutorService : 真正的线程池接口。常见子类ThreadPoolExecutor
- void execute(Runnable command) : 执行任务/命令,没有返回值,一般用来执行Runnable
- Future submit(Callable task) : 执行任务,有返回值,一般用来执行Callable
- void shutdown() : 关闭连接池
-
Executors :工具类、线程池的工厂类,用于创建并返回不同类型的线程池
// 线程池
public class TestPool {
public static void main(String[] args) {
//1. 创建服务,创建线程池
//newFixedThreadPool 参数为:线程池大小
ExecutorService service = Executors.newFixedThreadPool(10);
//执行
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
// 2. 关闭链接
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
总结
//回顾总结线程的创建
public class ThreadNew {
public static void main(String[] args) {
new MyThread1().start();
new Thread(new MyThread2()).start();
FutureTask<Integer> futureTask = new FutureTask<>(new MyThread3());
new Thread(futureTask).start();
try {
Integer integer = futureTask.get();
System.out.println(integer);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
// 1.继承Thread类
class MyThread1 extends Thread{
@Override
public void run() {
System.out.println("MyThread1");
}
}
// 2.实现Runnable 接口
class MyThread2 implements Runnable{
@Override
public void run() {
System.out.println("MyThread2");
}
}
// 3.实现Callable接口
class MyThread3 implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("MyThread3");
return 100;
}
}
以上内容来源于:狂神说