javase之多线程相关

多线程的创建方式

1)继承Thread类创建线程
2)实现Runnable接口创建线程
3)使用匿名内部类的形式创建线程
4)使用lambda表达式创建线程
5)使用callable和Future创建线程
6)使用线程池例如用Executor框架
7)spring @Async异步注解

继承Thread类创建线程

public class Thread01 extends Thread {
    /**
     * 线程执行的代码, 就是在run方法中 执行完毕 线程死亡
     */
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "<我是子线程>");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {

        }
        System.out.println(Thread.currentThread().getName() + "<阻塞完毕!>");
    }

    public static void main(String[] args) {
        //启动线程 调用start方法而不是run方法
        System.out.println(Thread.currentThread().getName());
        //调用start() 线程不会立即被cpu调度执行  就绪状态--等待cpu调度 线程从就绪到运行状态
        new Thread01().start();
        new Thread01().start();
        System.out.println("主线程执行完毕");
    }
}

实现Runnable接口创建线程

public class ThreadRunnable01 implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "<我是子线程>");
    }

    public static void main(String[] args) {
        //1 实现Runnable接口 启动线程
        new Thread(new ThreadRunnable01()).start();
        //2 使用匿名内部类创建线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "<我是子线程>");
            }
        }).start();
        //3 lambda表达式创建线程
        new Thread(() -> System.out.println(Thread.currentThread().getName() + "<我是子线程>"), "这个参数可以设置线程名称").start();
    }
}

使用callable和Future创建线程

callable和Future 线程可以获取到返回结果, 底层基于LockSupport

import java.util.concurrent.Callable;

public class ThreadCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println(Thread.currentThread().getName() + "开始执行..");
        try {
            Thread.sleep(3000);
        } catch (Exception e) {

        }
        System.out.println(Thread.currentThread().getName() + "返回1");
        return 1;
    }
}

测试类

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.locks.LockSupport;

public class Thread02 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ThreadCallable threadCallable = new ThreadCallable();
        FutureTask<Integer> integerFutureTask = new FutureTask<>(threadCallable);
        new Thread(integerFutureTask).start();
        //我的主线程需要等待我们的子线程给我的返回结果
        //调用get()方法获取返回结果, 底层会调用LockSupport.park()方法让我们的主线程阻塞, 等待子线程执行
        // 等子线程执行完毕之后, 执行LockSupport.unpark() 唤醒我们的主线程, 这样我们的主线程就可以拿到返回结果了
        Integer result = integerFutureTask.get();
        System.out.println(Thread.currentThread().getName() + "," + result);
        //juc 并发包
//        LockSupport.park(); //阻塞
//        LockSupport.unpark(); //唤醒

        //类似于下面的代码
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("我是子线程开始");
                LockSupport.park(); //让线程阻塞
                System.out.println("我是子线程结束");
            }
        });
        thread.start();
        try {
            Thread.sleep(3000);
        } catch (Exception e) {

        }
        LockSupport.unpark(thread); //唤醒线程
    }
}

使用线程池例如用Executor框架

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Thread03 {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "我是子线程");
            }
        });
        executorService.execute(() -> System.out.println(Thread.currentThread().getName() + "我是子线程"));
    }
}

spring @Async异步注解

直接在spring项目中,需要异步的方法上使用该注解就可以了(org.springframework.scheduling.annotation.Async),内部就会开辟一条线程独自去执行该方法

控制层

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@CrossOrigin
@RestController
@RequestMapping("/test")
@Slf4j
public class TestController {

    @Resource
    private TestService testService;

    @GetMapping("/test1")
    public String test1() {
        log.info("<log:1>");
        testService.async();
        log.info("<log:3>");
        return "test1";
    }
}

业务逻辑层

public interface TestService {
    void async();
}
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class TestServiceImpl implements TestService {
    @Override
    @Async
    public void async() {
        try {
            log.info("目标方法执行开始,阻塞3s");
            Thread.sleep(3000);
            log.info("<log:2>");
        } catch (Exception e) {

        }
    }
}

启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableAsync
public class DemoApplication {
	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
}

手写@Async异步注解

1.aop拦截只要在我们的方法上有使用到@MyAsync(自定义的注解)就单独的开启一个异步线程去执行我们的目标方法

自定义注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAsync {
}

