[JUC笔记]2.LOCK

多线程编程步骤

  1. 创建资源类,在资源类创建属性和操作方法【也就是run方法】
    • 什么是资源类?
      • 类似于空调,每个人都可以买,每个人都可以控制空调的各个功能
    • 高内聚低耦合
  2. 创建多个线程,调用资源类的操作方法

覆写run的目的:将自定义代码存储在run方法,让线程运行。

start方法:调用线程并执行该线程run方法

创建线程的多种方式:

  • 继承Thread类 【Java是单继承,因此很少用这种方式】
  • 实现Runnable接口
    • Thread(Runnable target,String name)
  • 使用Callable接口
  • 使用线程池

卖票举例代码

package sync;

import java.sql.Timestamp;

// 第一步: 创建资源类,定义属性和操作方法
class Ticket
{
    // 属性:票数
    private int number = 30;

    /**
     * 操作方法:买票
     * 但是因为是多线程操作,因此需要添加关键字: synchronized
     */
    public synchronized void sale()
    {
        if (number > 0)
        {
            System.out.println(Thread.currentThread().getName() + "卖出:" + (number--) + "  剩下:" + number);
        }
    }
}

public class SaleTicket
{
    // 第二部:创建多个线程,调用资源类的操作方法
    public static void main(String[] args)
    {
        // 创建ticket对象
        Ticket ticket = new Ticket();
        // 创建三个线程,采用匿名内部类的方式
        new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                // 调用买票的方法
                for(int i=0;i<40;i++)
                {
                    ticket.sale();
                }
            }
        },"AA").start();
        // 第二个线程
        new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                // 调用买票的方法
                for(int i=0;i<40;i++)
                {
                    ticket.sale();
                }
            }
        },"BB").start();

        // 第三个线程
        new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                // 调用买票的方法
                for(int i=0;i<40;i++)
                {
                    ticket.sale();
                }
            }
        },"CC").start();
    }

}
AA卖出:30  剩下:29
AA卖出:29  剩下:28
AA卖出:28  剩下:27
AA卖出:27  剩下:26
AA卖出:26  剩下:25
AA卖出:25  剩下:24
AA卖出:24  剩下:23
AA卖出:23  剩下:22
AA卖出:22  剩下:21
AA卖出:21  剩下:20
AA卖出:20  剩下:19
AA卖出:19  剩下:18
AA卖出:18  剩下:17
AA卖出:17  剩下:16
AA卖出:16  剩下:15
AA卖出:15  剩下:14
AA卖出:14  剩下:13
AA卖出:13  剩下:12
AA卖出:12  剩下:11
AA卖出:11  剩下:10
BB卖出:10  剩下:9
BB卖出:9  剩下:8
BB卖出:8  剩下:7
BB卖出:7  剩下:6
BB卖出:6  剩下:5
BB卖出:5  剩下:4
BB卖出:4  剩下:3
BB卖出:3  剩下:2
BB卖出:2  剩下:1
BB卖出:1  剩下:0

Process finished with exit code 0

LOCK接口

synchronized是自动上锁、解锁 , 而LOCK接口能够手动实现上锁。

java.util.concurrent.locks

实现类

  • ReentrantLock: 可重入锁
  • ReentrantReadWriteLock.ReadLock
  • ReentrantReadWriteLock.WriteLock

可重入锁举例: 上厕所,你进去把厕所门上锁,你出来了在把门解锁,下一个人进去了上锁,出来的时候解锁。就是这样的。

package Lock;

import java.util.concurrent.locks.ReentrantLock;

// 创建资源类,定义属性和操作方法
class Lticket
{
    // 票数量
    private int number = 30;
    // 创建可重入锁
    private final ReentrantLock lock = new ReentrantLock();

    // 买票方法
    public void sale()
    {
        // 上锁
        lock.lock();
        // 判断是否有票可卖
        /*if (number > 0)
        {
            System.out.println(Thread.currentThread().getName() + ":卖出了" + number-- + " s");
        }
        //        解锁
        lock.unlock();*/
        /*上面这么写有可能出现一个问题,上锁之后,if判断语句里面出异常了,就会无法释放锁,造成死锁了
        * 因此,采用try_catch */
        try
        {
//            判断有票可卖
            if(number>0)
            {
                System.out.println(Thread.currentThread().getName() + ": 卖出" + (number--) + "剩余: " + number);
            }
        }
        finally
        {
            lock.unlock();
        }

    }
}

public class LSaleTicket
{
    //    第二步:创建多个线程,调用资源类的属性与方法
    public static void main(String[] args)
    {
        Lticket ticket = new Lticket();
        // 创建线程,采用Lambda 方式
        new Thread(()->
        {
            for (int i=0;i<40;i++)
            {
                ticket.sale();
            }

        },"AA").start();

        new Thread(()->
        {
            for (int i=0;i<40;i++)
            {
                ticket.sale();
            }

        },"BB").start();

        new Thread(()->
        {
            for (int i=0;i<40;i++)
            {
                ticket.sale();
            }

        },"CC").start();

    }
}

AA: 卖出30剩余: 29
AA: 卖出29剩余: 28
AA: 卖出28剩余: 27
AA: 卖出27剩余: 26
AA: 卖出26剩余: 25
AA: 卖出25剩余: 24
AA: 卖出24剩余: 23
CC: 卖出23剩余: 22
CC: 卖出22剩余: 21
CC: 卖出21剩余: 20
CC: 卖出20剩余: 19
CC: 卖出19剩余: 18
CC: 卖出18剩余: 17
CC: 卖出17剩余: 16
CC: 卖出16剩余: 15
CC: 卖出15剩余: 14
CC: 卖出14剩余: 13
CC: 卖出13剩余: 12
CC: 卖出12剩余: 11
CC: 卖出11剩余: 10
CC: 卖出10剩余: 9
CC: 卖出9剩余: 8
CC: 卖出8剩余: 7
CC: 卖出7剩余: 6
CC: 卖出6剩余: 5
CC: 卖出5剩余: 4
CC: 卖出4剩余: 3
CC: 卖出3剩余: 2
CC: 卖出2剩余: 1
CC: 卖出1剩余: 0

Process finished with exit code 0

LOCK与synchronized 的区别

  1. Lock不是Java语言内置的,synchronize是Java语言的关键字,是内置的。Lock是一个类,通过这个类可以实现同步访问。
  2. 采用Synchronized不需要用户手动释放锁。当synchronized方法或者代码块执行结束之后,系统会自动让线程释放锁。
    • 而Lock必须手动释放锁,如果没有手动主动释放锁,就有可能出现死锁的现象。
  3. Lock 可以让等待锁的线程响应中断,
    • 而 synchronized 却不行,使用synchronized 时,等待的线程会一直等待下去,不能够响应中断;
  4. 通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到。
  5. Lock 可以提高多个线程进行读操作的效率。
posted @ 2021-10-22 21:19  取我方天画戟来  阅读(41)  评论(0)    收藏  举报