深入理解多线程底层执行原理(二)

9、多线程线程安全问题

什么是线程安全问题?

多线程同时对同一个全局变量做写的操作,可能会受到其他线程的干扰,就会发生线程安全性问题。

全局变量----java内存结构

什么是写操作------修改

当多个线程共享同一个全局变量,做写的操作时,可能会受到其他的线程干扰,发生线程安全问题。

public class ThreadCount implements Runnable {
    private static Integer count = 100;

    @Override
    public void run() {
        while (count > 1) {
            cal();
        }
    }

    private  void cal() {
        try {
            Thread.sleep(20);
        } catch (Exception e) {

        }
        count--;
        System.out.println(Thread.currentThread().getName() + "," + count);
    }


    public static void main(String[] args) {
        ThreadCount threadCount = new ThreadCount();
        Thread thread1 = new Thread(threadCount);
        Thread thread2 = new Thread(threadCount);
        thread1.start();
        thread2.start();
    }
}

同时执行概念

10、如何解决线程安全的问题

核心思想:上锁  分布式锁

在同一个jvm中,多个线程需要竞争锁的资源,最终只能够有一个线程能够获取到锁,多个线程同时抢同一把锁,谁(线程)能够获取到锁,谁就可以执行到该代码,如果没有获取锁成功 中间需要经历锁的升级过程如果一致没有获取到锁则会一直阻塞等待。

如果线程A获取锁成功 但是线程A一直不释放锁;线程B一直获取不到锁,则会一直阻塞等待。

代码从那一块需要上锁?-----可能会发生线程安全性问题的代码需要上锁。

Juc并发编程 锁 重入锁 悲观锁 乐观锁 公平锁 非公平锁

synchronize(this){
    count--;
    System.out.println(Thread.currentThread().getName() + "," + count);
}

线程0 线程1 同时获取 this锁,假设线程0 获取到this ,意味着线程1没有获取到锁

则会阻塞等待。等我们线程0 执行完count-- 释放锁之后  就会唤醒 线程1从新进入

到获取锁的资源。

获取锁与释放锁 全部都是有底层虚拟机实现好了。

对一块代码加锁缺点:

可能会影响到程序的执行效率。

如果是同一把锁 在多线程的情况下 最终只能够给一个线程使用。

如果有线程持有了该锁 意味着其他的线程 不能够在继续获取锁

核心思想:当多个线程共享同一个全局变量时,将可能会发生线程安全的代码

上锁,保证只有拿到锁的线程才可以执行,没有拿到锁的线程不可以执行,需要阻塞等待。

  1. 使用synchronized锁,JDK1.6开始 锁的升级过程 juc 18-25
  2. 使用Lock锁 ,需要自己实现锁的升级过程。底层是基于aqs实现
  3. 使用Threadlocal,需要注意内存泄漏的问题。
  4. 原子类 CAS 非阻塞式

11、synchronized锁的基本用法

在多线程的情况下 需要是同一个对象锁

Synchronized(对象锁)
{
     需要保证线程安全的代码

}

1.修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码快前要获得 给定对象 的锁。

2.修饰实例方法,作用于当前实例加锁,进入同步代码前要获得 当前实例 的锁

3.修饰静态方法,作用于当前类对象(当前类.class)加锁,进入同步代码前要获得 当前类对象 的锁

修饰代码块

修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得 给定对象 的锁。

public class ThreadCount implements Runnable {
    private static Integer count = 100;
    private String lock = "lock";

    @Override
    public void run() {
        while (count > 1) {
            cal();
        }
    }

    private void cal() {
        synchronized (this) {
            try {
                Thread.sleep(10);
            } catch (Exception e) {

            }
            count--;
            System.out.println(Thread.currentThread().getName() + "," + count);
        }

    }


    public static void main(String[] args) {
        ThreadCount threadCount = new ThreadCount();
        Thread thread1 = new Thread(threadCount);
        Thread thread2 = new Thread(threadCount);
        thread1.start();
        thread2.start();
    }
}

修饰实例方法

修饰实例方法,作用于当前实例加锁,进入同步代码前要获得 当前实例的锁

在实例方法上默认加上synchronized 默认使用this锁。

public class ThreadCount implements Runnable {
    private static Integer count = 100;
    private String lock = "lock";

    @Override
    public void run() {
        while (count > 1) {
            cal();
        }
    }