AOP拦截

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
@Slf4j
public class ExtThreadAsyncAop {
    /**
     * 环绕通知
     * 拦截方法
     *
     * @param joinPoint
     */
    @Around(value = "@annotation(com.example.demo.async.MyAsync)")
    public void around(ProceedingJoinPoint joinPoint) {
        log.info(">环绕通知开始执行<");
        new Thread(() -> {
            try {
                joinPoint.proceed(); //目标方法
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }
        }).start();
        log.info(">环绕通知结束执行<");
    }
}

使用

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class TestServiceImpl implements TestService {
    @Override
//    @Async
    @MyAsync
    public void async() {
        try {
            log.info("目标方法执行开始,阻塞3s");
            Thread.sleep(3000);
            log.info("<log:2>");
        } catch (Exception e) {

        }
    }
}

Synchronized锁的基本用法

1.修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码快前要获得给定对象 的锁
2.修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例 的锁
3.修饰静态方法,作用于当前类对象(当前类.class)加锁,进入同步代码前要获得当前类对象 的锁

案例代码

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

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

            }
//            synchronized (this) {
//                if (count > 1) {
//                    count--;
//                    System.out.println(Thread.currentThread().getName() + "," + count);
//                }
//            }
//            cal();
            cal2();
        }
    }

    /**
     * 如果将synchronized锁加在实例方法上, 则使用this锁
     */
    private synchronized void cal() {
        synchronized (this) { //写在实例方法上相当于写在这里面
        }
        if (count > 1) {
            count--;
            System.out.println(Thread.currentThread().getName() + "," + count);
        }
    }

    /**
     * 如果将synchronized锁加在静态方法上, 类名.class
     */
    private static synchronized void cal2() {
        synchronized (ThreadCount.class) { //写在静态方法上相当于写在这里面
        }
        if (count > 1) {
            count--;
            System.out.println(Thread.currentThread().getName() + "," + count);
        }
    }

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

线程如何实现同步

线程如何保证线程安全性问题

1.使用synchronized锁, JDK1.6开始 锁的升级过程 偏向锁 -> 轻量级锁 -> 重量级锁
2.使用Lock锁 (JUC并发包中), 需要自己实现锁的升级过程, 底层是基于aqs+cas实现
3.使用Threadlocal, 需要注意内存泄漏的问题
4.原子类CAS非阻塞式 (基于CAS可以纯手写一个Lock锁)

wait()、notify()

wait方法,阻塞锁对象, 必须在锁中使用,否则会报错

notify唤醒锁对象

public class Test {
    private Object objectLock = new Object();

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

