JUC多线程

JUC多线程

什么是JUC:源码+官方文档,面试高频问!

Runnable 、callable

1.线程和进程

进程:一个程序,QQ.exe等程序的集合,一个进程可以包含多个线程,至少包含一个!

java默认两个线程:2个,main、GC

线程:Thread,Runnable、Callable

2.回顾多线程

java是否可以开启线程? 答:不可以!
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) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }
	//本地方法,底层的C++,java无法直接操作硬件
    private native void start0();

并发:多线程操作同一资源(CPU单核情况下,快速交替)

并行:CPU多核,多个线程可以同时执行,线程池

package com.lsh;

public class demo01 {
    public static void main(String[] args) {
       // new Thread().start();
        //获取CPU的核数
        //cpu密集型,IO密集型
        System.out.println(Runtime.getRuntime().availableProcessors());
    }
}

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

 public enum State {
        //新生
        NEW,

        //运行
        RUNNABLE,

        //阻塞
        BLOCKED,
		//等待,死死地等
        
        WAITING,

        //超时等带
        TIMED_WAITING,

        //终止
        TERMINATED;
    }
wait/sleep区别

1.来自不同的类

wait来自于Object

sleep来自于Thread

2.关于锁的释放

wait会释放锁,sleep抱着锁睡觉,不释放锁

3.使用的范围不同

wait必须在同步代码块中

sleep可以在任何地方睡

3.传统的Synchronized锁

package com.lsh;

public class SaleTicketDemo01 {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }

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

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

        },"C").start();
    }



}
//资源类OOP
class Ticket{
    //属性、方法
    private int number = 40;
    //买票的方式
    public  synchronized  void sale(){
        if(number>0){
            System.out.println(Thread.currentThread().getName()+":卖出了第"+(number--)+"张票,剩余"+(number));
        }
    }
}

4.Lock锁

两个方法:加锁(lock)和解锁(unlock)

在这里插入图片描述
在这里插入图片描述

公平锁:可以先来后到

非公平锁:可以插队

package com.lsh;


import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SaleTicketDemo02 {
    public static void main(String[] args) {
        Ticket2 ticket = new Ticket2();
        new Thread(()->{for (int i = 0; i < 40; i++) ticket.sale();},"A").start();
        new Thread(()->{for (int i = 0; i < 40; i++) ticket.sale();},"B").start();
        new Thread(()->{for (int i = 0; i < 40; i++) ticket.sale();},"C").start();
    }


}
//资源类OOP
//lock三部曲:
//1.new ReentrantLock();
//2.lock方法
//3.unClock方法
class Ticket2{
    //属性、方法
    private int number = 40;
    Lock lock = new ReentrantLock();

