JAVA基础多线程核心API与Lock锁使用

join方法

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

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

创建Thread01类,包名:com.wdp.thread02.test

package com.wdp.thread02.test;

public class Thread01 {
    public static void main(String[] args) {
        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();


    }
}

运行结果:
在这里插入图片描述
并不是从上往下的顺序,而是cpu调用那个线程,那个线程就先执行,没有顺序可言,使用join就能够让他们一次执行

创建Thread02类,包名:com.wdp.thread02.test

package com.wdp.thread02.test;

public class Thread02 {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ",线程执行");
        }, "t1");

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

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

        t1.start();
        t2.start();
        t3.start();

    }
}

运行结果:
在这里插入图片描述
这次就是依次执行的

为什么使用join方法就可以依次执行,下面我们来看看join底层设计原理

join方法底层的设计原理
join(0)默认就是无限制等待

 public final void join() throws InterruptedException {
        join(0);
    }

再点join可以看见源码如下,join的底层就是synchronized和wait()方法封装的,synchronized加载实例方法是this锁
在这里插入图片描述
现在我们再来看下图这段代码的含义,当t2线程调用了t1.join,底层就是调用了同步锁synchronized,在锁里面还调用了wait方法,wait方法会主动释放this锁,线程变为阻塞等待状态,t2就无法下往下执行,只有当t1线程执行完毕以后,再来唤醒t2线程,t1线程执行完毕是怎么唤醒t2的,这里源码没有,唤醒的代码在jvm Hotspot源码中,当jvm在关闭线程之前会检测阻塞在t1线程对象上的线程,然后执行notfyAll(),这样t2就被唤醒了。

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

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

多线程七种执行的状态

初始化状态(也叫新建状态)
就绪状态
运行状态
死亡状态
阻塞状态
超时状态
等待状态

初始化状态:new一个线程的时候就是创建一个线程,就是初始化(新建)状态
就绪状态:当调用start方法的时候,就会进入就绪状态
运行状态:当被cpu调度执行该线程的时候,也就是进入线程的run方法,就是运行状态
死亡状态:线程运行完以后,就会变成死亡状态
阻塞状态:比如同步锁,没有抢到锁就会进入阻塞状态,然后等待拿到锁的线程执行完毕才会重新被唤醒进入就绪状态,等待cpu调用。
超时等待:线程调用了wait() join() slepp()方法的时候,并且设置了时间,这就叫超时状态,时间一到就会重新进入就绪状态,等待cpu调用
等待状态:比如线程调用了wait()方法,没有设置超时时间,线程就会一直等待。

守护线程与用户线程

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

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

创建Thread03类,包名:com.wdp.thread02.test

package com.wdp.thread02.test;

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

        thread.setDaemon(false);
        thread.start();
        System.out.println("我是主线程,代码执行结束");
    }
}

多线程yield

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

创建Thread04类,包名:com.wdp.thread02.test

package com.wdp.thread02.test;

public class Thread04 extends Thread {
    public Thread04(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 Thread04("wdp01").start();
        new Thread04("wdp02").start();
    }
}

多线程优先级

1.在java语言中,每个线程都有一个优先级,当线程调控器有机会选择新的线程时,线程的优先级越高越有可能先被选择执行,线程的优先级可以设置1-10,数字越大代表优先级越高
注意:Oracle为Linux提供的java虚拟机中,线程的优先级将被忽略,即所有线程具有相同的优先级。
所以,不要过度依赖优先级。
2.线程的优先级用数字来表示,默认范围是1到10,即Thread.MIN_PRIORITY到Thread.MAX_PRIORTY.一个线程的默认优先级是5,即Thread.NORM_PRIORTY
3.如果cpu非常繁忙时,优先级越高的线程获得更多的时间片,但是cpu空闲时,设置优先级几乎没有任何作用。
创建Thread05类,包名:com.wdp.thread02.test

package com.wdp.thread02.test;

public class Thread05 {
    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();
    }
}

sleep防止CPU占用100%

sleep(long millis) 线程睡眠 millis 毫秒
sleep(long millis, int nanos) 线程睡眠 millis 毫秒 + nanos 纳秒
比方一个线程一直调用一个死循环,cpu占内存就会非常高,使用了sleep方法后线程会有休眠时间,而不是一直执行
创建SleepThread类,包名:com.wdp.thread02.test

package com.wdp.thread02.test;

