栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

并发编程笔记(上)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

并发编程笔记(上)

并发编程(上) 1.线程
  • 进程和线程是什么?

  • 什么是并行和并发?

    并发:同一时段,多个任务在单个cpu上交替执行

    并行:同一时段,多个任务在多个cpu上同时执行

1.1 创建线程的三种方式
@Slf4j(topic = "c")
public class Test1 {
    // thread方式运行线程
    public static void main(String[] args) {
        Thread t1 = new Thread(()->{
            log.debug("t1 start...");
        },"t1");
        t1.start();
        log.debug("main线程执行");
    }
}

@Slf4j(topic = "c")
public class Test2 {
    public static void main(String[] args) {
        // Runable执行线程
        Runnable r1 = () -> {
            log.debug("runable start...");
        };
        Thread t1 =  new Thread(r1);
        t1.setName("runable");
        t1.start();
    }
}

@Slf4j(topic = "c")
public class Test3 {
    public static void main(String[] args) throws Exception{
        // callable
        FutureTask task = new FutureTask(new Callable() {
            @Override
            public Integer call() throws Exception {
                log.debug("callable start..");
                Thread.sleep(2000);
                return 100;
            }
        });
        Thread t1 = new Thread(task);
        t1.setName("callable");
        t1.start();
        Object result = task.get();
        log.debug("{}",result);
    }
}

3种创建区别?
thread单继承有局限性 Runnable和callble多实现 callble有返回值有异常

1.2 线程的运行原理

  • 任务调度器会将cpu的时间片交给某个类,然后虚拟机会将类的方法全放在方法区中,调用第一个方法时,会在线程栈中分配线程栈帧 ,程序计数器是线程私有的,它会记录当前改执行哪行代码
1.2 什么是线程上下文切换?

cpu因为某些原因不再执行当前的线程,转而执行另外一个线程的代码

  • 线程cpu时间片用完
  • 垃圾回收
  • 有优先级更高的线程需要执行
  • 线程调用了sleep,yield,wait,join,lock等方法

当线程上下文切换时,线程的状态会由操作系统保存,并恢复另外一个线程的状态,java中对应的概念就是程序计数器,他会记住下一条指令的地址,是线程私有的。

  • 线程状态包括继承计数器,虚拟机栈中每个栈帧的信息,如局部变量、操作数栈、返回地址。
1.3 线程常见方法

1.getState 获取线程状态

run 与 start 有什么区别?

可以发现 调用t1.run 其实还是main线程执行的,而start则会新起一个线程执行。

2.sleep休眠、interrupt打断休眠的线程
  • sleep会使线程进入阻塞状态
