java_线程优先级、Synchronized和Reentrantlock区别、sleep和wait区别、死锁

一、匿名类部类与线程优先级

1.1 匿名内部类

        没有名称的多线程实现类,并且写入一个类的内部或者方法的内部,叫匿名内部类。

package com.aaa.day02_mutilThread.demo1;

import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

/**
 * @author :caicai
 * @date :Created in 2022/7/19 14:02
 * @description:匿名内部类
 * @description:没有名称的多线程实现类,并且写入一个类的内部或者方法的内部,叫匿名内部类。
 * @modified By:
 * @version:
 */
public class AnonymousInternalClass {
    public static void main(String[] args) {
        // 继承Thread内部类写法
        new Thread(){
            @Override
            public void run() {
                for (int i = 1; i < 11; i++) {
                    // 获取当前线程
                    Thread thread = Thread.currentThread();
                    // 获取线程名称
                    System.out.println("线程"+thread.getName()+"打印了"+i);
                }
            }
        }.start();

        // 实现Runnable内部类的写法
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 11; i < 21; i++) {
                    Thread thread = Thread.currentThread();
                    System.out.println("线程"+thread.getName()+"打印了"+i);
                }
            }
        }).start();

        // 实现Callable内部类的写法
        new Thread(new FutureTask<String>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                for (int i = 21; i < 31; i++) {
                    Thread thread = Thread.currentThread();
                    System.out.println("线程"+thread.getName()+"打印了"+i);
                }
                String randomStr = UUID.randomUUID().toString();
                return randomStr;
            }
        })).start();

    }
}

        结果显示:

1.2 线程优先级

        setPriority 设置线程A的优先级 优先级一共有10个级别,分别数字1到10  不给线程指定优先级时,默认优先级为5 优先级数字越大,先执行的概率越大

package com.aaa.day02_mutilThread.demo1;

import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

/**
 * @author :caicai
 * @date :Created in 2022/7/19 14:02
 * @description:线程优先级
 * @description:setPriority 设置线程A的优先级 优先级一共有10个级别,分别数字1到10 
 *                           不给线程指定优先级时,默认优先级为5 优先级数字越大,先执行的概率越大
 */
public class ThreadPriorityDemo {
    public static void main(String[] args) {
        // 定义线程A对象
        Thread threadA = new Thread(){
            @Override
            public void run() {
                System.out.println("线程"+Thread.currentThread().getName()+"打印了AAA");
            }
        };

        // 定义线程B对象
        Thread threadB = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程"+Thread.currentThread().getName()+"打印了BBB");
            }
        });

        // 实现Callable内部类的写法
        Thread threadC = new Thread(new FutureTask<String>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                System.out.println("线程"+Thread.currentThread().getName()+"打印了CCC");
                return null;
            }
        }));

        // //setPriority 设置线程A的优先级 优先级一共有10个级别,分别数字1到10
        // 不给线程指定优先级时,默认优先级为5 优先级数字越大,先执行的概率越大
        threadA.setPriority(Thread.MAX_PRIORITY);
        // threadA.setPriority(10);
        threadB.setPriority(Thread.MIN_PRIORITY);
        // threadB.setPriority(1);
        threadC.setPriority(Thread.NORM_PRIORITY);
        // threadC.setPriority(5);
        threadA.setName("MAX_PRIORITY");
        threadA.start();
        threadB.setName("MIN_PRIORITY");
        threadB.start();
        threadC.setName("NORM_PRIORITY");
        threadC.start();

    }
}

        结果打印:

 二、Synchronized和Reentrantlock区别

2.1 同步和异步

        同步:多线程共享一个变量,当一个线程对该变量进行读或者写时,另一个线程不能对该变量进行写或者读,这就叫线程同步。

        异步:当一个线程执行一个方法,方法中业务复杂,发出请求,执行时间需要很长,不去等待该方法执行完成,继续执行其他方法,当上面方法执行完成再给我响应,这就叫异步,异步提高程序执行效率。

2.2 Synchronized的用法

