JUC基础知识点(一)

1、JUC概述

1.1 什么是JUC

在Java中,线程部分是一个重点,本篇文章说的JUC是关于线程的。JUC就是java.util.concurrent工具包的简称。这是一个处理线程的工具包,JDK1.5开始出现的

1.2 线程和进程的概念

  1. 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。程序是指令、数据及其组织形式的描述,进程是程序的实体。

  2. 线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

  3. 总结:

    1. 进程:指在系统中正在运行的一个应用程序;程序一旦运行就是进程;进程——资源分配的最小单位

    2. 线程:系统分配处理器时间资源的基本单元,或者说进程内独立执行的一个单元执行流。线程——程序执行的最小单位

1.3 线程的状态

线程状态枚举类(Thread.State): NEW(新建)、RUNNABLE(准备就绪)、BLOCKED(阻塞)、WAITING(不见不散)、TIMED_WAITING(过时不候)、TERMINATED(终结)

wait和sleep的区别

  1. sleep是Thread的静态方法,wait是Object的方法,任何对象实例都能调用

  2. sleep不会释放锁,它也不需要占用锁。wait会释放锁,但调用它的前提是当前线程占有锁(即代码要在synchronized中)

  3. 它们都可以被interrupted方法中断

并发与并行

  1. 并发:多个线程在访问同一个资源(线程频繁切换),多个线程对一个点

    1. 例子:春运抢票、电商秒杀,一个医生给多个病人轮流看病

  2. 并行:多项工作一起执行(同时),之后再汇总

    1. 多个医生同时给病人看病

管程: Monitor监视器 就是我们所说的锁 是一种同步机制,保证同一个时间,只有一个线程访问被保护数据或者代码。JVM同步基于进入和退出,使用管程对象实现的。

用户线程和守护线程

  1. 用户线程:自定义线程 主线程结束了,用户线程还在运行,jvm存活

  2. 守护线程:比如垃圾回收 没有用户线程了,都是守护线程,jvm结束

public class Main {
    public static void main(String[] args){
        Thread aa = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "::" + Thread.currentThread().isDaemon());
            while(true){
                
            }
        }, "aa");
        //设置守护线程(注意要在start()方法之前设置)
        aa.setDaemon(true);
        aa.start();
        System.out.println(Thread.currentThread().getName() + "over");
    }
}
//第一次输出结果 main over aa::false  表示是用户线程
//设置守护线程之后 main over

2、Lock接口

2.1 复习Synchronized

synchronized 是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:

  1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象

  2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法作用的对象是调用这个方法的对象

  3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象

  4. 修改一个,其作用的范围是synchronized后面括号括起来的部分,作用主要的对象是这个类的所有对象

2.2 什么是Lock接口

Lock实现提供了比使用synchronized方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的Condition对象

所有已知实现类:ReentrantLock、ReentrantReadWriteLock.ReadLock、ReentrantReadWriteLock.WriteLock

Lock与Synchronized区别:

  1. Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现

  2. synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock()时需要在finally块中释放锁

  3. Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断

  4. 通过Lock可以知道有没有成功获取锁,而synchronized却无法办到

  5. Lock可以提高多个线程进行操作的效率

  6. Lock和synchronized有一点非常大的不同,采用synchronized不需要用户去手动释放锁,当synchronized方法或synchronized代码块执行完之后,系统会自动让线程释放对锁的占用;

  7. Lock则必须要用户去手动释放锁,如果没有手动释放锁,就有可能出现死锁现象

2.3 创建线程的多种方式

  1. 继承Thread类创建线程

  2. 实现Runnable接口创建线程

  3. 使用Callable和Future创建线程

  4. 使用线程池例如用Executor框架

多线程编程步骤:

  1. 第一步:创建资源类,在资源类创建属性和操作方法(例子:空调)

  2. 第二步:创建多个线程,调用资源类的操作方法

2.4 使用Lock实现卖票例子