    public void print() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (objectLock) {
                    System.out.println(Thread.currentThread().getName() + ">1<");
                    try {
                        /**
                         * this.wait() 释放锁的资源, 同时当前线程会阻塞
                         * this.wait()、notify() 需要放到 synchronized 同步代码块中使用
                         */
                        objectLock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ">2<");
                }
            }
        }).start();

        try {
            Thread.sleep(3000);
            synchronized (objectLock) {
                /**
                 * 主线程3s之后 唤醒子线程
                 */
                objectLock.notify();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

wait、notify生产者与消费者模型

public class Test {
    /**
     * 共享对象Res
     */
    class Res {
        public String username;
        public char sex;
        /**
         * false 输入线程, 输入值
         * true  输出线程, 输出值
         */
        public 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) {
                    if (res.flag) {
                        try {
                            res.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    if (count == 0) {
                        res.username = "马牛逼";
                        res.sex = '男';
                    } else {
                        res.username = "小微";
                        res.sex = '女';
                    }
                    //输出线程 输出值
                    res.flag = true;
                    //唤醒输出线程
                    res.notify();
                }
                count = (count + 1) % 2;
            }
        }
    }

    class outputThread extends Thread {
        private Res res;

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

        @Override
        public void run() {
            while (true) {
                synchronized (res) {
                    if (!res.flag) {
                        //如果 res.flag = false 则 输出的线程 主动释放锁, 同时会阻塞该线程
                        try {
                            res.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println(res.username + "," + res.sex);
                    //输出完毕, 交给我们的输入线程继续输入
                    res.flag = false;
                    //唤醒输入线程
                    res.notify();
                }
            }
        }
    }

    public void print() {
        Res res = new Res();
        new inputThread(res).start();
        new outputThread(res).start();
    }

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

join同步

waitset集合: 调用wait方法后的线程会放到该集合中
blockedThreads: 存放我们阻塞, 没有获取到锁的线程, 它的集合名称叫做entrylist


好比四个线程去抢一个锁的时候,第一个线程抢到了,其他三个线程通过内部各种方式都没有获取到的话,就会被放到blockedThreads中,如果线程一,一旦释放完锁(调用wait或者执行结束)之后,就会唤醒正在阻塞的其他三个线程,进入到一个获取锁的状态

线程中一旦调用了wait方法,意味着这个线程,主动释放cpu执行权、释放锁,当前该线程变成了阻塞,会被放在waitset集合中,当调用notify的时候,唤醒该线程,意味着该线程又需要重新进入到一个获取锁的状态,重新去竞争锁的资源,相当于调用notify的时候从waitset集合移动到blockedThreads中,一旦需要的锁被释放的时候,所有没有获取到锁,正在等待的线程都会去竞争

代码

public class Test2 {
    public static void main(String[] args) {
        //t1 run方法执行完毕 唤醒---t2 正在等待
        Thread t1 = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "线程");
        }, "t1");
        //t2需要等待t1执行完毕
        Thread t2 = new Thread(() -> {
            try {
                //t1.wait(); t2调用t1(this锁).wait() 主动释放this锁 同时t2线程变为阻塞状态
                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();
    }
}

join原理

join底层原理是基于wait封装的, 唤醒的代码在 jvm Hotspot 源码中, 当jvm在关闭线程之前会检测线程阻塞在t1线程对象上的线程, 然后执行notifyAll(), 这样t2就被唤醒了

Join/Wait与sleep之间的区别

sleep(long)方法在睡眠时不释放对象锁
join(long)方法先执行另外的一个线程, 在等待的过程中释放对象锁, 底层是基于wait()
wait(long)方法在等待的过程中释放对象锁, 需要我们在 synchronized 中使用

多线程底层七种状态

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

新建状态(new Thread()) 调用start()-> 就绪状态 (等待cpu调度执行)-> 运行状态(执行run方法) -> 终止状态(run方法执行完毕)

运行状态(执行run方法) -> 没有获取到锁,会重新进入获取到锁的状态 -> 就绪状态
运行状态(执行run方法) -> 等待超时状态(sleep(long),wait(long),join(long)) 到时间/调用唤醒的方法就会到下一个状态 -> 就绪状态
运行状态(执行run方法) -> 等待状态(wait(),join(),Locksupport.park()) -> 调用(notify(),notifyAll(),Locksupport.unpark()) -> 就绪状态


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


Sleep  主动释放cpu执行权 休眠一段时间 
运行状态→限时等待状态
限时等待状态→就绪状态→运行状态

Synchronized 没有获取到锁 当前线程变为阻塞状态
如果有线程释放了锁,唤醒正在阻塞没有获取到锁的线程

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

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

sleep()方法可以防止CPU飙高

守护线程和用户线程

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

1.守护线程是依赖于用户线程, 用户线程退出了, 守护线程也就会退出, 典型的守护线程如垃圾回收线程
2.用户线程是独立存在的, 不会因为其他用户线程退出而退出

终止线程

stop()和interrupt()

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

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操作会被中断或者返回特殊异常值, 达到终止线程的目的
    如果以上条件都不满足, 则会设置此线程的中断状态(标志位改为true)

代码

public class TestThread extends Thread {
    @Override
    public void run() {
        while (true) {
            try {
                //interrupt()外部有人调用该方法,会变成true, 跳出循环, 结束线程
                //标志位, 相当于一个标志, 也可以自己定义一个变量
                if (this.isInterrupted()) break;
//                System.out.println("1");
//                Thread.sleep(1000000000);
//                System.out.println("2");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

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

        }
//        thread.stop(); //这样可以终止 thread 线程, 线程直接从运行状态进入死亡状态, 但是不建议
        System.out.println(">>中断<<");
        //中断 阻塞或者正在运行的线程
        //如果该线程正在阻塞状态, 会抛出异常, 属于正常现象
        //该方法不会让线程停止, 只是改变该线程中的 isInterrupted 值
        thread.interrupt(); // isInterrupted 这个值默认为false, 调用该方法会变成true
    }
}

Lock锁

概述

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

相关Api:
使用ReentrantLock实现同步
lock()方法:上锁
unlock()方法:释放锁
使用Condition实现等待/通知 类似于wait()和notify()及notifyAll()
Lock锁底层基于AQS实现, 需要自己封装实现自旋锁

Synchronized --属于 JDK 关键字, 底层数据 C++虚拟机底层实现
Lock锁底层基于AQS实现  如果没有获取到锁 -- 变为重量级锁(直接就变为阻塞状态)
Synchronized  底层原理jdk6开始做了优化 -- 锁的升级过程(如果没有获取到锁, 会重试,重新获取锁,如果当前线程重试多次还获取不到锁的话, 才会变为重量级锁)
Lock锁 过程中, 获取了锁, 代码中就要释放锁

Lock锁: 重量级锁, 手动挡, 获取锁和释放锁都要自己定义
Synchronized锁: 比较轻量级, 自动挡, 获取和释放锁不需要管, 有个优化过程

new ConcurrentHashMap(); 1.7版本使用Lock锁保证线程安全问题, 1.8使用Synchronized

lock锁使用方式

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

public class Test5 {
    private Lock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        Test5 test = new Test5();
        test.print1();
        Thread.sleep(500);
        test.print2();
    }

    public void print1() {
        /**
         * Lock 获取锁 和释放锁, 需要开发人员自己定义
         */
        new Thread(() -> {
            try {
                lock.lock(); //获取锁
                System.out.println(Thread.currentThread().getName() + "获取锁成功");
                Thread.sleep(3000);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock(); //释放锁, 不释放t2线程一直获取不到
                System.out.println(Thread.currentThread().getName() + "释放锁");
            }
        }, "t1").start();
    }

    public void print2() {
        new Thread(() -> {
            try {
                System.out.println(Thread.currentThread().getName() + ". 1");
                lock.lock(); //获取锁
                System.out.println(Thread.currentThread().getName() + ". 2");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "t2").start();
    }
}

condition用法

相当于synchronized的wait()、notify()

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

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

    public static void main(String[] args) throws InterruptedException {
        Test6 test6 = new Test6();
        test6.cal();
        Thread.sleep(3000);
        test6.signal();
    }

    public void cal() {
        new Thread(() -> {
            try {
                /**
                 * await()主动释放锁, 同时当前线程变为阻塞状态
                 */
                lock.lock();//获取锁
                System.out.println("1");
                condition.await();
                System.out.println("2");
            } catch (Exception e) {

            } finally {
                lock.unlock(); //释放锁
            }
        }).start();
    }

    public void signal() {
        lock.lock();
        //相当于 this.notify(); 唤醒正在阻塞的线程
        condition.signal();
        lock.unlock();
    }
}

多线程的yield()方法

1.多线程yield会让线程从运行状态进入到就绪状态, 然后调度执行其他线程
2.具体的实现依赖于底层操作系统的任务调度器

public class Test7 extends Thread {
    public Test7(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 Test7("t1").start();
        new Test7("t2").start();
    }
}

多线程优先级

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 Test8 {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            int count = 0;
            while (true) {
                System.out.println(Thread.currentThread().getName() + ", " + count++);
            }
        }, "t1");
        Thread t2 = new Thread(() -> {
            int count = 0;
            while (true) {
                System.out.println(Thread.currentThread().getName() + ", " + count++);
            }
        }, "t2");
        t1.setPriority(Thread.MIN_PRIORITY);
        t2.setPriority(Thread.MAX_PRIORITY);
        t1.start();
        t2.start();
    }
}

wait()为什么在Object中

因为wait()方法需要结合 synchronized(对象锁) 使用, 而对象锁可以是任意对象, 只有放在Object父类里, 才可以在任意对象锁中拿到wait和notify

纯手写callable与FutureTask

自定义Callable接口

public interface MyCallable<V> {
    V call() throws Exception;
}

写Callable实现类

public class MyCallableImpl<V> implements MyCallable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println(Thread.currentThread().getName() + ", 子线程在执行耗时的代码");
        Thread.sleep(3000);
        //耗时代码执行完毕之后, 返回1
        return 1;
    }
}

