「并发面试」JUC基础

116 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情

1. JUC

1.1 概述

JUC:java.util.concurrent并发编程包

进程:系统中正在运行的一个程序。

线程:程序执行的最小单元

Wait:Object类的方法,会释放锁(当前线程有锁的情况下)

Sleep:Thread类的方法,不会释放锁,不会占用锁

两个方法都会被interrupted方法中断

并发:多个线程访问一个资源;春运抢票

并行:多个线程一起执行一个任务,每个线程执行一部分操作;边刷牙边烧水

1.2 Synchronized和Lock

Sychronized

使用方式

针对上锁的操作方法,可以实现每次只允许一个线程访问

public sychronized int saleTicket(){
    .....
}

Lock

使用方式

//创建可重入锁
private final ReenrantLock lock=new ReentrantLock();
​
public void lockSale(){
  //加锁
  lock.lock();
  ...
  //解锁
  lock.unlock();
}

概念和实现原理

可重入锁:在进入操作方法的第一步上锁,出操作方法的最后一步解锁

Lock和Sychronized的区别

1、Lock需要手动上锁和释放锁,Sychronized不需要手动操作(异常自动释放锁),只需要加到对应的操作方法上即可

2、LockSychronized功能强大。Lock可扩展性更强(知道是否获取到锁、可以让那个等待线程响应中断)

3、Lock性能要高于Sychronized

1.3 并发编程思路

  1. 创建资源类,在类中创建属性和操作方法
  2. 创建多个线程,调用资源类的操作方法

针对这个操作方法赋予锁

/**
 * 1. 创建资源类,创建属性和操作方法
 * 2. 创建多个线程去调用资源类的操作方法
 */
public class SaleTicket {
    public static void main(String[] args) {
​
        Ticket ticket = new Ticket();
        //创建多个线程,每个线程都去调用资源类的操作方法
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    ticket.sale();
                }
            }
        }, "A").start();
       //lambda表达式写法
        new Thread(()->{
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    ticket.sale();
                }
            }
        }, "B").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    ticket.sale();
                }
            }
        }, "C").start();
    }
​
}
​
​
class Ticket {
    private int ticketNum = 10000;
    /**
     * 使用Synchronized来加锁
     * 1. 操作方法上加上synchronized关键字
     * 2. 业务操作
     */
    public void synchronizedSale() {
        //public synchronized void synchronizedSale(){}一样的操作,都是对对象加锁
        synchronized (this) {
            if (ticketNum > 0) {
                System.out.println("线程:" + Thread.currentThread().getName() + "售出第" + (10000 - ticketNum) + "张票,还剩下" + --ticketNum + "张票");
            }
        }
    }
    /**
     * 使用Lock来加锁
     * 1. 创建可重入锁
     * 2. 加锁
     * 3. 业务操作
     * 4. 解锁
     */
    private final ReentrantLock lock = new ReentrantLock();
​
    public void lockSale() {
        lock.lock();
        try {
            if (ticketNum > 0) {
                System.out.println("线程:" + Thread.currentThread().getName() + "售出第" + (10000 - ticketNum) + "张票,还剩下" + --ticketNum + "张票");
            }
        } finally {
            lock.unlock();
        }
    }
}
​

1.4 线程间通信(线程交替进行)

  1. 创建资源类,在类中创建属性和操作方法

  2. 操作方法

    1. 判断是否有锁(this.wait())
    2. 业务处理
    3. 通知另一个线程 notify()
  3. 创建多个线程,调用资源类的操作方法

使用synchnorized实现线程交替执行

this.wait()线程等待

this.notifyAll()线程唤醒

/**
 * 1. 创建资源类,创建属性和操作方法
 * 2. 判断、操作、通知
 * 3. 创建多个线程调用操作方法
 */
public class AlternateThread {
    public static void main(String[] args) {
        AlternateTicket ticket = new AlternateTicket();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    ticket.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    ticket.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
    }
}
class AlternateTicket {
    private int num = 10;
    public synchronized void incr() throws InterruptedException {
        //判断
        if (num > 10) {
            this.wait();
        }
        //操作
        num++;
        System.out.println(Thread.currentThread().getName() + ":" + num);
        //通知
        this.notifyAll();
    }
    public synchronized void decr() throws InterruptedException {
        //判断
        if (num <= 10) {
            this.wait();
        }
        //操作
        num--;
        System.out.println(Thread.currentThread().getName() + ":" + num);
        //通知
        this.notifyAll();
    }
}

可以看到A和B交替执行且输出1011

使用lock实现线程交替执行

  • 使用Lock.condition()对象进行线程等待和线程唤醒
  • lock.await()线程等待
  • lock.signalAll()唤醒所有线程

/**
 * 1. 创建资源类,创建属性和操作方法
 * 2. 判断、操作、通知
 * 3. 创建多个线程调用操作方法
 */
public class LockThread {
    public static void main(String[] args) {
        AlternateLockTicket ticket = new AlternateLockTicket();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    ticket.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    ticket.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
    }
}

class AlternateLockTicket{
    private int num=10;
    //创建锁对象
    private final Lock lock =new ReentrantLock();
    Condition condition=lock.newCondition();
    public void incr() throws InterruptedException {
        lock.lock();
        try{
            //判断
            if (num>10){
                condition.await();
            }
            //操作
            num++;
            System.out.println(Thread.currentThread().getName() + ":" + num);
            //通知
            condition.signalAll();
        }finally {
            lock.unlock();
        }
    }
    public void decr() throws InterruptedException {
        lock.lock();
        try{
            //判断
            if (num<=10){
                condition.await();
            }
            //操作
            num--;
            System.out.println(Thread.currentThread().getName() + ":" + num);
            //通知
            condition.signalAll();
        }finally {
            lock.unlock();
        }
    }
}