2.2.1 Synchronized的常见用法:

        修饰普通方法

        修饰静态方法

        修饰代码块(参考实例)

实例演示:售票

package com.aaa.day02_mutilThread.demo2;

/**
 * @author :caicai
 * @date :Created in 2022/7/19 14:35
 * @description:线程同步使用
 * @description:Synchronized的常见用法:修饰普通方法 修饰静态方法 修饰代码块
 * @version:
 */
public class SynchronizedTest implements Runnable {
    // 票的总数
    private int ticketNum = 20;
    /**
     *  普通方法
     */
    public synchronized void methodA(){
        // 代码块
    }
    /**
     *  静态方法
     */
    public synchronized void methodB(){
        // 代码块
    }

    @Override
    public void run() {
        while (ticketNum>0){
            // synchronized同步块的使用
            // this就是锁对象,要求多线程执行时,多个线程拿到的锁对象必须是一个
            synchronized (this){
                // 不判空出现负票情况
                // System.out.println("售票员"+Thread.currentThread().getName()+"售出一张,总票还剩:"+(--ticketNum)+"张");
                // 避免出现负票
                if (ticketNum>0){
                    System.out.println("售票员"+Thread.currentThread().getName()+"售出一张,总票还剩:"+(--ticketNum)+"张");
                }
            }
        }
    }

    public static void main(String[] args) {
        SynchronizedTest test = new SynchronizedTest();
        new Thread(test,"张三").start();
        new Thread(test,"李四").start();
        new Thread(test,"王五").start();
    }
}

class Test implements Runnable{
    private int ticketNum =20;
    @Override
    public void run() {
        while (ticketNum>0){
            synchronized (this){
                if (ticketNum>0){
                    System.out.println("销售员"+Thread.currentThread().getName()+"售出一张,余票为"+(--ticketNum));
                }
            }
        }
    }

    public static void main(String[] args) {
        Test test = new Test();
        new Thread(test,"张三").start();
        new Thread(test,"李四").start();
        new Thread(test,"王五").start();
    }
}

结果打印:非判空状态

 判空状态

 2.2.2 synchronized原理

        一旦代码块加上synchronized关键字 ,在代码底层就会加上monitorenter会给对象锁的计算器+1 ,获取锁,其他没有获取到锁的就在外面等待,然后获取锁的执行代码块业务,等业务执行完毕,就会看到monitorexit会给对象锁的计数器-1,释放锁,等待才会获取锁,重复该步骤。

 

 打开Open in Terminal后输入:javap -v SynchronizedTest.class

 2.3 ReentrantLock用法

        线程同步的,jdk1.5之后提供API层面(只能用于同步块)同步锁,要使用lock和unlock配合try finally使用。

2.3.1 代码演示

package com.aaa.day02_mutilThread.demo2;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author :caicai
 * @date :Created in 2022/7/19 15:03
 * @description:线程同步锁 要使用lock 和 unlock配合 try finally
 * @modified By:
 * @version:
 */
public class ReentrantLockTest implements Runnable{
    // 票的总数
    private int ticketNum = 20;
    // 实例化一个锁对象
    private ReentrantLock reentrantLock = new ReentrantLock();

    @Override
    public void run() {
        while (ticketNum>0){
            // 加锁  线程不管能否执行完毕最后都是要解锁的 所以使用try finally 对执行的代码块进行异常捕获 确保最后解锁
            reentrantLock.lock();

            try {
                // 出现负票
                //System.out.println("售票员"+Thread.currentThread().getName()+"售出一张,总票还剩:"+(--ticketNum)+"张");

                // 避免出现负票
                if (ticketNum>0){
                    //System.out.println(1/0);
                    System.out.println("售票员"+Thread.currentThread().getName()+"售出一张,总票还剩:"+(--ticketNum)+"张");
                }
            } finally {
                // 解锁
                reentrantLock.unlock();
            }
        }
    }


    public static void main(String[] args) {
        ReentrantLockTest test = new ReentrantLockTest();
        new Thread(test,"张三").start();
        new Thread(test,"李四").start();
        new Thread(test,"王五").start();
    }
}

 结果展示:

2.3.2 提高其他更高级功能:

1)ReentrantLock,当一个线程长时间拿不到锁时,会放弃等待

2)ReentrantLock 支持公平锁

3)ReentrantLock 支持多个对象加锁

2.4 Synchronized和Reentrantlock区别

        synchronized 早期的实现比较低效,对比 ReentrantLock,大多数场景性能都相差较大,但是在 Java 6 中对synchronized 进行了非常多的改进。

主要区别如下:

1)ReentrantLock 使用起来比较灵活,但是必须有释放锁的配合动作;

2)ReentrantLock 必须手动获取与释放锁,而 synchronized 不需要手动释放和开启锁;

3)ReentrantLock 只适用于代码块锁,而 synchronized 可用于修饰方法、代码块等。

4)volatile 标记的变量不会被编译器优化;synchronized 标记的变量可以被编译器优化。

三、sleep和wait区别

        1)sleep是Thread类中的静态方法,wait是Object中的成员方法

        2)sleep在不在同步块都可以执行,wait必须在同步块中执行,否则报IllegalMonitorStateException异常  (wait执行时要释放锁,如果不在同步中,无法获取锁,无法获取就无法释放)

      3)sleep,wait都在同步块中时区别,sleep不释放锁,必须等业务都执行完成后,再释放锁。wait释放锁,直到有其他线程唤醒才会继续执行,被唤醒时不是立马执行,只是具备拿锁资格,要等正在执行的线程执行完毕,释放锁,wait的线程才会执行

        sleep代码演示:

package com.aaa.day02_mutilThread.demo3;

/**
 * @author :caicai
 * @date :Created in 2022/7/19 15:49
 * @description:休息时长
 * @modified By:
 * @version:
 */