@Slf4j(topic = "c.test5")
public class Test5 {
    public static void main(String[] args) {
        Thread t1 = new Thread(() ->
        {
            try {
                log.debug("t1 sleep ...");
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        // interrupt 打断正在休眠的线程并抛出异常
        try {
            t1.start();
            log.debug("main interrupt");
            TimeUnit.SECONDS.sleep(1);
            t1.interrupt();
            log.debug("t1 status:{}", t1.getState());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
3.yield、setPriority
  • 将线程的某一段时间的cpu执行权让出去,让线程进行可运行状态,与sleep不同的是 任务调度器还是会分时间片给yield线程

  • Priority设置线程优先级

4.join
  • 等待线程结束 join(long time)最多等待多少s
@Slf4j(topic = "c.test7")
public class Test7 {
    private static Integer sum = 0;

    public static void main(String[] args) throws InterruptedException {

        Thread t1 = new Thread(() -> {
            try {
                Thread.sleep(1000);
                log.debug("t1 start ....");
                sum = 10;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        t1.start();
        log.debug("main start...");
        // 如果不等待t1线程执行完 那么sum值会一直为0
        t1.join();
        System.out.println(sum);

    }
}
5.interrupt
@Slf4j(topic = "c.test8")
public class Test8 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            log.debug("t1 start ,...");
            while (true) {
                // 获取当前线程打断状态
                boolean interrupted = Thread.currentThread().isInterrupted();
                if (interrupted) {
                    break;
                }
            }
        }, "t1");
        t1.start();
        Thread.sleep(1000);
        // 如果当前线程执行了wait join sleep方法 并不会把打断状态设置为ture 而是会抛出异常
        t1.interrupt();
        log.debug("main .....");
    }
}
1.4 二阶段终止模式
  • 二阶段终止模式是指线程1如何优雅的停止线程2

  • 需求如图

@Slf4j(topic = "c.test9")
public class Test9 {
    public static void main(String[] args) throws InterruptedException {
        TwoPhaseCommit twoPhaseCommit = new TwoPhaseCommit();
        twoPhaseCommit.start();
        Thread.sleep(6500);
        twoPhaseCommit.stop();
    }
}

// 两阶段终止线程
@Slf4j(topic = "c.twoPhaseCommit")
class TwoPhaseCommit {
    private Thread monit;

    // 开始线程方法
    public void start() {
        monit = new Thread(() -> {
            while (true) {
                boolean interrupted = monit.isInterrupted();
                if (interrupted) {
                    log.debug("料理后事");
                    break;
                }
                try {
                    Thread.sleep(2000);
                    log.debug("收集日志....");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    // 当线程调用sleep时 不会将打断标记设置为ture
                    monit.interrupt();
                }
            }
        }, "t1");
        monit.start();
    }

    // 停止线程
    public void stop() {
        monit.interrupt();
    }
}
  • park 打断当前线程不会清空打断状态

    @Slf4j(topic = "c.test10")
    public class Test10 {
        public static void main(String[] args) throws InterruptedException {
            test3();
        }
    
        private static void test3() throws InterruptedException {
            Thread t1 = new Thread(() -> {
                log.debug("park run");
                LockSupport.park();
                log.debug("unpark...");
                // interrupted作用:先将打断标记状态ture返回 再清除打断标记
                log.debug("线程打断标志:{}", Thread.interrupted());
                LockSupport.park();
                log.debug("unpark...");
            }, "t1");
            t1.start();
    
            sleep(1);
            t1.interrupt();
        }
    }
    
  • 守护线程

    只有其它非守护线程结束了,即使守护线程有没有执行完 也会强行停止

    @Slf4j(topic = "c.test11")
    public class Test11 {
        public static void main(String[] args) throws InterruptedException {
            Thread t1 = new Thread(() -> {
                while (true) {
                    if (Thread.currentThread().isInterrupted()) {
                        break;
                    }
                }
                log.debug("结束");
            }, "t1");
            t1.setDaemon(true);
            t1.start();
            Thread.sleep(1000);
            log.debug("主线程结束。。。。");
        }
    }
    
1.5 线程状态
  • cpu层面 5种

  • java层面的6种

2.并发之共享模型
  • 问题:两个线程对初始值为0对静态变量一个做自增 一个做自减 分别循环5000次 结果会为0吗?

​ 不会,因为再将源码编译成字节码后,count++或者count–其实是有4步操作,如果这几条指令按顺序执行肯定没问题,但多线程由于分时系统,会产生指令的交错。

  • 解决:线程1执行时上锁,线程2进入阻塞状态,线程1没有释放锁 线程2不可能执行。
@Slf4j(topic = "c.test1")
public class Test1 {
    static int sum = 0;

    static Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            for (int i = 1; i <= 5000; i++) {
                synchronized (lock) {
                    sum++;
                }
            }
        }, "t1");

        Thread t2 = new Thread(() -> {
            for (int i = 1; i <= 5000; i++) {
                synchronized (lock) {
                    sum--;
                }
            }
        }, "t2");

        t2.start();
        t1.start();
        t2.join();
        t1.join();
        System.out.println(sum);
    }
}
2.1 变量共享是否会产生安全问题
  • 多个线程共享同一变量会导致线程安全 不共享则不会 (后面会引入原子类解决这一问题)

  • 经典案例 卖票

    @Slf4j(topic = "c.test3")
    public class Test3 {
        public static void main(String[] args) throws InterruptedException {
            TickWindows tickWindows = new TickWindows(1000);
    
            List amountList = new Vector<>();
            List threadList = new ArrayList<>();
            // 开启线程
            for (int i = 0; i < 2000; i++) {
                Thread thread = new Thread(() -> {
                    int amount = tickWindows.sell(random(5));
                    amountList.add(amount);
                });
                threadList.add(thread);
                thread.start();
            }
    
            // 等待2000个线程结束
            for (Thread thread1 : threadList) {
                thread1.join();
            }
    
            log.debug("余票:{}" +tickWindows.getCount());
            log.debug("剩下:{}张票" + amountList.stream().mapToInt(a-> a).sum());
        }
    
        public static int random(int amount) {
            Random random = new Random();
            return random.nextInt(amount) + 1;
        }
    }
    
    class TickWindows {
        private int count;
    
        public TickWindows(int count) {
            this.count = count;
        }
    
        // 获取剩余票
        public int getCount() {
            return count;
        }
    
        // 售票
        public synchronized int sell(int amount) {
            if (this.count >= amount) {
                this.count -= amount;
                return amount;
            } else {
                return 0;
            }
        }
    }
    
2.2线程八锁

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N1mQOEaZ-1653143116023)(https://gitee.com/liubozzz/image/raw/20220520liubo/img/202205211944375.png)]

2.3 Monitor概念

java对象在内存中是由两部分组成 第一部分是对象头,另一部门是对象中的成员变量

Java对象头

  • klass word :它是一个指针 指向了对象存储的class 通过class能够知道它是什么类型的

    mark word 详细信息

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8CMyKMea-1653143116025)(https://gitee.com/liubozzz/image/raw/20220520liubo/img/202205202011985.png)]

Monitor(锁)

  • 当给对象加上synchronized时,会给这个对象关联一个monitor,通过指针指向monitor地址,并设置当前线程为owner当其他线程来获取当前对象的锁时,会被加入到entrylist中等待。
2.4 synchoronized原理 1.轻量级锁
  • 轻量级锁:如果一个对象有多个线程访问,并且多线程访问的时间是错开的,那么可以使用轻量级锁来优化

2.膨胀锁

3.自旋优化
  • 重量级锁竞争的时候,可以使用自旋来进行优化,如果当前线程自旋成功(其他持锁线程释放锁),这时线程避免了阻塞

4.偏向锁

轻量级锁在没有竞争时(自已线程)仍需要执行cas操作(耗性能)

在java6中引入偏向锁,只会在第一次去通过cas将线程id设置到markword头中,之后发现线程id是自已表示没有竞争,不会去执行cas操作,以后只要不发生竞争,这个对象就归当前线程所有

  • 偏向状态

  • 撤销

​ 当调用wait/notify 也会出现撤销

  • 批量重偏向

  • 批量撤销

2.5 wait/notify

  • api介绍

@Slf4j(topic = "c.wait")
public class WaitNotify {

    final static Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            log.debug("t1 执行");
            synchronized (lock) {
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.debug("t1 执行任务");
        }, "t1").start();

        new Thread(() -> {
            log.debug("t2 执行");
            synchronized (lock) {
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.debug("t2 执行任务");
        }, "t2").start();

        Thread.sleep(2000);
        log.debug("main 唤醒线程");
        synchronized (lock) {
//            lock.notify();  // 随机唤醒某个线程
            lock.notifyAll(); // 唤醒所有在waitset中的线程
        }
    }
}
  • wait和sleep的区别?
    1. sleep是thread的静态方法,wait是object的方法
    2. wait需要配合synchoronized使用,而sleep不需要
    3. wait会释放锁进入waitset等待,而sleep不会释放锁
2.6 模式 1.同步模式之保护性暂停