    //买票的方式
    public  synchronized  void sale(){
        lock.lock();

        try {



            if(number>0){
                System.out.println(Thread.currentThread().getName()+":卖出了第"+(number--)+"张票,剩余"+(number));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

5.Synchronized锁和Lock锁区别

1.synchronized 内置的java关键字,Lock是java类

2.synchronized 无法判断获取锁的状态,Lock可以判断是否获取到了锁

3.synchronized会自动释放锁,lock必须要手动释放锁,如果不释放锁,死锁

4.synchronized 线程个(获得 锁,阻塞)、线程2(等待、傻傻的等);Lock锁就不一定会等待下去;

5.sunchronized 可重入锁,不可以终端,非公平;

Lock 可重入锁,可以判断锁,非公平(可以自己设置)

6.synchronized 适合锁少量的代码同步问题,Lock适合锁大量的同步代码

6.传统的消费者和生产者问题、防止虚假唤醒

package com.lsh.pc;

/**
 * 线程之间的通信问题,生产者和消费者问题
 * 线程交替执行   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);
        this.notifyAll();
    }
    public synchronized void decrement() throws InterruptedException{
        if(number==0){
            //等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"=========>"+number);
        this.notifyAll();
    }
}

问题存在:多个线程时

在这里插入图片描述

虚假唤醒解决方式:将if改为while判断即可

7.Lock版生产者和消费者问题

在这里插入图片描述

通过Lock找到Condition
在这里插入图片描述

Lock版消费者生产者模式

package com.lsh.pc;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

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

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

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

        },"D").start();
    }
}

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

    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) {
            e.printStackTrace();
        } 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) {
            e.printStackTrace();
        } finally {
        }
    }
}

8.Condition实现精准通知唤醒

package com.lsh.pc;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

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{//资源类,lock锁
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
    private int number = 1;
    public void printA(){
        lock.lock();
        try {
            //业务-->判断-->执行-->通知
            while(number!=1){
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName()+"=====>"+"AAAAAAAAAA");
            number = 2;
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
    public void printB(){
        lock.lock();
        try {
            while(number!=2){
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName()+"=====>"+"BBBBBBBBBB");
            number = 3;
            condition3.signal();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
    public void printC(){
        lock.lock();
        try {
            while(number!=3){
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName()+"=====>"+"CCCCCCCCC");
            number = 1;
            condition1.signal();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}

9.八锁现象彻底理解锁

对象和class

第一组

package com.lsh.pc.lock8;

import java.util.concurrent.TimeUnit;

/**
 * 8锁:锁的八个问题
 * 1.标准情况下,两个线程是先打印   打电话还是发短信   //1.发短信 2.打电话
 * 2.发短信方法延迟4s,两个线程先打印   打电话还是发短信 //1.发短信 2.打电话
 *
 */
public class Test01 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(()->{
            phone.sendMes();
        },"A").start();

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

    //锁的对象是方法的调用者
    //两个方法用到的是同一把锁,谁先拿到谁执行
    public synchronized void sendMes(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

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

第二组

package com.lsh.pc.lock8;

import java.util.concurrent.TimeUnit;

/**
 * 8锁:锁的八个问题
 * 3.增加一个普通方法,hello()//先执行普通方法
 * 4.测试两个对象,先调用谁                    //先打电话,后发短信(睡了4s),锁的不同对象,相互之间不影响
 */
public class Test02 {
    public static void main(String[] args) {
        //测试两个对象,先调用谁
        Phone2 phone1 = new Phone2();
        Phone2 phone2 = new Phone2();
        new Thread(()->{
            phone1.sendMes();
        },"A").start();

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

}
class Phone2{

    //锁的对象是方法的调用者
    //两个方法用到的是同一把锁,谁先拿到谁执行
    public synchronized void sendMes(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("发短信");
    }
    public synchronized void call(){
        System.out.println("打电话");
    }
    //没有锁,普通方法
    public  void hello(){
        System.out.println("hello");
    }
}

第三组

package com.lsh.pc.lock8;

import java.util.concurrent.TimeUnit;

/**
 * 8锁:锁的八个问题
 * 5.静态方法     //先打印发短信,再打印打电话
 * 6.两个对象     //还是跟一个对象时一样,锁的是类(模板)
 */
public class Test03 {
    public static void main(String[] args) {
        //测试两个对象,先调用谁
        Phone3 phone1 = new Phone3();
        Phone3 phone2 = new Phone3();
        new Thread(()->{
            phone1.sendMes();
        },"A").start();

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

}
class Phone3{

    //锁的对象是方法的调用者
    //两个方法用到的是同一把锁,谁先拿到谁执行
    //static   类一加载就锁上了,锁模板
    public static synchronized void sendMes(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

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

}

第四组

package com.lsh.pc.lock8;

import java.util.concurrent.TimeUnit;

/**
 * 8锁:锁的八个问题
 * 7.1个静态的同步方法,1个普通的同步方法,  同一个对象?        //先打印打电话,再打印发短信
 * 8.2个静态的同步方法,1个普通的同步方法,不同的对象?         //先打印打电话,再打印发短信
 */
public class Test04 {
    public static void main(String[] args) {
        //测试两个对象,先调用谁
        Phone4 phone1 = new Phone4();
        Phone4 phone2 = new Phone4();
        new Thread(()->{
            phone1.sendMes();
        },"A").start();

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

}
class Phone4{

    //锁的对象是方法的调用者
    //两个方法用到的是同一把锁,谁先拿到谁执行
    //static   类一加载就锁上了,锁模板
    public static synchronized void sendMes(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

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

}
总结:分辨出具体锁的东西,是类模板还是具体的对象
普通方法:锁对象,方法调用者
static修饰方法:锁类模板

10.CopyOnWriteArrayList

package com.lsh.pc.unsafe;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;

//java.util.ConcurrentModificationException   并发修改异常
public class ListTest {
    public static void main(String[] args) {
        //并发下ArrayList不安全的
        /**
         * 解决方案:
         * 1.List<String> list = new Vector<>();
         * 2.List<String> list = Collections.synchronizedList(new ArrayList<>());
         * 3.List<String> list = new CopyOnWriteArrayList<>();
         *
         *
         */
        //CopyOnWrite  cow 写入时复制,计算机程序设计领域的一种优化策略
        //在写入的时候避免覆盖,造成数据问题
        //读写分离
        //copyOnWriteArrayList比Vector牛逼在哪里?
        /**
         * vector使用synchronized方法,导致性能差
         *  copyOnWriteArrayList使用Lock锁,性能好
         *  copyOnWriteArrayList源码
         *  public boolean add(E e) {
         *     final ReentrantLock lock = this.lock;
         *     lock.lock();
         *     try {
         *         Object[] elements = getArray();
         *         int len = elements.length;
         *         Object[] newElements = Arrays.copyOf(elements, len + 1);
         *         newElements[len] = e;
         *         setArray(newElements);
         *         return true;
         *     } finally {
         *         lock.unlock();
         *     }
         *  }
         *
         */
        List<String> list = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

11.CopyOnWriteArrayMap

1.CopyOnWriteArraySet

package com.lsh.pc.unsafe;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * 同理可证:java.util.ConcurrentModificationException
 */
public class SetTest {
    public static void main(String[] args) {
        //Set<String> set = new HashSet<>();
        //1.解决并发不安全问题
        //Set<String> set = Collections.synchronizedSet(new HashSet<>());
        //2.并发包下的类
        Set<String> set = new CopyOnWriteArraySet<>();

        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                set.add(UUID.randomUUID().toString().substring(0, 5));
                System.out.println(set);
            }, String.valueOf(i)).start();
        }
    }
}

HaseSet底层原理:

public HashSet() {
        map = new HashMap<>();
}
//add set 本质就是map  key是无法重复的(map的key和value可以为空,key为null只有一种情况)
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}
private static final Object PRESENT = new Object();

**HashMap **

package com.lsh.pc.unsafe;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

public class MapTest {
    public static void main(String[] args) {
        //new HashMap<>(16,0.75)
        //java.util.ConcurrentModificationException
        //1.new ConcurrentHashMap<>()
        Map<String,String> map = new ConcurrentHashMap<>();
        //Map<String,String> map = new HashMap<>();
        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0, 5));
                System.out.println(map);
            },String.valueOf(i)).start();
        }

    }
}

12.走进Callable

在这里插入图片描述

1.可以有返回值

2.可以抛出异常

3.方法不同,run()/call()

package com.lsh.pc.callable;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class CallableTest {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //new Thread(new Runnable()).start();
        //new Thread(new FutureTask<>()).start();
        //new Thread(new FutureTask<>(Callable)).start();

       // new Thread().start();
        //适配类 FutureTask
        MyThread myThread = new MyThread();
        FutureTask futureTask = new FutureTask(myThread);
        new Thread(futureTask,"A").start();

        Integer a = (Integer)futureTask.get();
        System.out.println(a);

    }

}

class MyThread implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        System.out.println("call()");
        return 1024;
    }
}