自定义FutureTask

import java.util.concurrent.locks.LockSupport;

public class MyFutureTask<V> implements Runnable {
    private MyCallable<V> myCallable;

    private Object lock = new Object();
    private V result;
    private Thread cuThread;

    public MyFutureTask(MyCallable<V> myCallable) {
        this.myCallable = myCallable;
    }

    @Override
    public void run() {
        try {
            //线程需要执行代码
            result = myCallable.call();
        } catch (Exception e) {
            e.printStackTrace();
        }
        //如果子线程执行完毕 唤醒主线程 可以拿到返回结果
//        synchronized (lock) {
//            lock.notify();
//        }
        if (cuThread != null)
            //唤醒 cuThread
            LockSupport.unpark(cuThread);
    }

    public V get() {
        //获取子线程异步执行完毕之后的返回结果
        //主线程阻塞
//        synchronized (lock) {
//            try {
//                lock.wait();
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//        }
        //主线程阻塞
        cuThread = Thread.currentThread();
        LockSupport.park();
        return result;
    }
}

测试

public class Test {
    public static void main(String[] args) {
        MyCallable<Integer> myCallable = new MyCallableImpl<>();
        MyFutureTask<Integer> myFutureTask = new MyFutureTask<>(myCallable);
        new Thread(myFutureTask).start();
        Integer i = myFutureTask.get();
        System.out.println(i);
    }
}