public class SleepThread {
    public static void main(String[] args) {
        new Thread(() -> {
            while (true) {
                try {
                    System.out.println("开始执行了");

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

死循环没有加上sleep方法,看cpu的内存占比达到67.9%

在这里插入图片描述
SleepThread类里加入sleep方法

package com.wdp.thread02.test;

public class SleepThread {
    public static void main(String[] args) {
        new Thread(() -> {
            while (true) {
                try {
                    System.out.println("开始执行了");
                    Thread.sleep(30);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

再看cpu只有0.4%
在这里插入图片描述

如何安全的停止一个线程

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

Interrupt(线程中止)
Interrupt 打断正在运行或者正在阻塞的线程
如果目标线程调用了wait()、wait(long)、wait(long, int)、join()、join(long, int)、sleep(long, int)这些方法,线程就是等待阻塞的状态,现在中止线程也会成功,但是会抛异常报错。

创建Thread06类,包名:com.wdp.thread02.test

package com.wdp.thread02.test;

public class Thread06 {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t1");
        t1.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t1.interrupt();
        System.out.println("是否等待");
        System.out.println("获取打断标记:" +t1.isInterrupted());
    }
}

运行结果:
在这里插入图片描述
这种方法不推荐使用,代码都没执行完成,强制把线程运行状态变为了死亡状态

interrupt()方法是中断线程的方法,调用这个方法,底层会有一个boolean变量isInterrupted,等于true就可以中断了,就可以自己代码进行逻辑判断了,而不是强制停止了

创建Thread07类,包名:com.wdp.thread02.test

package com.wdp.thread02.test;

public class Thread07 extends Thread {
    @Override
    public void run() {
        while (true){
            if(this.isInterrupted()){
                System.out.println("中断成功");
                break;
            }
        }
    }

    public static void main(String[] args) {
        Thread07 thread07 = new Thread07();
        thread07.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread07.interrupt();
    }
}

运行结果:
在这里插入图片描述正确的线程中止-标志位
在上方代码逻辑中,增加一个判断,用来控制线程执行的中止。

创建Thread08,包名:com.wdp.thread02.test

package com.wdp.thread02.test;

import java.util.concurrent.ConcurrentHashMap;

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

    @Override
    public void run() {
        while (isFlag){  //true就一直循环

        }
    }

    public static void main(String[] args) {
        Thread08 thread08 = new Thread08();
        thread08.start();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("中断线程");
        //设置为false就终止死循环
        thread08.isFlag = false;
    }
}

运行结果:正常终止,没有报错
在这里插入图片描述

Lock锁的基本使用

在jdk1.5后新增的ReentrantLock类同样可达到此效果,且在使用上比synchronized更加灵活
相关API:
使用ReentrantLock实现同步
lock()方法:上锁
unlock()方法:释放锁
使用Condition实现等待/通知 类似于 wait()和notify()及notifyAll()
Lock锁底层基于AQS实现,需要自己封装实现自旋锁。

Synchronized —属于JDK 关键字 底层属于 C++虚拟机底层实现
Lock锁底层基于AQS实现-- 变为重量级
Synchronized 底层原理—锁的升级过程
Lock 过程中 注意 获取锁 释放锁

ReentrantLock用法

创建Thread09,包名:com.wdp.thread02.test

package com.wdp.thread02.test;

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

public class Thread09 implements Runnable {
    private int count = 100;
    private Lock lock = new ReentrantLock();
    public static void main(String[] args) {
        Thread09 thread09 = new Thread09();
        Thread t1 = new Thread(thread09);
        Thread t2 = new Thread(thread09);
        t1.start();
        t2.start();
    }

    @Override
    public void run() {
        while (true){
            try {
                Thread.sleep(30);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                lock.lock();
                if (count > 1) {
                    count--;
                    System.out.println(Thread.currentThread().getName() + "," + count);

                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                lock.unlock();
            }

        }
    }
}

Condition用法
创建Thread10,包名:com.wdp.thread02.test

package com.wdp.thread02.test;

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

public class Thread10 {

    private ReentrantLock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    public static void main(String[] args) {
        Thread10 thread10 = new Thread10();
        thread10.print();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread10.signal();
    }

    private void signal() {
        try {
           //加锁
            lock.lock();
            //唤醒线程  相当于notify()
            condition.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    private void print() {
        new Thread(() -> {
            try {
                //加锁
                lock.lock();
                System.out.println(Thread.currentThread().getName() + ",1");
                //相当于wait()方法,释放锁并且让当前线程阻塞等待其他线程唤醒
                condition.await();
                System.out.println(Thread.currentThread().getName() + ",2");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                //解锁
                lock.unlock();
            }
        }).start();
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值