    private synchronized void cal() {
        try {
            Thread.sleep(10);
        } catch (Exception e) {

        }
        count--;
        System.out.println(Thread.currentThread().getName() + "," + count);
    }
    public static void main(String[] args) {
        ThreadCount threadCount = new ThreadCount();
        Thread thread1 = new Thread(threadCount);
        Thread thread2 = new Thread(threadCount);
        thread1.start();
        thread2.start();
    }
}

修饰静态方法

修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得 当前类对象的锁

默认使用当前类的类名.class 锁

public class ThreadCount implements Runnable {
    private static Integer count = 100;
    private static String lock = "lock";

    @Override
    public void run() {
        while (count > 1) {
            cal();
        }
    }

    private static void cal() {
        synchronized (ThreadCount.class) {
            try {
                Thread.sleep(10);
            } catch (Exception e) {

            }
            count--;
            System.out.println(Thread.currentThread().getName() + "," + count);
        }

    }


    public static void main(String[] args) {
        ThreadCount threadCount1 = new ThreadCount();
        ThreadCount threadCount2 = new ThreadCount();
        Thread thread1 = new Thread(threadCount1);
        Thread thread2 = new Thread(threadCount2);
        thread1.start();
        thread2.start();
    }
}

12、synchronized死锁问题

我们如果在使用synchronized 需要注意 synchronized锁嵌套的问题 避免死锁的问题发生。

案例:

public class DeadlockThread implements Runnable {
    private int count = 1;
    private String lock = "lock";

    @Override
    public void run() {
        while (true) {
            count++;
            if (count % 2 == 0) {
                // 线程1需要获取 lock 在获取 a方法this锁
                // 线程2需要获取this 锁在 获取B方法lock锁
                synchronized (lock) {
                    a();
                }
            } else {
                synchronized (this) {
                    b();
                }
            }
        }
    }

    public synchronized void a() {
        System.out.println(Thread.currentThread().getName() + ",a方法...");
    }

    public void b() {
        synchronized (lock) {
            System.out.println(Thread.currentThread().getName() + ",b方法...");
        }
    }

    public static void main(String[] args) {
        DeadlockThread deadlockThread = new DeadlockThread();
        Thread thread1 = new Thread(deadlockThread);
        Thread thread2 = new Thread(deadlockThread);
        thread1.start();
        thread2.start();
    }
}

synchronized 死锁诊断工具

D:\path\jdk\jdk8\bin\jconsole.exe

 

线程1 先获取到自定义对象的lock锁,进入到a方法需要获取this锁

线程2 先获取this锁,          进入到b方法需要自定义对象的lock锁

线程1 线程2 是在同时执行

线程1

线程2

先获取到自定义对象的lock锁

先获取this锁

需要线程2已经持有的this锁

线程1已经持有自定义对象的lock锁

13、springmvc 接口中使用synchronized

需要注意:

Spring MVC Controller默认是单例的 需要注意线程安全问题

单例的原因有二:

1、为了性能。

2、不需要多例。

@Scope(value = "prototype") 设置为多例子。

@RestController
@Slf4j
//@Scope(value = "prototype")
public class CountService {

    private int count = 0;

    @RequestMapping("/count")
    public synchronized String count() {
        try {
            log.info(">count<" + count++);
            try {
                Thread.sleep(3000);
            } catch (Exception e) {

            }
        } catch (Exception e) {

        }
        return "count";
    }
}

14、临界区

当多个线程读共享资源 读的过程中,没有任何问题,

在多个线程对共享资源读写操作时发生指令交错,就会发生线程安全问题

在多线程中如果存在对共享资源读写操作,该代码称作为临界区。

public class Thread08 extends Thread {
    int count = 0;

    @Override
    public void run() {
        // 该代码就是为临界区
        count++ ;
    }
}

竞争条件

多个线程在临界区内执行,由于代码的执行序列不同(指令)而导致结果无法预测,称之为发生了竞态条件

解决办法:

synchronized,Lock、原子类

15、字节码角度分析线程安全问题

线程安全问题:

  1. 字节码
  2. 上下文切换
  3. Jmm java内存模型

Java源代码 →编译成class文件

线程安全代码

public class Thread02 extends Thread {
    private static int sum = 0;

    @Override
    public void run() {
        sum();
    }

