首页 > 其他 > 详细

54 JUC并发编程

时间:2021-04-24 16:13:53      阅读:19      评论:0      收藏:0      [点我收藏+]

JUC

java.util 工具包

业务:普通的线程代码 Thread

Runnable:没有返回值,效率相比 Callable 相对较低

1、进程和线程

进程:一个程序,QQ.exe、Music.exe

一个进程往往可以包含多个线程,至少包含一个

java 默认 有两个线程:main线程 gc线程(垃圾回收)

线程:开了一个进程 world ,写字,自动保存(线程负责的)

对于java:Thread、Runnable、Callable

java真的可以开启线程吗? 不能 java无法直接操作硬件

本地方法,底层的c++,java无法直接操作硬件

private native void start()

线程有几个状态?

新生、运行、阻塞、等待、超时等待、终止

wait/sleep 区别

  1. 来自不同的类

    ? wait => Object

    ? sleep => Threat

  2. 关于锁的释放

    wait会释放锁

    sleep 不会释放,睡觉了,抱着不放

  3. 使用的范围是不同的

    wait:要在同步代码块中

    sleep 可以在任意地方睡

  4. 是否需要捕获异常

    wait 不需要捕获异常

    sleep 必须要捕获异常

2、并发 并行

并发:多线程操作同一个资源,交替进行、

  • cpu一核,模拟出来多条线程 快速交替

并行:多个人一起行走,同时进行

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

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

3、Lock锁(重点)

传统 synchronized

/**
 * 真正的多线程开发,公司中的开发,降低低耦合
 * 线程就是一个单独的资源类,没有任何附属的操作
 * **/
public class SaleTicketDemo1 {

    public static void main(String[] args) {
        //并发:多线程操作同一个资源类,把资源丢入线程
        Ticket1 ticket = new Ticket1();

        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();
    }

}

//资源类
class Ticket1{

    private int number = 30;

    public synchronized void saleTicket(){
        if (number > 0){
            System.out.println(Thread.currentThread().getName() + "买了第" + number-- + "票" + "还有" + number + "张票");
        }
    }
}

Lock锁

技术分享图片

公平锁:十分公平;可以先来后到

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

Lock三部曲:

  1. Lock lock = new ReentrantLock();
  2. lock.lock();//加锁
  3. finally => lock.unlock();//解锁
public class SaleTicketDemo2 {

    public static void main(String[] args) {
        Ticket1 ticket1 = new Ticket1();
        //并发:多线程操作同一个资源类,把资源丢入线程

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

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

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


}
//资源类
class Ticket2 {

    private int number = 30;

    Lock lock = new ReentrantLock();


    public  void saleTicket() {

        lock.lock();//加锁
        
        try {
            //业务代码
            if (number > 0) {
                System.out.println(Thread.currentThread().getName() + "买了第" + number-- + "票" + "还有" + number + "张票");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();//解锁
        }

    }
}

Synchronized 和 Lock 区别

  1. Synchronized 是内置的java关键字 Lock是一个java类
  2. Synchronized 无法获取锁的状态,Lock可以判断是否获取到了锁
  3. Synchronized 会自动释放锁,Lock必须要手动释放锁!如果不释放锁 ,死锁
  4. Synchronized 线程1(获得锁,如果阻塞 ) 线程2(等待,一直等) ,Lock锁就不一定会等待下去
  5. Synchronized 可重入锁,不可以中断的,非公平;Lock 可重入锁,可以判断,非公平锁(可设置)
  6. Synchronized 适合锁少量的代码同步问题;Lock 适合锁大量的同步代码

4、消费者和生产者

线程之间的通信问题! 等待唤醒,通知唤醒

线程交替执行 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) {
                    e.printStackTrace();
                }
            }
        },"A").start();

         new Thread(()->{
            for (int i = 0; i <10 ;i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"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);
        //通知其他线程,我+1完毕了
        this.notifyAll();
    }

    public synchronized void decrement() throws InterruptedException {

        if (number == 0){
            //等待
            this.wait();
        }
        number--;
        //通知其他线程,我-1完毕了
        System.out.println(Thread.currentThread().getName() + "=>"+number);

        this.notifyAll();

    }

}

问题存在,A B C D 4个线程! 虚假唤醒

线程也可以唤醒,而不会被通知,中断或超时,即所谓的虚假唤醒。等待应该总是出现在循环中

当一个条件满足时,很多线程都被唤醒了,但是只有其中部分是有用的唤醒,其它的唤醒都是无用的唤醒;

if改为while判断

  1. if判断流水线状态为空时,线程被阻塞,这时if判断就完成了,线程被唤醒后直接执行线程剩余操作
  2. while判断流水线状态为空时,线程被阻塞,这时的while循环没有完成,线程被唤醒后会先进行while判断

理解:两个加法线程A、C来说,比如A先执行,执行时调用了wait方法,此时会释放锁,接着如果线程C获得锁并且也会执行wait方法,两个加线程一起等待被唤醒。此时减线程中的B线程执行完毕并且唤醒了这俩加线程,那么这俩加线程不会一起执行,其中A获取了锁并且加1,执行完毕之后B再执行。如果是if的话,那么A修改完num后,B不会再去判断num的值,直接会给num+1。如果是while的话,A执行完之后,B还会去判断num的值,因此就不会执行。

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) {
                    e.printStackTrace();
                }
            }
        },"A").start();

         new Thread(()->{
            for (int i = 0; i <10 ;i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
         new Thread(()->{
            for (int i = 0; i <10 ;i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();

         new Thread(()->{
            for (int i = 0; i <10 ;i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();

         
    }
}

//判断等待、业务、通知
class Data{

    private int number = 0;

    public synchronized void increment() throws InterruptedException {

        while (number != 0){
            //等待
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName() + "=>"+number);
        //通知其他线程,我+1完毕了
        this.notifyAll();
    }

    public synchronized void decrement() throws InterruptedException {

        while (number == 0){
            //等待
            this.wait();
        }
        number--;
        //通知其他线程,我-1完毕了
        System.out.println(Thread.currentThread().getName() + "=>"+number);

        this.notifyAll();

    }

}

juc版的生产者和消费者问题
技术分享图片

54 JUC并发编程

原文:https://www.cnblogs.com/flypigggg/p/14696916.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!