//第一步 创建资源类,定义属性和操作方法
class Ticket{
    //票数
    private int number = 30;
    //操作方法:卖票
    public synchronized void sale(){
        //判断:是否有票
        if(number > 0){
            System.out.println(Thread.currentThread().getName()+":卖出:" +(number--)+"剩下:"+number);
        }
    }
}
public class SaleTicket {
    //第二步 创建多个线程,调用资源类的操作方法
    public static void main(String[] args){
        //创建Ticket对象
        Ticket ticket = new Ticket();
        //创建三个线程
        new Thread(new Runnable(){
            @Override
            public void run(){
                //调用卖票方法
                for(int i = 0; i < 40; i++){
                    ticket.sale();
                }
            }
        },"AA").start();
        new Thread(new Runnable(){
            @Override
            public void run(){
                //调用卖票方法
                for(int i = 0; i < 40; i++){
                    ticket.sale();
                }
            }
        },"BB").start();
        new Thread(new Runnable(){
            @Override
            public void run(){
                //调用卖票方法
                for(int i = 0; i < 40; i++){
                    ticket.sale();
                }
            }
        },"CC").start();
    }
}
//第一步 创建资源类,定义属性和操作方法
class LTicket{
    //票数量
    private int number = 30;
    //创建可重入锁
    private final ReentrantLock lock = new ReentrantLock();
    //卖票方法
    public void sale(){
        //上锁
        lock.lock();
        try{
        //判断是否有票
        if(num > 0){
            System.out.println(Thread.currentThread().getName()+":卖出:" +(number--)+"剩余:"+number);
        }
        }finally{
            //解锁
        lock.unlock();
        }       
    }
}
​
public class LSaleTicket(){
    //第二步 创建多个线程,调用资源类的操作方法
    //创建三个线程
    public static void main(String[] args){
        LTicket ticket = new LTicket();
        
        new Thread(() -> {
            for(int i = 0; i < 40; i++){
                ticket.sale();
            }
        }, "AA").start();
        new Thread(() -> {
            for(int i = 0; i < 40; i++){
                ticket.sale();
            }
        }, "BB").start();
        new Thread(() -> {
            for(int i = 0; i < 40; i++){
                ticket.sale();
            }
        }, "CC").start();
    }
}

3、线程间通信

