JUC——并发编程—第一部分

准备工作

搞一个空的Maven项目

JUC——并发编程—第一部分_第1张图片

确保这里lambda这里是8版本.  

JUC——并发编程—第一部分_第2张图片 javac这里也改成是8 JUC——并发编程—第一部分_第3张图片

1.什么是JUC()

JUC全称是Java util Concurrent . java里面的三个包。

 在jdk1.8帮助文档里面可以看见 JUC包下面还扩展了一个atomic原子性和一个locks锁。和一个java.util.function包。

 java.util 工具包

接触的最早的concurrent就是Callable接口,这玩意就是JUC包下的

2.线程和进程

JUC——多线程补充_北岭山脚鼠鼠的博客-CSDN博客

java实际不能真正开启线程。在new Thread的底层实际上是调用的native本地方法来开启线程. 

JUC——并发编程—第一部分_第4张图片

并发编程 : 并发、并行

并发(多线程操作同一个资源 )

  • CPU单核,cpu时间轮转交替执行

并行(多个人一起行走 )

  •  CPU多核,多个线程可以同时执行;线程池
        //获取CPU核数
        //CPU密集型,IO密集型
        System.out.println(Runtime.getRuntime().availableProcessors());

 并发编程本质:充分的利用CPU资源

3.Lock锁

/**
 * 真正的多线程开发,公司中的开发,降低耦合性。
 * 线程就是一个单独的资源类,没有任何附属的作用!
 * 1、属性、方法
 */
public class Test1 {
    public static void main(String[] args) {
        //并发:多线程操作同一个资源类,把资源类丢入线程
        Ticket ticket=new Ticket();

        //@FunctionalInterface 函数式接口,jdk1.8 lambda表达式(参数)->{代码}
        new Thread(()->{for(int i=0;i<60;i++) ticket.sale();},"A").start();
        new Thread(()->{for(int i=0;i<60;i++) ticket.sale();},"B").start();
        new Thread(()->{for(int i=0;i<60;i++) ticket.sale();},"C").start();
    }
}
//Lock锁三步走
//1. new ReentrantLock();
//2. lock.lock();     //加锁
//3. lock.unlock();  //解锁
class Ticket{
    //属性,方法
    private int number=50;

    Lock lock=new ReentrantLock();

    //卖票的方式
    //synchronized 本质: 队列,锁
    public  void sale(){
        lock.lock();     //加锁

        try {
            //业务代码
            if(number>0){
                System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余:"+number);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();  //解锁
        }
    }
}