    public void sum() {
        for (int i = 0; i < 10000; i++) {
            sum++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread02 t1 = new Thread02();
        Thread02 t2 = new Thread02();
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(sum);
    }
}

字节码文件分析

javap -p -v Thread01.class

sum++

Getstatic ###  获取静态变量值sum

iconst_1 ##  准备一个常量1

Iadd   ### 自增

Putstatic ### 将修改后的值存入静态变量sum

Cpu上下文角度分析线程安全问题

 

分析:

共享变量值 sum=0

假设现在cpu执行到t1线程,t1线程执行到13行 就切换到另外t2线程执行,

t2线程将静态变量sum=0改成=sum=1 有切换回来执行t1线程 t1线程 使用之前获取

Sum=0 +1 赋值给共享变量sum ,则最终结果:sum=1.

但是现在sum++ 执行 最终结果是算了一次。

16、多线程线程之间通讯

等待/通知机制

等待/通知的相关方法是任意Java对象都具备的,因为这些方法被定义在所有对象的超类java.lang.Object上,方法如下:

1.notify() :通知一个在对象上等待的线程,使其从main()方法返回,而返回的前提是该线程获取到了对象的锁

2.notifyAll():通知所有等待在该对象的线程

3.wait():调用该方法的线程进入WAITING状态,只有等待其他线程的通知或者被中断,才会返回。需要注意调用wait()方法后,会释放对象的锁 。

Exception in thread "Thread-0" java.lang.IllegalMonitorStateException

at java.lang.Object.wait(Native Method)

at java.lang.Object.wait(Object.java:502)

at com.mayikt.thread.days02.Thread03.run(Thread03.java:16)

注意:wait,notify和notifyAll要与synchronized一起使用

wait/notify/notifyAll在Object类中

因为我们在使用synchronized锁 对象锁可以是任意对象,所以wait/notify/notifyAll需要放在Object类中。

wait/notify/简单的用法

public class Thread03 extends Thread {
    @Override
    public void run() {
        try {
            synchronized (this) {
                System.out.println(Thread.currentThread().getName() + ">>当前线程阻塞,同时释放锁!<<");
                this.wait();
            }
            System.out.println(">>run()<<");
        } catch (InterruptedException e) {

        }
    }

    public static void main(String[] args) {
        Thread03 thread = new Thread03();
        thread.start();
        try {
            Thread.sleep(3000);
        } catch (Exception e) {

        }
        synchronized (thread) {
            // 唤醒正在阻塞的线程
            thread.notify();
        }

    }
}

多线程通讯实现生产者与消费者

public class Thread04 {
    class Res {
        /**
         * 姓名
         */
        private String userName;
        /**
         * 性别
         */
        private char sex;
        /**
         * 标记
         */
        private boolean flag = false;
    }

    class InputThread extends Thread {
        private Res res;

        public InputThread(Res res) {
            this.res = res;
        }

        @Override
        public void run() {
            int count = 0;
            while (true) {
                synchronized (res) {
                    //flag = false  写入输入 flag = true 则不能写入数据 只能读取数据
                    try {
                        // 如果flag = true 则不能写入数据 只能读取数据 同时释放锁!
                        if (res.flag) {
                            res.wait();
                        }
                    } catch (Exception e) {

                    }
                    if (count == 0) {
                        this.res.userName = "余胜军";
                        this.res.sex = '男';
                    } else {
                        this.res.userName = "小薇";
                        this.res.sex = '女';
                    }
                    res.flag = true;
                    res.notify();
                }

                count = (count + 1) % 2;
            }
        }
    }

    class OutThread extends Thread {
        private Res res;

        public OutThread(Res res) {
            this.res = res;
        }


        @Override
        public void run() {
            while (true) {
                synchronized (res) {
                    try {
                        if (!res.flag) {
                            res.wait();
                        }
                    } catch (Exception e) {

                    }
                    System.out.println(res.userName + "," + res.sex);
                    res.flag = false;
                    res.notify();
                }
            }
        }
    }


    public static void main(String[] args) {
        new Thread04().print();
    }

