JUC详解(一)

JUC详解(一),第1张

JUC详解(一)

本次笔记来自狂神说和尚硅谷

JUC(一)

1.什么是JUC?

1.1 概念1.2 注意点1.3 题外话 2.线程的几个状态

2.1 6个状态2.2 wait/sleep区别 3.juc方式--Lock锁

3.1 传统synchronized3.2 Lock接口(公平锁和非公平锁)3.3 synchronized 和 Lock接口区别3.4 代码 4.生产者和消费者问题

4.1 多线程编程步骤:4.2 生产者和消费者问题Synchronized版 wait notify4.3 问题:虚假唤醒4.4 JUC版的生产者和消费者问题4.5 定制化通信 5. 8锁现象

5.1 标准情况下,两个线程先打印发短信还是打电话?5.2 send延迟4秒,两个线程先打印发短信还是打电话?5.3 增加了一个普通方法后! 先执行发短信还是Hello5.4 两个对象,两个同步方法, 发短信还是打电话?5.5 增加两个静态的同步方法,只有一个对象,先打印发短信?打电话?5.6 两个对象!增加两个静态的同步方法,先打印发短信?打电话?5.7 1个静态的同步方法,1个普通的同步方法,一个对象,先打印发短信?打电话?5.8 1个静态的同步方法,1个普通的同步方法,两个对象,先打印发短信?打电话?5.9 synchronized实现同步的基础 6.集合类不安全

6.1 List CopyOnWriteArrayList6.2 Set CopyOnWriteArraySet6.3 Map ConcurrentHashMap 7.Callable

7.1 与Runnable的区别7.2 Callable怎么启动7.3 代码

1.什么是JUC? 1.1 概念

JUC就是java.util .concurrent等等工具包的简称,如下:
这是一个处理线程的工具包。

java.util.current 主讲并发相关的和Callable([kənˈkʌrənt])
java.util.current.atomic主讲 原子性([əˈtɒmɪk])
java.util.current.locks 主讲锁

1.2 注意点

idea坏境必须都要一致8版本的,
不然用lambda表达式等等java8新特性用不了。

1.3 题外话

1.线程和进程
进程就是一个程序
一个进程往往可以包含多个线程,至少包含一个!

2.Java默认有几个线程?
2个,mian(主线程)、 GC(垃圾回收机制)。

3.java真的可以开启线程吗?
》不可以

因为java 没有权限去开线程,
只能通过本地方法去调 thread.java里的private native void start0();,
去调底层的c++ ,
java无法直接 *** 作硬件它是运行在虚拟机之上 。

4.并发和并行
并发(多线程 *** 作同一个资源)》CPU单核
并发编程的本质: 充分利用CPU的资源

并行(多个人一起行走)》CPU多核

用最高性能可以使用线程池

2.线程的几个状态 2.1 6个状态

Thread.State

public enum State {
//新生
    NEW,
//运行
    RUNNABLE,
//阻塞
    BLOCKED,
//等待,死死地等
    WAITING,
//超时等待,过时不候
    TIMED_WAITING,
//终止
    TERMINATED;
}
2.2 wait/sleep区别

1.来自不同的类
wait => Object
sleep => Thread

2.关于锁的释放
wait会释放锁, sleep睡觉了, 抱着锁睡觉,不会释放

3.使用的范围是不同的
wait必须在同步代码块中
sleep可以在任何地方睡

3.juc方式–Lock锁 3.1 传统synchronized

synchronized 本质:队列 ,锁

3.2 Lock接口(公平锁和非公平锁)

源码

  Lock l = ...; l.lock(); 
  try { 
  // access the resource protected by this lock  
  } 
  finally { l.unlock(); }

l.lock() 加锁
l.unlock() 解锁

接口的三个实现类
可重入锁(常用) ReentrantLock
读锁 ReentrantReadWriteLock.ReadLock
写锁 ReentrantReadWriteLock.WriteLock

Lock lock = new ReentrantLock();

ReentrantLock里面的源码

public ReentrantLock() {  sync = new NonfairSync(); } 

默认是非公平锁

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
} 

三元表达式:如果为true那就是公平锁,false即非公平锁

NonfairSync非公平锁
十分不公平:可以插队(默认),谁抢到就是谁的

但一般这个是公平锁 3h 3s 3s等3h
可能会出现一个线程全执行完了,其他线程饿死的情况
好处是效率高

FairSync公平锁
十分公平:不能够插队,必须先来后到

效率相对较低,相对于就是底层会礼貌的问一句,有个动作

3.3 synchronized 和 Lock接口区别