public class SleepTest {
    public static void main(String[] args) {
        Thread threadA = new Thread(){
            @Override
            public void run() {

                System.out.println(111);
                try {
                    // 让线程A先休息 3s
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(222);
            }
        };

        Thread threadB = new Thread(){
            @Override
            public void run() {

                System.out.println(333);
                try {
                    // 让线程B在休息5s
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(444);
            }
        };

        threadA.start();
        threadB.start();
    }
}

        结果打印:

         wait代码演示

package com.aaa.day02_mutilThread.demo3;

/**
 * @author :caicai
 * @date :Created in 2022/7/19 15:49
 * @description:等待唤醒
 * @modified By:
 * @version:
 */

public class WaitTest {
    private static Object object = new Object();
    public static void main(String[] args) {
        Thread threadA = new Thread(){
            @Override
            public void run() {
                // 加锁
                synchronized (object) {
                    System.out.println(111);
                    try {
                        // wait执行时要释放锁,如果不在同步中,无法获取锁,无法获取就无法释放
                        // wait释放锁,直到有其他线程唤醒才会继续执行,被唤醒时不是立马执行,只是具备拿锁资格,要等正在执行的线程执行完毕,释放锁,wait的线程才会执行
                        object.wait();
                        // 在同步块中时区别,sleep不释放锁,必须等业务都执行完成后,再释放锁
                        //Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    object.notifyAll();
                    System.out.println(222);
                }
            }
        };

        Thread threadB = new Thread(){
            @Override
            public void run() {
                // 加锁
                synchronized (object) {
                    System.out.println(333);
                    try {
                        // 唤醒 wait释放锁  object.notifyAll()唤醒所有;
                        object.notify();
                        Thread.sleep(1000);
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(444);
                }
            }
        };
        threadA.start();
        threadB.start();

    }
}

         结果展示

package com.aaa.day02_mutilThread.demo3;

/**
 * @author :caicai
 * @date :Created in 2022/7/19 15:49
 * @description:等待唤醒
 * @modified By:
 * @version:
 */
public class WaitTest {
    private static Object object = new Object();
    public static void main(String[] args) {
        Thread threadA = new Thread(){
            @Override
            public void run() {
                // 加锁
                synchronized (object) {
                    System.out.println(111);
                    try {
                        // wait执行时要释放锁,如果不在同步中,无法获取锁,无法获取就无法释放
                        // wait释放锁,直到有其他线程唤醒才会继续执行,被唤醒时不是立马执行,只是具备拿锁资格,要等正在执行的线程执行完毕,释放锁,wait的线程才会执行
                        object.wait();
                        // 在同步块中时区别,sleep不释放锁,必须等业务都执行完成后,再释放锁
                        //Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    object.notifyAll();
                    System.out.println(222);
                }
            }
        };
        Thread threadB = new Thread(){
            @Override
            public void run() {
                // 加锁
                synchronized (object) {
                    System.out.println(333);
                    try {
                        // 唤醒 wait释放锁  object.notifyAll()唤醒所有;
                        object.notify();
                        object.wait();
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(444);
                }
            }
        };
        threadA.start();
        threadB.start();
    }
}

        结果展示:

 四、死锁及如何避免死锁

4.1 死锁的现象

        线程1 持有 资源1(锁1) ,线程2持有资源2(锁2),线程1想要获取资源2(锁2) 线程2要获取资源1(锁1),像这种互相持有对方需要的锁,而发生的阻塞现象,我们称为死锁。

4.2 死锁实例演示

        父亲 拥有玩具 想要儿子成绩单,儿子 拥有成绩单,想要父亲玩具。都想让对方先给, 这样构成死锁

        父亲:

package com.aaa.day02_mutilThread.demo4;

public class Father {
    public void say(){
        System.out.println("你把成绩单给我,我就给你玩具");
    }
    public void get(){
        System.out.println("获得成绩单");
    }
}

        儿子:

package com.aaa.day02_mutilThread.demo4;

public class Son {
    public void say(){
        System.out.println("你把玩具给我,我就给你成绩单");
    }
    public void get(){
        System.out.println("获得玩具");
    }
}

        死锁的形成:

package com.aaa.day02_mutilThread.demo4;

/**
 * @author :caicai
 * @date :Created in 2022/7/19 16:15
 * @description:死锁案例
 * @modified By:
 * @version:
 */
public class DeadLock implements Runnable{
    // 业务对象
    private Father father = new Father();
    private Son son = new Son();
    private boolean isFather;
    // 模拟两把锁
    private static Object lock1 = new Object();
    private static Object lock2 = new Object();
    @Override
    public void run() {
        // 让父亲和儿子一起执行业务
        if (isFather){
            synchronized (lock1){
                // 父亲说话
                father.say();
                try {
                    // 先休眠让儿子有机会拿到lock2
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock2) {
                    // 父亲获取资源
                    father.get();
                }
            }
        }else {
            synchronized (lock2){
                // 儿子说话
                son.say();
                try {
                    // 先休眠让儿子有机会拿到lock1
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock1) {
                    // 儿子获取资源
                    son.get();
                }
            }
        }

    }
    public static void main(String[] args) {
        DeadLock deadLock1 = new DeadLock();
        deadLock1.isFather = true;

        DeadLock deadLock2 = new DeadLock();
        deadLock2.isFather = false;

        // 启动线程
        new Thread(deadLock1,"father").start();
        new Thread(deadLock2,"son").start();
    }
}

        结果打印:

4.2.1 如何避免死锁

1) 禁止一个线程持同时持有多个锁

        父亲不要同时对lock1和lock2进行锁定 等等

2)具备相同的加锁顺序

        父亲先拿lock1 再拿lock2 儿子也先拿lock1 再拿lock2

3) 设置锁超时

        现实业务中,一个线程拿到锁后最好设置锁过期,因为业务异常或者其他原因长时间持久锁时,锁会自动过期,有效避免死锁。

4) 死锁检测

        使用一定算法拿锁前,先检测会不会发生死锁,如果会就不去拿该锁(适用相同加锁顺序和锁超时都无法使用的场景)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

菜鸡本蔡

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

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

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

打赏作者

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

抵扣说明:

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

余额充值