    public void print() {
        Res res = new Res();
        InputThread inputThread = new InputThread(res);
        OutThread outThread = new OutThread(res);
        inputThread.start();
        outThread.start();
    }
}

/**
 * flag 默认值==false
 * flag false 输入线程 输入值    输出线程 先拿到锁 释放锁
 * flag true 输出线程 输出值
 */
public boolean flag = false;

17、Join/Wait与sleep之间的区别

sleep(long)方法在睡眠时不释放对象锁

join(long)方法先执行另外的一个线程,在等待的过程中释放对象锁 底层是基于wait封装的,

Wait(long)方法在等待的过程中释放对象锁

18、三个线程 T1,T2,T3,怎么确保它们按顺序执行?

Thread t1 = new Thread(() -> System.out.println(Thread.currentThread().getName() + ",线程执行"), "t1");
Thread t2 = new Thread(() -> System.out.println(Thread.currentThread().getName() + ",线程执行"), "t2");
Thread t3 = new Thread(() -> System.out.println(Thread.currentThread().getName() + ",线程执行"), "t3");
t1.start();
t2.start();
t3.start();

 

public class Thread05 {


    public    static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            try {
                Thread.sleep(3000);
            } catch (Exception e) {

            }
            System.out.println(Thread.currentThread().getName() + ",线程执行");
        }, "t1");
        Thread t2 = new Thread(() -> {
            try {
                t1.join();
            } catch (InterruptedException e) {

            }
            System.out.println(Thread.currentThread().getName() + ",线程执行");
        }, "t2");
        Thread t3 = new Thread(() -> {
            try {
                t2.join();
            } catch (InterruptedException e) {

            }
            System.out.println(Thread.currentThread().getName() + ",线程执行");
        }, "t3");
        t1.start();
        t2.start();
        t3.start();
    }
}

19、Join的底层原理如何实现

public class Thread06 {
    private Object object = new Object();

    public static void main(String[] args) throws InterruptedException {
        Thread06 thread06 = new Thread06();
        Thread thread = thread06.print();
        thread.start();
        try {
            Thread.sleep(3000);
            thread.interrupt();
        } catch (Exception e) {

        }

    }

    public Thread print() {
        Thread thread = new Thread(() -> {
            synchronized (object) {
                System.out.println("1");
                try {
                    object.wait(0);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("2");
            }
        });
        return thread;
    }


}

Join底层原理是基于wait封装的,唤醒的代码在jvm Hotspot 源码中 当

jvm在关闭线程之前会检测线阻塞在t1线程对象上的线程,然后执行notfyAll(),这样t2就被唤醒了。

多线程知识高级部分

20、多线程七种执行的状态

  • 初始化状态
  • 就绪状态
  • 运行状态
  • 死亡状态
  • 阻塞状态
  • 超时等待
  • 等待状态

  • start():调用start()方法会使得该线程开始执行,正确启动线程的方式。
  • wait():调用wait()方法,进入等待状态,释放资源,让出CPU。需要在同步快中调用。
  • sleep():调用sleep()方法,进入超时等待,不释放资源,让出CPU
  • stop():调用sleep()方法,线程停止,线程不安全,不释放锁导致死锁,过时。
  • join():调用sleep()方法,线程是同步,它可以使得线程之间的并行执行变为串行执行。
  • yield():暂停当前正在执行的线程对象,并执行其他线程,让出CPU资源可能立刻获得资源执行。
  • yield()的目的是让相同优先级的线程之间能适当的轮转执行
  • notify():在锁池随机唤醒一个线程。需要在同步快中调用。
  • nnotifyAll():唤醒锁池里所有的线程。需要在同步快中调用。

Sleep  主动释放cpu执行权 休眠一段时间

运行状态→限时等待状态

限时等待状态→就绪状态→运行状态

Synchronized 没有获取到锁 当前线程变为阻塞状态

如果有线程释放了锁,唤醒正在阻塞没有获取到锁的线程

从新进入到获取锁的状态

wait() 运行---等待状态

notify() 等待状态--阻塞状态(没有获取到锁的线程 队列)

---就绪状态→运行状态

 

21、守护线程与用户线程

java中线程分为两种类型:用户线程和守护线程。通过Thread.setDaemon(false)设置为用户线程;通过Thread.setDaemon(true)设置为守护线程。如果不设置次属性,默认为用户线程。

1.守护线程是依赖于用户线程,用户线程退出了,守护线程也就会退出,典型的守护线程如垃圾回收线程。

2.用户线程是独立存在的,不会因为其他用户线程退出而退出。

public class Thread01 {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName() + ",我是子线程");
                } catch (Exception e) {

                }
            }
        });
        /**
         * 1.setDaemon 设置为true 守护线程是依赖于用户线程,用户线程退出了,守护线程也就会退出,典型的守护线程如垃圾回收线程。
         * 2.setDaemon 设置为false 用户线程是独立存在的,不会因为其他用户线程退出而退出。
         */
        thread.setDaemon(true);
        thread.start();
        System.out.println("我是主线程,代码执行结束");
    }
}