//线程间通信 —— synchronized实现
//第一步 创建资源类,定义属性和操作方法
class Share{
    //初始值
    private int number = 0;
    //+1的方法
    public synchronized void incr() throws InterruptedException{
        //第二步 判断 干活 通知
        while(number != 0){//判断num是否是0,如果不是,等待
            this.wait();//在哪里睡,就会在哪里醒
            //前面判断如果用if就会有虚假唤醒问题
        }
        //如果number值是0,就+1操作
        number++;
        System.out.println(Thread.currentThread().getName()+"::"+number);
        //通知其他线程
        this.notifyAll();
    }
    //-1的方法
    public synchronized void decr(){
        while(number != 1){//判断
            this.wait();
        }
        number--;//干活
         System.out.println(Thread.currentThread().getName()+"::"+number);
        //通知其他线程
        this.notifyAll();
    }
}
//第三步 创建多个线程,调用资源类的操作方法
public class ThreadDemo1{
    public static void main(String[] args){
        Share share = new Share();
        //创建线程
        new Thread(() -> {
            for(int i = 1; i <= 10; i++){
                try{
                    share.incr();//调用+1方法
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
            }
        }, "AA").start();
        new Thread(() -> {
            for(int i = 1; i <= 10; i++){
                try{
                    share.decr();//调用-1方法
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
            }
        }, "BB").start();
        new Thread(() -> {
            for(int i = 1; i <= 10; i++){
                try{
                    share.incr();//调用+1方法
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
            }
        }, "CC").start();
        new Thread(() -> {
            for(int i = 1; i <= 10; i++){
                try{
                    share.decr();//调用-1方法
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
            }
        }, "DD").start();
    }
}
//线程间通信 —— lock实现
//第一步 创建资源类,定义属性和操作方法
class Share {
    private int number = 0;
    //创建Lock
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    
    //+1
    public void incr() throws InterruptedException{
        //上锁
        lock.lock();
        try{
            //判断
            while(num != 0){
                condition.await();
            }
            number++;//干活
            System.out.println(Thread.currentThread().getName()+"::"+number);
            //通知
            condition.signalAll();
        }finally{
            //解锁
            lock.unlock();
        }
    }
    //-1
    public void decr(){
        lock.lock();
        try{
            while(number != 1){
                aondition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName()+"::"+number);
            condition.signalAll();
        }finally{
            lock.unlock();
        }
    }
}
public class ThreadDenm2{
   public static void main(String[] args){
       Share share = new Share();
       new Thread(()->{
           for(int i = 1; i < 10; i++){
               try{
                   share.incr();
               }catch(InterruptedException e){
                   e.printStackTrace();
               }
           }
       }, "AA").start();
       new Thread(()->{
           for(int i = 1; i < 10; i++){
               try{
                   share.decr();
               }catch(InterruptedException e){
                   e.printStackTrace();
               }
           }
       }, "BB").start();
       new Thread(()->{
           for(int i = 1; i < 10; i++){
               try{
                   share.incr();
               }catch(InterruptedException e){
                   e.printStackTrace();
               }
           }
       }, "CC").start();
       new Thread(()->{
           for(int i = 1; i < 10; i++){
               try{
                   share.decr();
               }catch(InterruptedException e){
                   e.printStackTrace();
               }
           }
       }, "DD").start();
   } 
}

4、线程间定制化通信

//线程间定制化通信 —— 通过标志位
//第一步 创建资源类
class ShareResource{
    //定义标志位
    private int flag = 1;//1 AA  2 BB  3 CC
    //创建Lock锁
    private lock lock = new ReentrantLock();
    
    //创建三个condition
    private Condition c1 = lock.newCondition();
    private Condition c2 = lock.newCondition();
    private Condition c3 = lock.newCondition();
    
    //打印5次,参数第几轮
    public void print5(int loop){
        //上锁
        lock.lock();
        try{
            //判断
            while(flag != 1){
                //等待
                c1.await();
            }
            for(int i = 1; i <= 5; i++){//干活
                System.out.println(Thread.currentThread().getName()+"::"+" : 轮数 :"+loop)
            }
            flag = 2;//修改标志位2
            c2.signal();//通知
        }finally{
            //释放锁
            lock.unlock();
        }
    }
    //打印10次,参数第几轮
    public void print15(int loop){
        //上锁
        lock.lock();
        try{
            //判断
            while(flag != 2){
                //等待
                c2.await();
            }
            for(int i = 1; i <= 5; i++){//干活
                System.out.println(Thread.currentThread().getName()+"::"+" : 轮数 :"+loop)
            }
            flag = 3;//修改标志位2
            c2.signal();//通知
        }finally{
            //释放锁
            lock.unlock();
        }
    }
    //打印15次,参数第几轮
    public void print15(int loop){
        //上锁
        lock.lock();
        try{
            //判断
            while(flag != 3){
                //等待
                c3.await();
            }
            for(int i = 1; i <= 5; i++){//干活
                System.out.println(Thread.currentThread().getName()+"::"+" : 轮数 :"+loop)
            }
            flag = 1;//修改标志位2
            c3.signal();//通知
        }finally{
            //释放锁
            lock.unlock();
        }
    }
}
public class ThreadDemo3{
    ShareResource shareResource = new ShareResource();
    new Thread(() -> {
       for(int i = 1; i <= 10; i++){
           try{
               shareRource.print5(i);
           } catch (InterruptedException e){
               e.printStackTrace();
           }
       }
    },"AA").start();
    new Thread(() -> {
       for(int i = 1; i <= 10; i++){
           try{
               shareRource.print10(i);
           } catch (InterruptedException e){
               e.printStackTrace();
           }
       }
    },"BB").start();
    new Thread(() -> {
       for(int i = 1; i <= 10; i++){
           try{
               shareRource.print15(i);
           } catch (InterruptedException e){
               e.printStackTrace();
           }
       }
    },"CC").start();
}

5、集合的线程安全

5.1 集合线程不安全演示

//list集合线程不安全
public class ThreadDemo4{
    public static void main(String[] args){
        //创建ArrayList集合
        List<String> list = new ArrayList<>();
        for(int i = 1; i <= 30; i++){
            new Thread(() -> {
                //向集合添加内容
                list.add(UUID.randomUUID().toString().substring(0, 8));
                //从集合中获取内容
                System.out.print(list);
            }, String.valueOf(i)).start();
        }
    }
}

解决方案-Vector

将List<String> list = new ArrayList<>();

改成
List<String> list = new Vector();即可   (很古老的方案——不常用)

解决方案-Collections

List<String> list = Collections.synchronizedList(new ArrayList());

解决方案-CopyOnWriteArrayList ——常用

List<String> list = new CopyOnWriteArrayList<>();

写时复制技术,读的时候支持并发读,写是独立写,写之前先复制内容,在新的地址写入新的内容之后合并,后面再读取新的内容 —— 例如签到

首先将当前容器复制一份,然后在新副本上执行写操作,结束之后再将原容器的引用指向新容器

5.2 HashSet线程不安全

public class ThreadDemo4{
    public static void main(String[] args){
        //创建HashSet集合
        //Set<String> set = new HashSet<>();
        //解决方案
        Set<String> set = new CopyOnWriteArraySet<>();
        for(int i = 1; i <= 30; i++){
            new Thread(() -> {
                //向集合添加内容
                set.add(UUID.randomUUID().toString().substring(0, 8));
                //从集合中获取内容
                System.out.print(set);
            }, String.valueOf(i)).start();
        }
    }
}

5.3 HashMap线程不安全

public class ThreadDemo4{
    public static void main(String[] args){
        //Map<String, String> map = new HashMap<>();
        Map<String, String> map = new ConcurrentHashMap<>();
        for(int i = 1; i <= 30; i++){
            new Thread(() -> {
                //向集合添加内容
                map.put(key,UUID.randomUUID().toString().substring(0, 8));
                //从集合中获取内容
                System.out.print(map);
            }, String.valueOf(i)).start();
        }
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值