JUC并发编程详解

一,什么是JUC

JUC是java.util.concurrent包的缩写,用于多线程并发编程,为了处理高并发任务。 

三个工具包,可以看帮助文档,想看的话点链接api帮助文档

先记住一点:普通线程代码用Thread,Runnable没有返回值并且效率比Callable要低(有个印象,后面会证明)

二,线程和进程的关系

(1)举一个例子

你用qq,当年打开qq时,这是开启了一个进程,这时你玩qq,同时和你朋友打视频然后给另一个朋友发信息,还可以看qq空间,其中就涉及了三个线程。从中可以得知一个进程可以包含多个线程,至少有一个线程。

(2)java默认的线程有几个?

两个 main线程和GC(垃圾回收器)线程 第二个需要你了解JVM

(3)面试题:java可以开启线程?

开不了,给你看看代码

第一段代码

public class demo01 {
    public static void main(String[] args) {
        new Thread().start();
    }
}

第二段代码 进入start()里面的方面 (不用看全部代码)

public synchronized void start() {
       
        if (threadStatus != 0)
            throw new IllegalThreadStateException();
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
         
            }
        }
    }

第三段代码 进入start0();

private native void start0();

此时发现有native关键字,这是需要你了解jvm体系结构,有一个本地方法栈,调用了本地方法,在底层,就是java代码调用了一个非java代码写的接口,如果你了解操作系统,操作系统给用户提供了程序接口(通过程序调用)和命令接口(直接调用),主要作用就是隐藏复杂的实现细节,你只需调用我就可以实现功能,从中就可得知,线程的开启是操作系统帮我们完成的 

(4)并发和并行

假如有两个线程,

若cpu只有一核,此时两个线程同时是start(),此时就会出现并发现象,只有一个运行完了才能下一个,并发是一种快速交替,让你感觉好像是同时进行,但实际上不是。

若cpu是二核,则是真正的并行,同时运行。

并发编程本质:充分利用cpu的资源。并行可以用线程池来解决。

public class demo01 {
    public static void main(String[] args) {
        //获取cpu的核数
        System.out.println(Runtime.getRuntime().availableProcessors());
    }
}

三,线程的状态

(1)直接上代码

第一段

public class demo01 {
    public static void main(String[] args) {
         Thread.State
    }
}

第二段直接进入State,发现是一个枚举

public enum State {
       //新生,就绪
        NEW,

       //运行
        RUNNABLE,

      //阻塞
        BLOCKED,

     //等待  会一直等下去
        WAITING,
     
    //超时等待  不会
        TIMED_WAITING,

      //死亡
        TERMINATED;
    }

(2)面试题:请回答wait()和sleep()这两个方法的区别

第一:所属的类不一样 wait方法是Object类 而sleep方法是Thread类

第二:使用的范围不一样,wait方法只能在同步代码块中,而sleep在哪里都可以

第三:wait方法会使调用者释放锁资源,而sleep不会让调用者释放锁资源

第四:wait不需要捕获异常,而sleep需要捕获异常,因为怕发生超时等待

四,lock锁(重点)

1,传统的synchronized

上代码

public class demo01 {
    public static void main(String[] args) {
       Ticket ticket=  new Ticket();
        new Thread(
                ()->{
            for (int i = 0; i < 30; i++) {
                ticket.sale();
            }
            },"A").start();
        new Thread(()->{for (int i = 0; i < 30; i++) {
            ticket.sale();
        }},"B").start();
        new Thread(() ->{for (int i = 0; i < 30; i++) {
            ticket.sale();
        }},"C").start();
    }
}
class Ticket
{
    private int number=50;
    public void sale()
    {
        if(number>0)
        {
            System.out.println(Thread.currentThread().getName()+"买的票号是"+(number--)+"还剩余"+number+"张票");
        }
    }
}

会有问题加上synchronized就可以解决

class Ticket
{
    private int number=50;
    public synchronized void  sale()
    {
        if(number>0)
        {
            System.out.println(Thread.currentThread().getName()+"买的票号是"+(number--)+"还剩余"+number+"张票");
        }
    }
}

 2,lock锁的实现类

 分别对于 可重入锁,读锁,写锁

了解两个概念

1,公平锁:先来后到,十分公平

2,非公平锁:可以插队,不公平(默认)

3,可重入锁:它允许同一个线程多次获取同一个锁。也就是一个线程获取了锁资源,还可以继续获取同一个锁资源,避免死锁现象发生。

为什么默认非公平锁:假想一个场景有两个线程来了第一个要运行三小时,第二个运行三秒,假设用公平锁那么三秒的线程要等三小时,若用非公平锁,则三小时的只用等三秒,主要减少等待时间。

上代码:先示例化,然后进入Reentrantlock()

Lock lock= new ReentrantLock();
 public ReentrantLock() {
        sync = new NonfairSync();//非公平锁
    }

  
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();//公平锁
    }

这是使用 

 第一步:首先示例化一个Lock的引用lock

第二步:加锁lock.lock();

第三步:解锁lock.lock();

class Ticket
{
    private int number=50;
    Lock lock= new ReentrantLock();

    public  void  sale()
    {
        lock.lock();
        try {
            if(number>0)
            {
                System.out.println(Thread.currentThread().getName()+"买的票号是"+(number--)+"还剩余"+number+"张票");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

3,synchronized和lock锁的区别

第一:synchronized是java内置的关键字,lock是java里面的一个接口

第二:synchronized不可以判断获取锁的状态,而lock可以判断是否获取到了锁()

第三:synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手动释放锁(unlock()方法释放锁),否则容易造成线程死锁;

第四:用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;

第五:synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可)、

第六:Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。

4,生产者和消费者问题

一般面试笔试问题:单例模式,排序算法,生产者消费者,死锁

生产者消费者其实就三步

第一步:判断是否需要等待

第二步:执行业务

第三步:通知

上代码

public class demo02 {
    public static void main(String[] args) {
        Data data =new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.increment();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data.decrement();
            }
        },"B").start();
    }
}
class Data{
    private int number=0;
    public synchronized void increment (){
        if(number!=0)
        {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        notifyAll();
    }
    public synchronized void decrement (){
        if(number==0)
        {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        notifyAll();
    }
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值