22、多线程yield

主动释放cpu执行权

1.多线程yield 会让线程从运行状态进入到就绪状态,让后调度执行其他线程。

2.具体的实现依赖于底层操作系统的任务调度器

public Thread02(String name) {
    super(name);
}

@Override
public void run() {
    for (int i = 0; i < 50; i++) {
        if (i == 30) {
            System.out.println(Thread.currentThread().getName() + ",释放cpu执行权");
            this.yield();
        }
        System.out.println(Thread.currentThread().getName() + "," + i);
    }


}

public static void main(String[] args) {
    new Thread02("mayikt01").start();
    new Thread02("mayikt02").start();
}

23、多线程优先级

1.在java语言中,每个线程都有一个优先级,当线程调控器有机会选择新的线程时,线程的优先级越高越有可能先被选择执行,线程的优先级可以设置1-10,数字越大代表优先级越高

注意:Oracle为Linux提供的java虚拟机中,线程的优先级将被忽略,即所有线程具有相同的优先级。

所以,不要过度依赖优先级。

2.线程的优先级用数字来表示,默认范围是1到10,即Thread.MIN_PRIORITY到Thread.MAX_PRIORTY.一个线程的默认优先级是5,即Thread.NORM_PRIORTY

3.如果cpu非常繁忙时,优先级越高的线程获得更多的时间片,但是cpu空闲时,设置优先级几乎没有任何作用。

public class Thread03 {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            int count = 0;
            for (; ; ) {
                System.out.println(Thread.currentThread().getName() + "," + count++);
            }
        }, "t1线程:");
        Thread t2 = new Thread(() -> {
            int count = 0;
            for (; ; ) {
                System.out.println(Thread.currentThread().getName() + "," + count++);
            }
        }, "t2线程:");
        t1.setPriority(Thread.MIN_PRIORITY);
        t1.setPriority(Thread.MAX_PRIORITY);
        t1.start();
        t2.start();
    }
}

24、sleep防止CPU占用100%

sleep(long millis)  线程睡眠 millis 毫秒

sleep(long millis, int nanos)  线程睡眠 millis 毫秒 + nanos 纳秒

使用sleep方法避免cpu空转 防止cpu占用100%