1.Synchronized内置的Java关键字 ,Lock 是一个Java类
2.Synchronized 无法判断获取锁的状态,Lyock可以判断是否获取到了锁
3.Synchronized 会自动释放锁, lock必须要手动释放锁!如果不释放锁,死锁
4.Synchronized 线程1 (获得锁,阻塞)、线程2 (等待,傻傻的等) ;
Lock锁就不一定会等待下去;
lock.tryLock() 尝试获取锁 等不到就结束了
5.Synchronized 可重 入锁,不可以中断的,非公平;
Lock ,可重入锁,可以判断锁,非公平(可以自己设置) ;
6.Synchronized 适 合锁少量的代码同步问题, Lock适合锁大量的同步代码!
Lock可以提高多个线程进行读 *** 作的效率

3.4 代码

synchronized

public class Test01 {
    public static void main(String[] args) {
    //并发:多线程 *** 作同一个资源类
        Ticket ticket = new Ticket();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
            //检40次票
        },"A").start();
        //匿名内部类使用lambda表达式
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"C").start();
        //一共有三个线程
    }
}
//资源类 oop
class Ticket{
    private int number = 30;
    //卖票的方式
    public synchronized void sale(){
        if (number > 0 ){
            System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "票,剩余" + number);
        }
    }
}

Lock

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Test02 {
    public static void main(String[] args) {
        Ticket2 ticket = new Ticket2();
        new Thread(()->{ for (int i = 0; i < 40; i++)  ticket.sale(); },"A").start();
        new Thread(()->{ for (int i = 0; i < 40; i++)  ticket.sale(); },"B").start();
        new Thread(()->{ for (int i = 0; i < 40; i++)  ticket.sale(); },"C").start();
    }
}

class Ticket2{
    private int number = 30;
    Lock lock = new ReentrantLock();
    public  void sale(){
        lock.lock();//加锁
        try {
            if (number > 0 ){
                System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "票,剩余" + number);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();//解锁
        }
    }
}
4.生产者和消费者问题 4.1 多线程编程步骤:

第一步创建资源类,在资源类创建属性和 *** 作方法
第二步在资源类 *** 作方法(1)判断(2)干活(3)通知
第三步创建多个线程,调用资源类的 *** 作方法
第四步:防止虚假唤醒问题

4.2 生产者和消费者问题Synchronized版 wait notify

线程之间的通信问题:
生产者和消费者问题! 等待唤醒,通知唤醒
线程交替执行 A B *** 作同一个变量 num=0
A num+1
B num-1

public class A {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();//+1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();//+1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
    }
}
class Data{
    private int number = 0;
    //+1
    public synchronized void increment() throws InterruptedException {
        if (number!=0){
            this.wait(); //等待 会释放锁 别的线程抢
        }  
        number++;
        System.out.println(Thread.currentThread().getName() + "=>" + number);
        //通知其他线程,我+1完毕了
        this.notifyAll();
    }
    //-1
    public synchronized void decrement() throws InterruptedException {
        if (number==0){
            this.wait(); //等待
        }
        number--;
        System.out.println(Thread.currentThread().getName() + "=>" + number);
        //通知其他线程,我-1完毕了
        this.notifyAll();
    }
}

A=>1
B=>0
A=>1
B=>0
… 》一共执行20次

4.3 问题:虚假唤醒

问题存在,如果有ABCD 4个线程就会出现错误 !(虚假唤醒)
wait的特点 :在哪里睡,就在哪里醒
原因:
假设线程c之前等待了,但后来又被唤醒了
会往下走,if只判断一次,不会生效。

详细原因:
虚假唤醒,实质上是一次唤醒了所有线程,而CPU执行了本来不该被执行的线程,比如库存为0后,本想让CPU执行生产者线程,但是CPU却执行了另一个被唤醒的消费者,而该消费者没有重新去判断库存,直接进行消费。

解决方法:
使用while,在解除阻塞时,需要直到满足条件才会执行接下来的代码。

虚假唤醒(wait)的概念:
线程也可以唤醒,而不会被通知,中断或超时,即所谓的虛假唤醒。虽然这在实践中很少会发生 ,但应用程序必须通过测试应该使线程被唤醒的条件来防范,并且如果条件不满足则继续等待。换句话说, 等待应该总是出现在循环中。

synchronized (obj) {
		while ()
				obj.wait(timeout);
		…// Perform action appropriate to condition
}

虚假唤醒就是当一个条件满足时,很多线程都被唤醒了,
但是只有其中部分是有用的唤醒,其它的唤醒都是无用功
》正确做法 if改为while判断

(1)if判断流水线状态为空时,线程被阻塞,
这时if判断就完成了,线程被唤醒后直接执行线程剩余 *** 作