 Synchronized和lock的区别 

JUC——并发编程—第一部分_第5张图片

4.生产者和消费者问题

面试的: 单例模式、排序算法、生产者和消费者、死锁

传统Synchronized 版(虚假唤醒问题)

/**
 * 线程之间的通信问题:生产者和消费者问题
 * 线程交替执行   A B操作同一个变量 num=0;
 * A num+1
 * B num-1
 */
public class A {
    public static void main(String[] args) {
        Data data=new Data();
        new Thread(()->{
            for(int i=0;i<10;i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"A").start();
        new Thread(()->{
            for(int i=0;i<10;i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"B").start();
    }
}

//等待,业务,通知
class Data{
    private int number=0;

    public synchronized void increment() throws InterruptedException {
        if(number!=0){
            //等待
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        //通知其他线程
        this.notify();
    }

    public synchronized void decrement() throws InterruptedException {
        if(number==0){
            //等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        //通知其他线程
        this.notify();
    }

}

问题存在,A,B,C,D,E多个线程时是否安全。

会出现如下状况,因为唤醒是一次唤醒其它所有线程,可能会有

JUC——并发编程—第一部分_第6张图片

虚假唤醒问题 

把上面资源类中的if判断改为while判断即可 ,这个也叫做自旋.

wait释放锁后,当别人唤醒你,if你就不需要判断,直接往下执行,while被唤醒后还要重新判断

JUC——并发编程—第一部分_第7张图片

JUC版生产者消费者问题        

原本使用的是wait和notify,现在用的Condition对象 ,而不是lock和unlock方法。

JUC——并发编程—第一部分_第8张图片

JUC——并发编程—第一部分_第9张图片

在这里会有个await方法找Condition接口下。

JUC——并发编程—第一部分_第10张图片

JUC——并发编程—第一部分_第11张图片 用法就是先new一把锁,用这个锁来new一个监视对象。

然后用监视对象来等待await,通知signal。

JUC——并发编程—第一部分_第12张图片

 代码实现:

使用lock和unlock代替Synchronized,使用await和signal代替wait和notify

public class B {
    public static void main(String[] args) {
        Data2 data=new Data2();
        new Thread(()->{
            for(int i=0;i<10;i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"A").start();
        new Thread(()->{
            for(int i=0;i<10;i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"B").start();
        new Thread(()->{
            for(int i=0;i<10;i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"C").start();
        new Thread(()->{
            for(int i=0;i<10;i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"D").start();
    }
}

//等待,业务,通知
class Data2{
    private int number=0;
    Lock lock=new ReentrantLock();
    Condition condition = lock.newCondition();

//            condition.await();   等待
//        condition.signalAll();   唤醒全部
    public  void increment() throws InterruptedException {
        lock.lock();
        try {
            while(number!=0){
                //等待
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName()+"=>"+number);
            //通知其他线程
            condition.signalAll();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }
    public  void decrement() throws InterruptedException {
        lock.lock();
        try {
            while(number==0){
                //等待
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName()+"=>"+number);
            //通知其他线程
            condition.signalAll();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }
}

使用condition的优势

精准的通知和唤醒,在Synchronized中不能做到这一点。

定义多个同步监视器Condition,我原本以为是直接去唤醒指定线程,没想到是通过唤醒监视器间接唤醒了线程。

/**
 * A执行完调用B,B执行完调用C
 */
public class C {
    public static void main(String[] args) {
        Data3 data3=new Data3();
        new Thread(()->{
            for(int i=0;i<10;i++)
                data3.printA();
        },"A").start();
        new Thread(()->{
            for(int i=0;i<10;i++)
                data3.printB();
        },"B").start();
        new Thread(()->{
            for(int i=0;i<10;i++)
                data3.printC();
        },"C").start();
    }
}

/**
 * 资源类
 */
class Data3 {
    private Lock lock=new ReentrantLock();
    private Condition condition1=lock.newCondition();
    private Condition condition2=lock.newCondition();
    private Condition condition3=lock.newCondition();

    private int number=1;//1A,2B,3C

    public void printA(){
        lock.lock();
        try {
            //业务,判断-》执行-》通知
            while(number!=1){
                //等待
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName()+"=》BBBBB");
            //唤醒指定的人B
            number=2;
            condition2.signal();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }

    };
    public void printB(){
        lock.lock();
        try {
            //业务,判断-》执行-》通知
            while(number!=2){
                //等待
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName()+"=》CCCCC");
            //唤醒指定的人B
            number=3;
            condition3.signal();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    };
    public void printC(){
        lock.lock();
        try {
            //业务,判断-》执行-》通知
            while(number!=3){
                //等待
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName()+"=》AAAAA");
            //唤醒指定的人B
            number=1;
            condition1.signal();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    };
}

5. 八种锁现象

1.标准情况下,两个线程先打印 发短信还是 打电话?

这里肯定是先发短信

/**
 * 8锁,关于锁的八个问题
 * 1.标准情况下,两个线程先打印 发短信还是 打电话?   1/发短信 2/打电话
 */
public class Test1 {
    public static void main(String[] args) {
        phone phone=new phone();
        new Thread(()->{phone.sendSms();},"A").start();
        //捕获异常
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        new Thread(()->{phone.call();},"B").start();
    }
}

class phone{
    public synchronized void sendSms(){System.out.println("发短信");}
    public synchronized void call(){System.out.println("打电话");}
}

2.sendSms延迟4s,标准情况下,两个线程先打印 发短信还是 打电话?

还是发短信先。

在第一个代码里面可能会觉得是因为主线程sleep了1s所以导致A先执行,但是这种想法是错误的。

下面这个代码里面A线程里面睡了4s也照样是先发短信。

synchronized 锁住的对象是方法的调用者! 两个方法用的是同一把锁,所以谁先拿到谁执行

phone类只有一个,A先B一步拿到了phone的锁,并且sleep时没有放开

/**
 * 8锁,关于锁的八个问题
 * 1.标准情况下,两个线程先打印 发短信还是 打电话?   1/发短信 2/打电话
 * 2.sendSms延迟4s,标准情况下,两个线程先打印 发短信还是 打电话?   1/发短信 2/打电话
 */
public class Test1 {
    public static void main(String[] args) {
        phone phone=new phone();
        new Thread(()->{phone.sendSms();},"A").start();
        //捕获异常
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        new Thread(()->{phone.call();},"B").start();
    }
}

class phone{
    public synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("发短信");
    }
    public synchronized void call(){System.out.println("打电话");}
}

3. 增加了一个普通方法hello,先发短信还是hello

因为hello是普通方法,不是同步方法,不受锁的影响,所以是先输出hello再输出发短信。

/**
 * 3.增加了一个普通方法hello,先发短信还是hello
 */
public class Test2 {
    public static void main(String[] args) {
        phone2 phone=new phone2();
        new Thread(()->{phone.sendSms();},"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        new Thread(()->{phone.hello();},"B").start();
    }
}

class phone2{

    //synchronized 锁住的对象是方法的调用者!
    public synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("发短信");
    }
    public synchronized void call(){System.out.println("打电话");}

    public void hello(){
        System.out.println("hello");
    }
}

4.两个对象,两个同步方法,发短信还是打电话?

 这里创建了两个phone对象执行两个同步方法,最后是先输出打电话。

原因:  

        两个不同的phone对象,有两把锁

/**
 * 3.增加了一个普通方法hello,先发短信还是hello
 * 4.两个对象,两个同步方法,发短信还是打电话?
 */
public class Test2 {
    public static void main(String[] args) {

        //两个对象
        phone2 phone1=new phone2();
        phone2 phone2=new phone2();

        new Thread(()->{phone1.sendSms();},"A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        new Thread(()->{phone2.call();},"B").start();
    }
}

class phone2{

    //synchronized 锁住的对象是方法的调用者!
    public synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("发短信");
    }
    public synchronized void call(){System.out.println("打电话");}

    public void hello(){
        System.out.println("hello");
    }
}

5.增加两个静态的同步方法,只有一个对象,先哪个?

静态方法是类的方法,不管有几个对象,静态方法都只有一个。并且类一加载就有了。

上面4个代码锁的都是new出来的phone对象,但是这里锁的是Class

/**
 * 5.增加两个静态的同步方法,只有一个对象,先哪个?
 */
public class Test3 {
    public static void main(String[] args) {

        //两个对象
        phone3 phone=new phone3();

        new Thread(()->{phone.sendSms();},"A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        new Thread(()->{phone.call();},"B").start();
    }
}

class phone3{
    //synchronized 锁住的对象是方法的调用者!
    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("发短信");
    }
    public static synchronized void call(){System.out.println("打电话");}
}

6.两个对象调用两个静态同步方法,先哪个?

还是先发短信。因为锁住的还是全局唯一的Class类.

/**
 * 5.增加两个静态的同步方法,只有一个对象,先哪个?
 * 6.两个对象调用两个静态同步方法,先哪个?
 */
public class Test3 {
    public static void main(String[] args) {

        //两个对象
        phone3 phone1=new phone3();
        phone3 phone2=new phone3();

        new Thread(()->{phone1.sendSms();},"A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        new Thread(()->{phone2.call();},"B").start();
    }
}

class phone3{
    //synchronized 锁住的对象是方法的调用者!
    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("发短信");
    }
    public static synchronized void call(){System.out.println("打电话");}
}

7.一个普通同步,一个静态同步,一个对象,先哪个?

这里是先打电话。

原因:

        静态那个锁的是phone的class模板,普通的那个锁的是phone对象。有两个锁,所以B不需要等待A释放锁。

/**
 * 7.一个普通同步,一个静态同步,一个对象,先哪个?
 */
public class Test4 {
    public static void main(String[] args) {

        //两个对象
        phone4 phone=new phone4();

        new Thread(()->{phone.sendSms();},"A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        new Thread(()->{phone.call();},"B").start();
    }
}

class phone4{
    //静态同步方法
    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("发短信");
    }

    //普通同步方法
    public  synchronized void call(){System.out.println("打电话");}
}

8.一个普通同步,一个静态同步,两个对象,先哪个?

这个也是先输出打电话。

还是因为两个锁锁的东西不一样。

/**
 * 7.一个普通同步,一个静态同步,一个对象,先哪个?
 * 8.一个普通同步,一个静态同步,两个对象,先哪个?
 */
public class Test4 {
    public static void main(String[] args) {

        //两个对象
        phone4 phone1=new phone4();
        phone4 phone2=new phone4();

        new Thread(()->{phone1.sendSms();},"A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        new Thread(()->{phone2.call();},"B").start();
    }
}

class phone4{
    //静态同步方法
    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("发短信");
    }

    //普通同步方法
    public  synchronized void call(){System.out.println("打电话");}
}

小结

这里只要确定锁的两个东西,一个是this对象,一个是Class模板。

你可能感兴趣的:(JVM+JUC,java,开发语言)