public static void main(String[] args) {
    new Thread(() -> {
        while (true) {
            try {
                Thread.sleep(30);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }).start();
}

25、如何安全的停止一个线程

调用stop方法

Stop:中止线程,并且清除监控器锁的信息,但是可能导致 线程安全问题,JDK不建议用。 Destroy: JDK未实现该方法。

Interrupt线程中止

Interrupt 打断正在运行或者正在阻塞的线程。

1.如果目标线程在调用Object class的wait()、wait(long)或wait(long, int)方法、join()、join(long, int)或sleep(long, int)方法时被阻塞,那么Interrupt会生效,该线程的中断状态将被清除,抛出InterruptedException异常。

2.如果目标线程是被I/O或者NIO中的Channel所阻塞,同样,I/O操作会被中断或者返回特殊异常值。达到终止线程的目的。

如果以上条件都不满足,则会设置此线程的中断状态。

打断正在阻塞的线程

public static void main(String[] args) {
    Thread t1 = new Thread(() -> {
        try {
            Thread.sleep(5000);
        } catch (Exception e) {
              e.printStackTrace();
        }
    }, "t1");
    t1.start();
    try {
        Thread.sleep(1000);
    } catch (Exception e) {

    }
    System.out.println("打断子线程");
    //调用interrupt 打断正在阻塞的线程
    t1.interrupt();
    System.out.println("获取打断标记:" + t1.isInterrupted());
}

打断正在运行的线程

public class Thread06 extends Thread {
    @Override
    public void run() {
        while (true) {
            // 如果终止了线程,则停止当前线程
            if (this.isInterrupted()) {
                break;
            }
        }
    }

    public static void main(String[] args) {
        Thread06 thread06 = new Thread06();
        thread06.start();
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
        }
        thread06.interrupt();

    }
}

26、正确的线程中止-标志位

在上方代码逻辑中,增加一个判断,用来控制线程执行的中止。

public class Thread07 extends Thread {
    private volatile boolean isFlag = true;

    @Override
    public void run() {
        while (isFlag) {

        }
    }

    public static void main(String[] args) {
        Thread07 thread07 = new Thread07();
        thread07.start();
//        thread07.isFlag = false;

    }
}

27、Lock锁的基本使用

在jdk1.5后新增的ReentrantLock类同样可达到此效果,且在使用上比synchronized更加灵活

相关API:

使用ReentrantLock实现同步

lock()方法:上锁

unlock()方法:释放锁

使用Condition实现等待/通知 类似于 wait()和notify()及notifyAll()

Lock锁底层基于AQS实现,需要自己封装实现自旋锁。

Synchronized  ---属于JDK 关键字 底层属于  C++虚拟机底层实现

Lock锁底层基于AQS实现-- 变为重量级

Synchronized   底层原理---锁的升级过程

Lock 过程中 注意 获取锁 释放锁

ReentrantLock用法

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

/**
 * @author 余胜军
 * @ClassName Thread09
 * @qq 644064779
 * @addres www.mayikt.com
 * 微信:yushengjun644
 */
public class Thread09 implements Runnable {
    private int count = 100;
    private Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(30);
            } catch (Exception e) {

            }
            try {
                // 获取锁
                lock.lock();
                if (count > 1) {
                    count--;
                    System.out.println(Thread.currentThread().getName() + "," + count);

                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                // 释放锁
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        Thread09 thread09 = new Thread09();
        Thread t1 = new Thread(thread09);
        Thread t2 = new Thread(thread09);
        t1.start();
        t2.start();
    }
}

Condition用法

public class Thread10 {
    private ReentrantLock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public static void main(String[] args) {
        Thread10 thread10 = new Thread10();
        try {
            thread10.print();
            Thread.sleep(3000);
            thread10.signal();
        } catch (Exception e) {

        }
    }

    public void print() {
        new Thread(() -> {
            try {
                // 释放锁 同时当前线程阻塞
                lock.lock();
                System.out.println(Thread.currentThread().getName() + ",1");
                condition.await();
                System.out.println(Thread.currentThread().getName() + ",2");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }).start();
    }

    public void signal() {
        try {
            lock.lock();
            condition.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

28、多线程综合小案例

手写Callable与FutureTask模式

可以基于Callable+FutureTask可以实现异步线程执行 带返回结果。

LockSupport实现方式

定义Callable+FutureTask

public interface MayiktCallable<V> {
    V call();
}
public class MayiktFutureTask<V> implements Runnable {
    private MayiktCallable<V> mayiktCallable;
    private Thread cuThread;
    private V result;

    public MayiktFutureTask(MayiktCallable mayiktCallable) {
        this.mayiktCallable = mayiktCallable;
    }

    @Override
    public void run() {
        // 获取到mayiktCallable 执行返回结果
        result = mayiktCallable.call();
        // 如果 call方法执行完毕 则唤醒当前阻塞的线程
        if (cuThread != null)
            LockSupport.unpark(cuThread);
    }

    /**
     * 调用get方法 当前线程就会阻塞。
     */
    public V get() {
        cuThread = Thread.currentThread();
        // 阻塞当前线程
        LockSupport.park();
        return result;
    }
}
MayiktCallable mayiktCallable = new MayiktCallableImpl();
MayiktFutureTask<Integer> futureTask = new MayiktFutureTask<Integer>(mayiktCallable);
new Thread(futureTask).start();
Integer result = futureTask.get();
System.out.println(Thread.currentThread().getName() + result);

wait()与notify

public class MayiktFutureTask<V> implements Runnable {
    private MayiktCallable<V> mayiktCallable;
    private Thread cuThread;
    private V result;
    private Object lock = new Object();

    public MayiktFutureTask(MayiktCallable mayiktCallable) {
        this.mayiktCallable = mayiktCallable;
    }

    @Override
    public void run() {
        // 获取到mayiktCallable 执行返回结果
        result = mayiktCallable.call();
        // 如果 call方法执行完毕 则唤醒当前阻塞的线程
        if (cuThread != null)
            synchronized (lock) {
                lock.notify();
            }
    }

    /**
     * 调用get方法 当前线程就会阻塞。
     */
    public V get() {
        cuThread = Thread.currentThread();
        synchronized (lock) {
            try {
                // 当前线程释放锁 变为阻塞状态
                lock.wait();
            } catch (Exception e) {
            }
        }
        return result;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

出世&入世

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值