(2)while判断流水线状态为空时,线程被阻塞,
这时的while循环没有完成,线程被唤醒后会先进行while判断

4.4 JUC版的生产者和消费者问题

》通过Lock类找到Condition

Lock替换synchronized方法和语句的使用,
Condition取代了对象监视器方法的使用。

Condition就等价于 wait和notify,里面专属的是await和signal

第一版代码:

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class B {
    public static void main(String[] args) {
        Data2 data = new Data2();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();//+1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();//-1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
       //省略CD 线程
    }
}

class Data2{
    private int number = 0;
    Lock lock = new ReentrantLock();//可重入锁
    Condition condition = lock.newCondition();
    //+1
    public void increment() throws InterruptedException {
        lock.lock();//上锁
        try {
            //业务代码 判断
            while (number!=0){
                condition.await();  //等待
            }
            //干活
            number++;
            System.out.println(Thread.currentThread().getName() + "=>" + number);
            //通知其他线程,我+1完毕了
            condition.signalAll();//唤醒全部
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();//开锁
        }

    }
    //-1
    public  void decrement() throws InterruptedException {
        lock.lock();//上锁
        try {
            //业务代码
            while (number==0){
                condition.await();  //等待
            }
            number--;
            System.out.println(Thread.currentThread().getName() + "=>" + number);
            //通知其他线程,我-1完毕了
            condition.signalAll();//唤醒全部
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();//开锁
        }
    }
}

注意这个结果虽然可以了,但还是有问题
它是随机的状态生成 ,几个线程不规律
如何让他有序执行 先A 再B 再C 再D

4.5 定制化通信

解决方法: Condition精准的通知和唤醒线程
给每个线程定义一个标识位,来完成定制化通信