纯手写日志框架设计原理

@Slf4j中
log.info() -> 缓存(队列) -> 将日志写入到磁盘中(单独线程从缓存队列中获取日志, 然后将日志写入磁盘中)
多线程操作

日志类

import com.mayikt.service.utils.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;

@Component
public class LogManage {
    /**
     * 缓存日志内容
     */
    private BlockingDeque<String> blockingDeque = new LinkedBlockingDeque<String>();
    private LogThread logThread;
    private String filePath = "D:\\log\\my.log";

    public LogManage() {
        logThread = new LogThread();
        logThread.start();
    }

    public void info(String log) {
        // 将日志缓存起来
        blockingDeque.offer(log);
    }

    class LogThread extends Thread {
        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(50);
                } catch (Exception e) {

                }
                // 缓冲池 批量写入到磁盘中
                String log = blockingDeque.poll();
                if (StringUtils.isEmpty(log)) {
                    continue;
                }
                // 需要继续获取消息
                StringBuilder sbLog = new StringBuilder(log);
                for (int i = 0; i < 9; i++) {
                    String logTemp = blockingDeque.poll();
                    if (StringUtils.isEmpty(logTemp)) {
                        break;
                    }
                    sbLog.append(logTemp);

                }
                String logAppend = sbLog.toString();
                if (!StringUtils.isEmpty(logAppend)) {
                    // 在将该日志写入到磁盘中
                    FileUtils.writeText(filePath, logAppend, true);
                }
            }
        }
    }
}

AOP切面

import com.alibaba.fastjson.JSON;
import com.my.service.manage.LogManage;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Objects;

@Aspect
@Component
@Slf4j
public class AopLog {
    private static final String START_TIME = "request-start";
    private SimpleDateFormat sdf4 = new SimpleDateFormat("yyyy年MM月dd日HH时mm分ss秒");
    @Autowired
    private LogManage logManage;

    /**
     * 切入点
     */
    @Pointcut("execution(public * com.my.service.*Service.*(..))")
    public void log() {
    }

    /**
     * 前置操作
     *
     * @param point 切入点
     */
    @Before("log()")
    public void beforeLog(JoinPoint point) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();

        logManage.info("【请求 时间】:" + sdf4.format(new Date()));
        logManage.info("【请求 URL】:" + request.getRequestURL());
        logManage.info("【请求 IP】:" + request.getRemoteAddr());
        logManage.info("【类名 Class】:" + point.getSignature().getDeclaringTypeName());
        logManage.info("【方法名 Method】:" + point.getSignature().getName());
        logManage.info("【请求参数 Args】:" +
                JSON.toJSONString(point.getArgs())
        );
//        log.info("【请求 时间】:{}", System.currentTimeMillis());
//        log.info("【请求 URL】:{}", request.getRequestURL());
//        log.info("【请求 IP】:{}", request.getRemoteAddr());
//        log.info("【类名 Class】:{},【方法名 Method】:{}", point.getSignature().getDeclaringTypeName(), point.getSignature().getName());
//        log.info("【请求参数 Args】:{},", point.getArgs());
    }

}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值