java多线程 知识点及实例结合

这篇博客讨论了在Java中实现多窗口同时售卖电影票时遇到的线程安全问题,如票重复售卖和负数票。作者列举了四种解决线程安全的方法:同步代码块、同步方法、使用ReentrantLock(重入锁)。通过这些同步机制,确保了票的正确售卖,避免了并发问题。总结了synchronized和Lock的区别,包括锁的可重入性、中断、公平性和释放方式等。
摘要由CSDN通过智能技术生成

实际案例:多窗口同时售卖电影票。

/**
 * @PACKAGE_NAME: PACKAGE_NAME
 * @Description:
 * @Author: liangxu
 * @Date: 2021/8/14 11:06 上午
 * @Version: V1.0
 */
public class Ticket implements Runnable{

    private int ricknum = 100;//电影票数量

    @Override
    public void run() {
        while (true){//一直售卖  死循环
            if(ricknum > 0){//判断是否有票
                //有票,让线程睡眠100毫秒
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
               //打印当前售出的票数字和线程名,票数-1
               final String name= Thread.currentThread().getName();
               System.out.println("线程:"+name+"销售电影票:"+ ricknum--);
            }
        }
    }

    public static void main(String[] args) {
        //创建电影票对象
        Ticket ticket = new Ticket();
        //创建Thread对象,执行电影票售卖
        new Thread(ticket,"窗口1").start();
        new Thread(ticket,"窗口2").start();
        new Thread(ticket,"窗口3").start();
    }
	/**
    * 以上出现线程安全问题  售卖结果存在有些票有售卖多次的情况,而且出现了收费票为0和负数的情况
    */

}

解决以上问题的的方法 – 线程同步

    /**
     * 1.同步代码块
     * 2.同步方法
     * 3.同步锁 (比同步方法效率高)
     * 4.特殊域及变量
     * 5.局部变量
     * 6.阻塞队列
     * 7.原子变量
     */

1.同步代码块

/**
 * @PACKAGE_NAME: PACKAGE_NAME
 * @Description:
 * @Author: liangxu
 * @Date: 2021/8/14 11:06 上午
 * @Version: V1.0
 */
public class Ticket implements Runnable{

    private int ricknum = 100;//电影票数量
    private Object obj = new Object();//创建锁对象

    @Override
    public void run() {
        while (true){//一直售卖  死循环
            synchronized (obj){ //同步代码块
                if(ricknum > 0){//判断是否有票
                    //有票,让线程睡眠100毫秒
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //打印当前售出的票数字和线程名,票数-1
                    final String name = Thread.currentThread().getName();
                    System.out.println("线程:"+name+"销售电影票:"+ ricknum--);
                }
            }
        }
    }

    public static void main(String[] args) {
        //创建电影票对象
        Ticket ticket = new Ticket();
        //创建Thread对象,执行电影票售卖
        new Thread(ticket,"窗口1").start();
        new Thread(ticket,"窗口2").start();
        new Thread(ticket,"窗口3").start();
        /**
	    * 以上线程安全
	    */
    }
}

2.同步方法

/**
 * @PACKAGE_NAME: PACKAGE_NAME
 * @Description:
 * @Author: liangxu
 * @Date: 2021/8/14 11:06 上午
 * @Version: V1.0
 */
public class Ticket implements Runnable{

    private int ricknum = 100;//电影票数量

    @Override
    public void run() {
        while (true){//一直售卖  死循环
            saleTicket();
        }
    }

    //不是静态方法 锁对象synchronized(this) 或者说 为new出来的Ticket对象
    //static 静态方法  锁对象synchronized(Ticket.class)
    private /*static*/ synchronized void saleTicket(){
        if(ricknum > 0){//判断是否有票
            //有票,让线程睡眠100毫秒
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //打印当前售出的票数字和线程名,票数-1
            final String name = Thread.currentThread().getName();
            System.out.println("线程:"+name+"销售电影票:"+ ricknum--);
        }
    }

    public static void main(String[] args) {
        //创建电影票对象
        Ticket ticket = new Ticket();
        //创建Thread对象,执行电影票售卖
        Thread t1 = new Thread(ticket, "窗口1");
        Thread t2 = new Thread(ticket,"窗口2");
        Thread t3 = new Thread(ticket,"窗口3");
        //启动线程 开始售卖
        t1.start();
        t2.start();
        t3.start();
        /**
	    * 以上线程安全
	    */
    }
}

同步锁 为一种比较复杂的机制

存在多种锁:读写锁、重入锁等等
下面以重入锁为例:ReentrantLock

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

/**
 * @PACKAGE_NAME: PACKAGE_NAME
 * @Description:
 * @Author: liangxu
 * @Date: 2021/8/14 11:06 上午
 * @Version: V1.0
 */
public class Ticket implements Runnable{

    private int ricknum = 100;//电影票数量
    /**
     * 重入锁  参数为是否公平锁,
     * true:多个线程都公平拥有执行权(每个线程都公平的执行一段时间)
     * false:非公平,独占锁。默认值  线程只要不释放,那么可以一直拥有执行权
     */
    private Lock lock = new ReentrantLock(true);


    @Override
    public void run() {
        while (true){//一直售卖  死循环
            lock.lock();//加锁
            /**
             * 使用lock锁机制,必须保证lock调用后,unlock一定会被调用,估我们采用try finally方式使用
             * 否则就会出现死锁
             */
            try {
                if(ricknum > 0){//判断是否有票
                    //有票,让线程睡眠100毫秒
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //打印当前售出的票数字和线程名,票数-1
                    final String name = Thread.currentThread().getName();
                    System.out.println("线程:"+name+"销售电影票:"+ ricknum--);
                }
            }finally {
                lock.unlock();//释放锁
            }
        }
    }


    public static void main(String[] args) {
        //创建电影票对象
        Ticket ticket = new Ticket();
        //创建Thread对象,执行电影票售卖
        Thread t1 = new Thread(ticket, "窗口1");
        Thread t2 = new Thread(ticket,"窗口2");
        Thread t3 = new Thread(ticket,"窗口3");
        //启动线程 开始售卖
        t1.start();
        t2.start();
        t3.start();
        /**
         * 以上出现安全
         * 每个窗口都公平售卖
         */
    }
}

在这里插入图片描述

小结:

synchronized 和 Lock 区别
1.synchronized 是java内置关键字,在jvm层面,Lock是个java类。
2.synchronized 无法获取是否获取锁的状态,lock可以判断是否获取到锁
3.synchronized会自动释放锁(a线程执行完同步代码会释放锁;b线程执行过程中发生异常会释放锁),lock需要再finally中手工释放锁(unlock方法释放),否则容易造成线程死锁
4.用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而lock锁就不一定会等待下去,如果城市获取不到锁,线程可以不用一直等待就结束了
5.synchronized的锁可重入、不可中断、非公平,而lock锁可重入、可判断、可公平(两者皆可)
6.lock锁适合大量的同步代码的同步问题,synchronized锁适合代码少量的同步问题。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值