常用的辅助类(13,14,15)

13.CountDownLatch

package com.lsh.add;

import java.util.concurrent.CountDownLatch;
//计数器
public class CountDownLatchDemo {

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(6);

        //总数是6
        for (int i = 1; i <= 6; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"Go Out");
                countDownLatch.countDown();//数量-1
            },String.valueOf(i)).start();
        }
        countDownLatch.await();//等待计数器为0,再向下执行

        System.out.println("Close Door");

    }

}

countDawnLatch.countDown();countDownLatch.countDown();//数量-1

countDownLatch.await();//等待计数器为0,再向下执行

每次有线程调用countDown()数量-1,假设计数器变为0,countDownLatch.await()就会被唤醒,继续执行!

14.CyclicBarrier

package com.lsh.add;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierDemo {


    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
            System.out.println("召唤神龙成功!");
        });
        for (int i = 1; i <= 7; i++) {
            int temp = i;
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"收集第"+temp+"龙珠");
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {


                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }


}

15.Semaphore

package com.lsh.add;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class SemaphoreTest {
    private final static int THREAD_COUNT = 6;
    //private static ExecutorService threadPool= Executors.newFixedThreadPool(THREAD_COUNT);
    private static Semaphore s = new Semaphore(3);
    public static void main(String[] args) {
        for (int i = 1; i < THREAD_COUNT; i++) {
            new Thread(()->{
                try {
                    s.acquire();
                    System.out.println(Thread.currentThread().getName()+"抢到车位");
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println((Thread.currentThread().getName() + "离开车位"));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    s.release();//释放
                }

            },String.valueOf(i)).start();
        }
    }
}

原理:

semaphore.acquire()获得,假设满了,等待,等待被释放为止;

semaphore.release()释放,会将当前的信号量+1, 然后唤醒等待的线程

作用:多个共享资源的互斥,并发限流,控制最大的线程数

未完待续~持续更新,持续学习

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值