  • 具体实现
// 下载百度首页数据
public class Download {
    public static List download() throws IOException {
        HttpURLConnection url = (HttpURLConnection) new URL("https://www.baidu.com/").openConnection();
        List list = new ArrayList<>();

        try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.getInputStream(), StandardCharsets.UTF_8))) {
            String line;
            while ((line = reader.readLine()) != null) {
                list.add(line);
            }
        }
        return list;
    }
}
@Slf4j(topic = "c.test4")
public class Test4 {
    public static void main(String[] args) {
        GuardedObject object = new GuardedObject();
        new Thread(() -> {
            log.debug("等待结果");
            List o = (List) object.get(2000);
            log.debug("结果是:{}", o);
        }, "t1").start();

        new Thread(() -> {
            log.debug("执行下载");
            Sleeper.sleep(1);
            object.complete(null);
        }, "t2").start();
    }
}

// 增强超时效果
class GuardedObject {
    private Object response;

    public Object get(int timeout) {
        synchronized (this) {
            // 开始时间
            long startTime = System.currentTimeMillis();  // 09:00:00
            // 经历时间
            long passedTime = 0;
            while (response == null) {
                long waitTime = timeout - passedTime;  // 可能存在虚假唤醒
                // 经历时间 大于等待时间 退出循环
                if (waitTime <= 0) {
                    break;
                }
                try {
                    this.wait(waitTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                passedTime = System.currentTimeMillis() - startTime;  // 02
            }
            return response;
        }
    }

    public void complete(Object response) {
        synchronized (this) {
            // 给成员变量赋值
            this.response = response;
            // 唤醒wait线程
            this.notify();
        }
    }
}
  • join源码

    底层其实就是使用了wait的保护性暂停模式

  • 扩展保护性暂停-解偶等待和生产

    @Slf4j(topic = "c.test5")
    public class Test5 {
        public static void main(String[] args) {
            for (int i = 0; i < 3; i++) {
                new People().start();
            }
            Sleeper.sleep(1);
            for (Integer id : MailBox.getIdList()) {
                new Postman(id, "内容" + id).start();
            }
        }
    }
    
    @Slf4j(topic = "c.People")
    class People extends Thread {
        @Override
        public void run() {
            GuardedObjects obj = MailBox.createObject();
            log.debug("开始收信{}", obj.getId());
            Object mail = obj.get(5000);
            log.debug("收到信id:{},内容:{}", obj.getId(), mail);
        }
    }
    
    @Slf4j(topic = "c.Postman")
    class Postman extends Thread {
        private int mailId;
        private String mail;
    
        public Postman(int mailId, String mail) {
            this.mailId = mailId;
            this.mail = mail;
        }
    
        @Override
        public void run() {
            GuardedObjects guardedObjects = MailBox.getGuardedObjects(mailId);
            log.debug("开始送信id:{},内容:{}", mailId, mail);
            guardedObjects.complete(mail);
        }
    }
    
    
    class MailBox {
        private static Map map = new Hashtable<>();
    
        private static int id = 1;
    
        // 产生唯一id
        private static synchronized int generateId() {
            id++;
            return id;
        }
    
        // 创建GuardedObjects对象
        public static GuardedObjects createObject() {
            GuardedObjects obj = new GuardedObjects(generateId());
            map.put(obj.getId(), obj);
            return obj;
        }
    
        // 获取到所有到id
        public static Set getIdList() {
            return map.keySet();
        }
    
        // 通过id获取到GuardedObjects对象
        public static GuardedObjects getGuardedObjects(int id) {
            return map.remove(id);
        }
    }
    
    
    class GuardedObjects {
    
        private int id;
    
        public void setId(int id) {
            this.id = id;
        }
    
        public int getId() {
            return id;
        }
    
        public GuardedObjects(int id) {
            this.id = id;
        }
    
    
        private Object response;
    
        public Object get(int timeout) {
            synchronized (this) {
                // 开始时间
                long startTime = System.currentTimeMillis();  // 09:00:00
                // 经历时间
                long passedTime = 0;
                while (response == null) {
                    long waitTime = timeout - passedTime;  // 可能存在虚假唤醒
                    // 经历时间 大于等待时间 退出循环
                    if (waitTime <= 0) {
                        break;
                    }
                    try {
                        this.wait(waitTime);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    passedTime = System.currentTimeMillis() - startTime;  // 02
                }
                return response;
            }
        }
    
        public void complete(Object response) {
            synchronized (this) {
                // 给成员变量赋值
                this.response = response;
                // 唤醒wait线程
                this.notifyAll();
            }
        }
    }
    
    
4.异步模式之生产者/消费者

  • java实现

    @Slf4j(topic = "c.test6")
    public class Test6 {
        public static void main(String[] args) {
            MessageQueue queue = new MessageQueue(2);
            for (int i = 0; i < 3; i++) {
                // 3个生产者
                int id = i;
                new Thread(() -> {
                    queue.put(new Message(id, "值" + id));
                }, "生产者").start();
            }
    
            new Thread(() -> {
               while (true){
                   Sleeper.sleep(1);
                   Message task = queue.task();
               }
            }, "消费者").start();
        }
    }
    
    @Slf4j(topic = "c.message")
    class MessageQueue {
        private LinkedList list = new LinkedList<>();
        private int capacity;
    
        public MessageQueue(int capacity) {
            this.capacity = capacity;
        }
    
        // 消费消息
        public Message task() {
            synchronized (list) {
                // 如果集合为空进入等待 如果不为空 返回集合中第一个
                while (list.isEmpty()) {
                    try {
                        log.debug("队列为空,消费者线程等待");
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                Message message = list.removeFirst();
                log.debug("消费者消费消息id:{},value:{}", message.getId(), message.getValue());
                list.notifyAll();
                return message;
            }
        }
    
        // 存入消息
        public void put(Message message) {
            synchronized (list) {
                while (list.size() == capacity) {
                    try {
                        log.debug("队列已达到最大容量,生产者线程等待");
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                list.addLast(message);
                log.debug("生产者生产消息");
                list.notifyAll();
            }
        }
    }
    
    
    final class Message {
        private int id;
        private Object value;
    
        public Message(int id, Object value) {
            this.id = id;
            this.value = value;
        }
    
        public int getId() {
            return id;
        }
    
        public Object getValue() {
            return value;
        }
    
        @Override
        public String toString() {
            return "Message{" +
                    "id=" + id +
                    ", value=" + value +
                    '}';
        }
    }
    

2.7 park/unpark

  • 代码实现

    @Slf4j(topic = "c.test7")
    public class Test7 {
        public static void main(String[] args) {
            Thread t1 = new Thread(() -> {
                log.debug("t1 start...");
                Sleeper.sleep(2);
                log.debug("park...");
                LockSupport.park();
                log.debug("recover...");
            }, "t1");
    
            t1.start();
    
            Sleeper.sleep(3);
            log.debug("main ...");
            LockSupport.unpark(t1);
        }
    }
    
  • debug发现 使用park后 t1线程的状态是wait

  • park和object中的wait/notify/notifyall 有什么区别?

    wait/notify和notifyall需要object monitor一起使用,而park不需要

    park可以指定线程进行阻塞或唤醒,notify是随机或者全部

    park可以先unpark 而wait不能先notify

  • park/unpark原理

  • 调用park

  • 调用unpark

  • 先调用unpark 先调用park的情况

2.8重新理解线程状态

  • 代码实现

    @Slf4j(topic = "c.test8")
    public class Test8 {
        public static void main(String[] args) {
            final Object obj = new Object();
            new Thread(() -> {
                synchronized (obj) {
                    log.debug("t1....");
                    try {
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug("start....");
            }, "t1").start();
    
            new Thread(() -> {
                synchronized (obj) {
                    log.debug("t1....");
                    try {
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug("start....");
            }, "t2").start();
            synchronized (obj){
            Sleeper.sleep(1);
            log.debug("main start...");
            obj.notifyAll();
           }
        }
    }
    

  • debug可以发现。当t1,t2线程调用wait方法时 会使线程进入waitting状态,

  • 当主线程调用notifyall方法时 t1,t2线程进入blocked状态

  • 当主线程代码全部执行完后 在waitset中的线程会去竞争锁

2.9 多把锁

@Slf4j(topic = "c.test9")
public class Test9 {
    public static void main(String[] args) {
        Room room = new Room();
        new Thread(() -> {
            room.sleep();
        }, "t1").start();

        new Thread(() -> {
            room.study();
        }, "t2").start();
    }
}

@Slf4j(topic = "c.room")
class Room {
    private static final Object sleep = new Object();
    private static final Object study = new Object();

    public void sleep() {
        synchronized (sleep) {
            log.debug("sleep....");
            Sleeper.sleep(1);
        }
    }

    public void study() {
        synchronized (study) {
            log.debug("study....");
            Sleeper.sleep(1);
        }
    }
}
  • 细粒度锁

    并发度高,但同一个线程同时获取多把锁可能会产生死锁问题

3.0 活跃度 1.死锁
@Slf4j(topic = "c.test10")
public class Test10 {

    public static void main(String[] args) {
        test1();
    }


    private static void test1() {
        Object A = new Object();
        Object B = new Object();

        Thread t1 = new Thread(() -> {
            synchronized (A) {
                log.debug("t1 线程获取到A锁");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (B) {
                    log.debug("t1线程尝试获取B锁");
                    log.debug("t1获取到b锁 进行其他操作");
                }
            }
        }, "t1");

        Thread t2 = new Thread(() -> {
            synchronized (B) {
                log.debug("t2线程获取到B锁");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (A) {
                    log.debug("t2线程尝试获取A锁");
                    log.debug("t2获取到a锁 进行其他操作");
                }
            }
        }, "t2");

        t1.start();
        t2.start();
    }
}

  • t2获取到b锁后尝试去获取a锁 但a1锁已经被t1获取,t1想去获取b锁,但b锁一起被t2获取 导致死锁
  • 使用jconsole命令可以查看死锁情况

2.活锁
  • 活锁出现在两个线程互相改变对方的结束条件,最后谁也无法结束

    @Slf4j(topic = "c.test11")
    public class Test11 {
        static volatile int count = 10;
    
        public static void main(String[] args) {
            new Thread(() -> {
                // 期望count为0时退出循环
                while (count > 0) {
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count--;
                    log.debug("count:{}", count);
                }
            }, "t1").start();
    
            new Thread(() -> {
                // 期望count超过20时退出循环
                while (count < 20) {
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count++;
                    log.debug("count:{}", count);
                }
            }, "t2").start();
        }
    }
    

  • 解决办法:增加某个线程的睡眠时间 让线程交错执行

3.饥饿

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MfmwrRav-1653143116033)(images/image-20220521153611295.png)]

3.1 ReentrantLock
  • 相对于synchoronized它具备如下特点

    • 可中断

    • 可设置超时时间

    • 可设置为公平锁

    • 支持多个变量 对waitSet更加细粒度多划分

1.可重入
  • 可重入是指同一个线程如果首次获取到了这把锁,那么它就是这把锁的拥有者,它有权利再次获取到这把锁
@Slf4j(topic = "c.test12")
public class Test12 {
    private static ReentrantLock lock = new ReentrantLock();

    // 实现可重入
    public static void main(String[] args) {
        try {
            lock.lock();
            log.debug("实现可重入");
            method1();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public static void method1() {
        try {
            lock.lock();
            log.debug("m1 重入");
            method2();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public static void method2() {
        try {
            lock.lock();
            log.debug("m2 重入");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
2.可打断
@Slf4j(topic = "c.test13")
public class Test13 {
    private static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            log.debug("尝试获取锁");
            try {
                // 如果没有竞争,可以获取到lock锁
                // 如果有竞争就会进入阻塞队列,但是其他线程可以打断
                lock.lockInterruptibly();
            } catch (InterruptedException e) {
                e.printStackTrace();
                log.debug("没有获取到锁,返回");
                return;
            }
            try {
                log.debug("获取到锁");
            } finally {
                lock.unlock();
            }
        }, "t1");

        // 主线程获取锁
        lock.lock();
        t1.start();
        // 打断t1线程获取锁
        Sleeper.sleep(1);
        t1.interrupt();
    }
}
3.锁超时
@Slf4j(topic = "c.test14")
public class Test14 {
    private static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            try {
                log.debug("尝试获取锁");
                if (!lock.tryLock(2, TimeUnit.SECONDS)) {
                    log.debug("获取锁失败");
                    return;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
                log.debug("获取锁失败");
                return;
            }
            try {
                log.debug("获取到锁");
            } finally {
                lock.unlock();
            }
        }, "t1");

        log.debug("获取到锁");
        lock.lock();
        t1.start();
        Sleeper.sleep(1);
        log.debug("释放锁");
        lock.unlock();
    }
}
4.哲学家就餐问题 使用renntrantlock解决
@Slf4j(topic = "c.test15")
public class Test15 {
    public static void main(String[] args) {
        Chopstick c1 = new Chopstick("1");
        Chopstick c2 = new Chopstick("2");
        Chopstick c3 = new Chopstick("3");
        Chopstick c4 = new Chopstick("4");
        Chopstick c5 = new Chopstick("5");

        new Philosopher("t1",c1,c2).start();
        new Philosopher("t2",c2,c3).start();
        new Philosopher("t3",c3,c4).start();
        new Philosopher("t4",c4,c5).start();
        new Philosopher("t5",c5,c1).start();
    }
}

@Slf4j(topic = "c.Philosopher")
class Philosopher extends Thread {
    Chopstick left;
    Chopstick right;

    public Philosopher(String name, Chopstick left, Chopstick right) {
        super(name);
        this.left = left;
        this.right = right;
    }

    @Override
    public void run() {
        while (true) {
            // 尝试获取左手筷子
            if (left.tryLock()) {
                try {
                    // 尝试获取右手筷子
                    if (right.tryLock()) {
                        try {
                            eat();
                        } finally {
                            // 获取失败 释放
                            right.unlock();
                        }
                    }
                } finally {
                    // 获取失败
                    left.unlock();
                }
            }
        }
    }


    private void eat() {
        log.debug("eat....");
        Sleeper.sleep(1);
    }
}


class Chopstick extends ReentrantLock {
    private String name;

    public Chopstick(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "筷子{" +
                "name='" + name + ''' +
                '}';
    }
}
5.公平锁

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KxZZh6ul-1653143116033)(https://gitee.com/liubozzz/image/raw/20220520liubo/img/202205211931628.png)]

6.条件变量

7.同步模式之顺序控制
  • 要求t1/t2线程 先打印t2线程

    或者使用wait/notify

    @Slf4j(topic = "c.test16")
    public class Test16 {
        public static void main(String[] args) {
            Thread t1 = new Thread(() -> {
                LockSupport.park();
                log.debug("t1");
            }, "T1");
    
    
            new Thread(() -> {
                log.debug("t2");
                LockSupport.unpark(t1);
            }, "T2").start();
            t1.start();
        }
    }
    
  • 交替输出 要求线程1打印a 线程2打印b 线程3打印c 循环5次

    • 使用wait和notify解决

    • 使用await和single方法解决

      @Slf4j(topic = "c.test17")
      public class Test17 {
          public static void main(String[] args) {
              AwaitSignal lock = new AwaitSignal(5);
              Condition condition1 = lock.newCondition();
              Condition condition2 = lock.newCondition();
              Condition condition3 = lock.newCondition();
      
              new Thread(() -> {
                  lock.println("a", condition1, condition2);
              }, "t1").start();
      
              new Thread(() -> {
                  lock.println("b", condition2, condition3);
              }, "t2").start();
      
              new Thread(() -> {
                  lock.println("c", condition3, condition1);
              }, "t3").start();
      
              Sleeper.sleep(1);
              try {
                  lock.lock();
                  condition1.signal();
              } finally {
                  lock.unlock();
              }
      
          }
      }
      
      class AwaitSignal extends ReentrantLock {
          public void println(String str, Condition current, Condition next) {
              for (int i = 0; i < loopNumber; i++) {
                  try {
                      lock();
                      current.await();
                      System.out.print(str);
                      // 唤醒下一个线程
                      next.signal();
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  } finally {
                      unlock();
                  }
              }
          }
      
          private int loopNumber;
      
          public AwaitSignal(int loopNumber) {
              this.loopNumber = loopNumber;
          }
      }
      
      
    • 使用park/unpark解决

      @Slf4j(topic = "c.test18")
      public class Test18 {
          static Thread t1;
          static Thread t2;
          static Thread t3;
      
          public static void main(String[] args) {
              ParkUnPark park = new ParkUnPark(5);
              t1 = new Thread(() -> {
                  park.println("a", t2);
              }, "t1");
      
              t2 = new Thread(() -> {
                  park.println("b", t3);
              }, "t2");
      
              t3 = new Thread(() -> {
                  park.println("c", t1);
              }, "t3");
      
              t1.start();
              t2.start();
              t3.start();
              Sleeper.sleep(1);
              LockSupport.unpark(t1);
          }
      }
      
      class ParkUnPark extends ReentrantLock {
          public void println(String str, Thread thread) {
              for (int i = 0; i < loopNumber; i++) {
                  LockSupport.park();
                  System.out.print(str);
                  LockSupport.unpark(thread);
              }
          }
      
          private int loopNumber;
      
          public ParkUnPark(int loopNumber) {
              this.loopNumber = loopNumber;
          }
      }
      
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/921527.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号