第二版代码

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//A 执行完 调 B,B 执行完 调 C,C 执行完 调 A
public class C {
    public static void main(String[] args) {
        Data3 data = new Data3();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.printA();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.printB();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.printC();
            }
        },"C").start();
    }
}
class Data3{
    private Lock lock = new ReentrantLock();//锁
    private  Condition condition1 = lock.newCondition();//同步监视器
    private  Condition condition2 = lock.newCondition();//同步监视器
    private  Condition condition3 = lock.newCondition();//同步监视器
    //通过condition多个监视器来精确的通知执行访问顺序
    private int number = 1;//1》A 2》B 3》C 判断的类
    public void printA(){
        lock.lock();
        try {//业务,判断-》执行-》通知
            while (number!=1){
                condition1.await();//等待
            }
            System.out.println(Thread.currentThread().getName() + "=>AAA");
            //唤醒,唤醒指定的人,B
            number = 2;
            condition2.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void printB(){
        lock.lock();
        try {//业务,判断-》执行-》通知
            while (number!=2){
                condition2.await();//等待
            }
            System.out.println(Thread.currentThread().getName() + "=>BBB");
            //唤醒,唤醒指定的人,C
            number = 3;
            condition3.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void printC(){
        lock.lock();
        try {//业务,判断-》执行-》通知
            while (number!=3){
                condition3.await();//等待
            }
            System.out.println(Thread.currentThread().getName() + "=>CCC");
            //唤醒,唤醒指定的人,A
            number = 1;
            condition1.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

A=>AAA
B=>BBB
C=>CCC
。。。

5. 8锁现象

如何判断锁的是谁!是否是同一把锁,锁的范围
对象、Class

8锁,关于锁的8个问题

5.1 标准情况下,两个线程先打印发短信还是打电话?

》1/发短信 2/打电话

5.2 send延迟4秒,两个线程先打印发短信还是打电话?

》1/发短信 2/打电话

总结:
这边synchronized 锁的是当前的对象(this)
》调用send方法时,把当前对象锁住了,
call方法只能等待,结束之后才能调用
》因为是对象锁,一个对象只有一把锁,是同一把锁

import java.util.concurrent.TimeUnit;
public class Test1 {
    public static void main(String[] args) throws Exception {
        Phone phone = new Phone();
        //并不是因为A先执行的,而是有锁的存在
        new Thread(()->{
            try {
                phone.send();
            } catch (Exception e) { e.printStackTrace(); }
        },"A").start();
        TimeUnit.SECONDS.sleep(1);
        //等价于 Thread.sleep(1000)
//start方法什么时候创建不确定,所以加sleep,因为调了sleep,所以一定是唯一的值
        new Thread(()->{
            phone.call();
        },"B").start();
    }
}
class Phone{
    // synchronized 锁的对象是方法的调用者!
    // 两个方法用的是同一个锁,谁先拿到谁执行!
    public synchronized void send() throws Exception {
        TimeUnit.SECONDS.sleep(4);//延迟4秒
        System.out.println("发短信");
    }
    public synchronized void call(){ System.out.println("打电话"); }
}
5.3 增加了一个普通方法后! 先执行发短信还是Hello

》1/hello 2/发短信

总结:
hello就是个普通方法和锁无关 所以先执行

import java.util.concurrent.TimeUnit;
public class Test2 {
    public static void main(String[] args) throws Exception {
        Phone2 phone = new Phone2();
        new Thread(()->{
            try {
                phone.send();
            } catch (Exception e) { e.printStackTrace(); }
        },"A").start();
        //锁的存在
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
            phone.hello();
        },"B").start();
    }
}
class Phone2{
    // synchronized 锁的对象是方法的调用者!
    public synchronized void send() throws Exception {
        TimeUnit.SECONDS.sleep(4);//延迟4秒
        System.out.println("发短信");
    }
    //这里没有锁!不是同步方法,不受锁的影响
    public void hello(){ System.out.println("hello"); }
}
5.4 两个对象,两个同步方法, 发短信还是打电话?

》1/打电话 2/发短信

总结:
不是同一把锁 因为send方法等了四秒所以晚输出
和上厕所一样各上各的

import java.util.concurrent.TimeUnit;
public class Test3 {
    public static void main(String[] args) throws Exception {
        //两个对象 两个调用者 两个锁 按时间来的
        Phone3 phone1 = new Phone3();
        Phone3 phone2 = new Phone3();
        new Thread(()->{
            try {
                phone1.send();
            } catch (Exception e) {  e.printStackTrace(); }
        },"A").start();
        //锁的存在
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
            phone2.call();
        },"B").start();
    }
}
class Phone3{
    // synchronized 锁的对象是方法的调用者!
    public synchronized void send() throws Exception {
        TimeUnit.SECONDS.sleep(4);//延迟4秒
        System.out.println("发短信");
    }
    public synchronized void call(){ System.out.println("打电话"); }
}
5.5 增加两个静态的同步方法,只有一个对象,先打印发短信?打电话?

》1/发短信 2/打电话

5.6 两个对象!增加两个静态的同步方法,先打印发短信?打电话?

》1/发短信 2/打电话

总结:
锁的是当前类的Class对象(字节码)
》顺序是因为static 不能说一定是synchronized

import java.util.concurrent.TimeUnit;
public class Test4 {
    public static void main(String[] args) throws Exception {
        //两个对象的Class类模板只有一个,static, 锁的是CLass
        Phone4 phone1 = new Phone4();
        Phone4 phone2 = new Phone4();
        new Thread(()->{
            try {
                phone1.send();
            } catch (InterruptedException e) { e.printStackTrace(); }
        },"A").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
            phone2.call();
        },"B").start();
    }
}
// Phone4唯一的一个 Class对象
class Phone4{
    // synchronized 锁的对象是方法的调用者!
    // static 静态方法
    //类一加载就有了!锁的是Class  class模板
    public static synchronized void send() throws InterruptedException {
        TimeUnit.SECONDS.sleep(4);//延迟4秒
        System.out.println("发短信");
    }
    public static synchronized void call(){ System.out.println("打电话"); }
}
5.7 1个静态的同步方法,1个普通的同步方法,一个对象,先打印发短信?打电话?

》1/打电话2/发短信

5.8 1个静态的同步方法,1个普通的同步方法,两个对象,先打印发短信?打电话?

》1/打电话2/发短信

总结:
1.不是同一把锁
》两个方法用的不是同一个锁 不需要去等待 最快最先
2.synchronized对象锁,就是看对象是不是同一个
是的话谁在前面先拿到锁 谁就先执行里面的内容

3.如果有static修饰方法,都是代表class类对象,
即谁先拿到锁谁先执行内容

import java.util.concurrent.TimeUnit;
public class Test5 {
    public static void main(String[] args) throws Exception {
        ///两个对象的Class类模板只有一个,static, 锁的是CLass
        Phone5 phone1 = new Phone5();
        Phone5 phone2 = new Phone5();
        new Thread(()->{
            try {
                phone1.send();
            } catch (Exception e) { e.printStackTrace(); }
        },"A").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
            phone2.call();
        },"B").start();
    }
}
class Phone5{
    // 静态的同步方法 锁的是Class类模板
    public static synchronized void send() throws Exception {
        TimeUnit.SECONDS.sleep(4);//延迟4秒
        System.out.println("发短信");
    }
    //普通的同步方法   锁的调用者
    public synchronized void call(){ System.out.println("打电话"); }
}

