高级Java编程-----JUC基础入门(一)

注:本系列教程只适用于JUC入门

什么是JUC

JUC 就是JavaJDK中的(java.util.concurrent)包的名字的缩写,在jdk1.5开始提供,在此包中增加了在并发编程中很常用的实用工具类,用于定义类似于线程的自定义子系统,包括线程池、异步IO 和轻量级任务框架。提供可调的、灵活的线程池。还提供了设计用于多线程上下文中Collection 实现等。
在这里插入图片描述

回顾初级多线程

我们在学习Java的初级阶段的时候我们,我们一定学过线程,我们回忆一下,创建线程的两大方法

  1. 继承Thread类,重写run方法
  2. 实现Runnable接口

还有一点我们需要注意,重写run方法并不能开启线程,开启线程需要调用Thread.start()方法

我们既然回顾,那就再多写一点,我们来看一道经典的电影院窗口买票的例子

继承Thread类实现

首先:我们来通过继承Thread方法来实现多线程

代码如下:

public class OldThread {
    public static void main(String[] args) {
        Ticket ticket1 = new Ticket("1号窗");
        Ticket ticket2 = new Ticket("2号窗");
        Ticket ticket3 = new Ticket("3号窗");
        ticket1.start();
        ticket2.start();
        ticket3.start();
    }
}
class Ticket extends Thread {


    public Ticket() {
    }

    public Ticket(String name) {
        super(name);
    }

    private static int totalTicket = 100;

    @Override
    public  void run() {
        while (true){
            if (totalTicket > 0) {
                System.out.println(getName() + "卖出第" + (totalTicket--) + "张票,还剩下" + totalTicket + "张票");
            } else {
                break;
            }
        }

    }
}

运行截图:
在这里插入图片描述
问题分析:

我们明显能看出99张票买了2次,这在我们实际生活中是不被允许的,所以我们需要在run方法上加上synchronized,通过加锁来实现同步,让我们的票同一时间只能有一个被线程操作。

public class OldThread {
    public static void main(String[] args) {
        Ticket ticket1 = new Ticket("1号窗");
        Ticket ticket2 = new Ticket("2号窗");
        Ticket ticket3 = new Ticket("3号窗");
        ticket1.start();
        ticket2.start();
        ticket3.start();
    }
}
class Ticket extends Thread {


    public Ticket() {
    }

    public Ticket(String name) {
        super(name);
    }

    private  static int totalTicket = 100;

    @Override
    public synchronized void run() {
        while (true){
            if (totalTicket > 0) {
                System.out.println(getName() + "卖出第" + (totalTicket--) + "张票,还剩下" + totalTicket + "张票");
            } else {
                break;
            }
        }

    }
}

实现Runnable接口

实现Runnable接口来创建开启线程有两种方法,第一,通过在操作类上实现Runnable接口,第二,通过匿名内部类方法实现Runnable接口

public class OldRunnable {
    public static void main(String[] args) {
        Ticket myRunnable = new Ticket();
        new Thread(myRunnable, "1号窗").start();
        new Thread(myRunnable, "2号窗").start();
        new Thread(myRunnable, "3号窗").start();
    }
}


class Ticket implements Runnable {

    private  int totalTicket = 100;

    @Override
    public  void run() {
        while (true){
            if (totalTicket > 0) {
                System.out.println(Thread.currentThread().getName() + "卖出第" + (totalTicket--) + "张票,还剩下" + totalTicket + "张票");
            } else {
                break;
            }
        }
    }
}

匿名内部类实现多线程

public class AnonymousRunnable {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            private int ticket = 30;
            @Override
            public void run() {
                while (true) {
                    if (ticket > 0) {
                        System.out.println(Thread.currentThread().getName() + "卖出第" + (ticket--) + "张票,还剩下" + ticket + "张票");
                    } else {
                        break;
                    }
                }
            }
        },"窗口1").start();
    }
}

多线程的状态

public enum State {
		//创建
        NEW,  
        //就绪,可运行
        RUNNABLE,
		//阻塞 wait,sleep
        BLOCKED,
		//等待,一直等待,(俗称:不见不散)
        WAITING,
        //在一定时间内等待,时间一到,就继续运行(俗称:过期不候)
        TIMED_WAITING,
		//终结
        TERMINATED;
    }
wait和sleep的区别

wait,放开手里的锁阻塞
sleep,不放开锁阻塞

wait和sleep的主要区别是放不放手里的锁

线程的生命周期图

在这里插入图片描述

Lock锁

我们在java.util.concurrent.locks包下我们发现了Lock接口
在这里插入图片描述
我们发现官方文档直接说,Lock实现提供比使用synchronized方法和语句可以获得的更广泛的锁定操作。也就是说Lock的锁机制,比synchronized的更加好。

那他的使用方法是什么呢?

因为我们知道Lock是一个接口,那么我们看看该接口是否有他自己的实现类,实现类如下
在这里插入图片描述
共有三个实现类,但是我们一般使用的是第一个实现类。官方给的使用方法为:

 Lock l = ...;  //我们一般写为 Lock l = new ReentrantLock();
 l.lock(); 
 try {
     // access the resource protected by this lock 
} finally { 
l.unlock(); 
} 

所以我们就明白了我们的加锁机制

之前代码:

class Ticket{
    private int number = 30;
    public void saleTicket(){
        if (number > 0) {
            System.out.println(Thread.currentThread().getName() + "正在卖第" + (number--) + "张票,还剩" + number + "张票");
        }
    }
}

/**
 * 多线程编程的口诀:线程  操作   资源类
 */
public class MyLock {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 40; i++) {
                    ticket.saleTicket();
                }
            }
        },"A").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 40; i++) {
                    ticket.saleTicket();
                }
            }
        },"B").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 40; i++) {
                    ticket.saleTicket();
                }
            }
        },"C").start();
    }
}

然后我们就可以将我么们的代码改写

class Ticket{
    private int number = 30;

    private Lock l = new ReentrantLock();

    public void saleTicket(){
        l.lock();
        try {
            if (number > 0) {
                System.out.println(Thread.currentThread().getName() + "正在卖第" + (number--) + "张票,还剩" + number + "张票");
            }
        }finally {
            l.unlock();
        }
    }
}

/**
 * 多线程编程的口诀:线程  操作   资源类
 */
public class MyLock {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 40; i++) {
                    ticket.saleTicket();
                }
            }
        },"A").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 40; i++) {
                    ticket.saleTicket();
                }
            }
        },"B").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 40; i++) {
                    ticket.saleTicket();
                }
            }
        },"C").start();
    }
}

其实匿名内部类我们也可以通过lambda表达式来进行实现

lambda表达式口诀拷贝小括号,写死右箭头,落地大括号

改写后

class Ticket{
    private int number = 30;

    private Lock l = new ReentrantLock();

    public void saleTicket(){
        l.lock();
        try {
            if (number > 0) {
                System.out.println(Thread.currentThread().getName() + "正在卖第" + (number--) + "张票,还剩" + number + "张票");
            }
        }finally {
            l.unlock();
        }
    }
}

/**
 * 多线程编程的口诀:线程  操作   资源类
 */
public class MyLock {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();

        new Thread(() -> { for (int i = 0; i < 40; i++) { ticket.saleTicket(); } },"A").start();

        new Thread(()-> {for (int i = 0; i < 40; i++) {ticket.saleTicket(); }},"B").start();

        new Thread(()->{for (int i = 0; i < 40; i++) {ticket.saleTicket(); }},"C").start();
    }
}

我们发现代码无比简洁,更有条理性,更加容易阅读,提高了观赏性。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值