5.9 synchronized实现同步的基础

Java中的每一个对象都可以作为锁。

具体为以下3种形式:
对于普通同步方法,锁是当前实例对象。
对于静态同步方法,锁是当前类的Class对象。
对于同步方法块,锁是Synchonized括号里配置的对象

6.集合类不安全

只要在并发下,用默认的集合,在集合里面都会出现这个错
java.util.ConcurrentModificationException 并发修改异常!

虽然有时候可能没出问题,但不是就代表多线程没问题

6.1 List CopyonWriteArrayList

解决方法:
1.List list = new Vector<>(); vector默认就是安全的,里面有Synchronized
但这个太老了,一般不用
2.List list = collections.synchronizedList(new ArrayList<>());(工具类)
用工具类让它变得安全
3.List list = new CopyOnWriteArrayList<>(); JUC解决方案 更牛逼
CopyonWrite 》写入时复制 简称Cow,它是计算机程序设计领域的一种优化策略;
注意:
多个线程调用的时候,list是唯一的,读取的时候是固定的,但写入的时候会存在覆盖的行为,在写入的时候避免覆盖,造成数据问题! 所以需要用到CopyOnWrite,写入的时候复制一份,写完在插进去

CopyonWriteArrayList 比 Vector 好在哪里?
Vector 用到 Synchronized 性能比较低
而前者用到了lock锁,效率和性能更高

代码:

import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
public class ListTest {
    public static void main(String[] args) {    
        List list= new CopyOnWriteArrayList<>();
        for (int i = 0; i < 10; i++) {
            //10个线程去调用
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,5));//生成随机字符串
                System.out.println(list);
            },String.valueOf(i)).start();
            //String.valueOf(int i) : 将 int 变量 i 转换成字符串
        }
    }
}

结果:(随机)

6.2 Set CopyonWriteArraySet

用HashSet set = new HashSet<>();可能就会报错。

解决方法:
1.Set set = Collections.synchronizedSet(new HashSet<>());
2. Set set = new CopyOnWriteArraySet<>();

import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
public class SetList {
    public static void main(String[] args) {
        Set set = new CopyOnWriteArraySet<>();
        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,5));//生成随机字符串
                System.out.println(set);
            },String.valueOf(i)).start();
        }
    }
}

题外话:

hashSet底层是什么?

public HashSet() { map = new HashMap<>(); }

add set本质就是map key是无法重复的!

public boolean add(E e) {  return map.put(e, PRESENT)==null; }
private static final Object PRESENT = new Object();//PRESENT是不变的值
6.3 Map ConcurrentHashMap

底层代码:
默认初始容量为16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 写成了位运算

最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;

默认的加载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;

Map map = new HashMap<>();
默认等价于》 new HashMap<>(16,0.75);

解决方法:
1.Map map = Collections.synchronizedMap(new HashMap<>());
2.Map map = new ConcurrentHashMap<>();
代码:

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class MapList {
    public static void main(String[] args) {
        Map map = new ConcurrentHashMap<>();
        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,5));//生成随机字符串
                System.out.println(map);
            },String.valueOf(i)).start();
        }
    }
}
7.Callable 7.1 与Runnable的区别

1.可以有返回值
2.可以抛出异常
3.方法不同,run()/call()
4. Runnable效率比Callable相对较低,用Callable更多

7.2 Callable怎么启动

Thread的实现接口是Runnable,所以只能接收它
Runnable有个实现类FutureTask,它就和Callable有关系。
FutureTask里面有个构造方法,里面的参数是Callable
最终:Callable通过FutureTask,可以调用Thread

Callable是无法直接启动的,那么怎么启动呢?
new Thread(new Runnable()).start();
等价于 new Thread(new FutureTask()).start();
所以new Thread(new FutureTask(Callable)).start();

7.3 代码
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //怎么启动Callable
        MyThread thread = new MyThread();
        FutureTask futureTask = new FutureTask<>(thread);//适配类
        new Thread(futureTask,"A").start();
        new Thread(futureTask,"B").start();
        //结果会被缓存,效率高 有两个线程但结果只有一个
        Integer o = futureTask.get();//获取Callable的返回结果
        //上面的get方法需要等待结果返回 如果下面的方法是个耗时的 *** 作,
        //那么就可能会产生阻塞,所以最好写在最后,或者使用异步通信来处理
        System.out.println(o);
    }
}
class MyThread implements Callable{
    @Override
    public Integer call() throws Exception {
        System.out.println("call()");
        return 1024;
    }
}

结果:
call()
1024

注意点:
1.有缓存
2.结果可能需要等待,会阻塞。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5707338.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存