JUC知识点记录

JUC知识点记录,第1张

JUC知识点记录 JUC知识点记录

看完狂神的视频 纯属想记录 回顾 狂神哥直链: 遇见狂神说的个人空间_哔哩哔哩_bilibili 没有不会做的事,只有不想做的事。

1、线程和进程

进程是 *** 作系统中的应用程序、是资源分配的基本单位,线程是用来执行具体的任务和功能,是CPU调度和分派的最小单位
一个进程往往可以包含多个线程,至少包含一个

线程

Java默认有几个线程?2个线程! main线程、GC线程

线程的状态

public enum State {

    	//运行
        NEW,

    	//运行
        RUNNABLE,

    	//阻塞
        BLOCKED,

    	//等待
        WAITING,

    	//超时等待
        TIMED_WAITING,

    	//终止
        TERMINATED;
    }

线程的生命周期

新建、就绪、运行、阻塞、死亡

进程

JAVA真的可以开启线程吗? 开不了的!

java是没有权限去开启线程、 *** 作硬件的,这是一个native的一个本地方法,它可以调用底层的c++代码

    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) {
                
            }
        }
    }
	//这是一个C++底层,Java是没有权限 *** 作底层硬件的
    private native void start0();



并发

多线程 *** 作同一个资源

  • CPU只有一核,模拟出来多个线程,天下武功唯快不破,那么就会造成一个CPU调度速度很快假象同时执行的,实则是交替执行,并发编程的本质就是充分的利用CPU资源
并行

并行:多个人行走

  • CPU多核的情况,多个线程同时执行,我们可以使用线程池!

    获取CPU的核数

    //获取CPU核数
    System.out.println(Runtime.getRuntime().availableProcessors());
    }
    

wait 跟sleep的区别

​ 1、父类不同

​ wait => Object

​ sleep => Thread

一般企业中使用的休眠都是:

package com.neihan.dome;

import java.util.concurrent.TimeUnit;

public class CpuDome {
    public static void main(String[] args) {
        TimeUnit.SECONDS.sleep(1);  // SEConDS 是休眠的单位
    }
}

2、关于锁释放

wait: 会释放锁

sleep:不会释放锁

3、使用的范围是不同的

wait:必须在同步代码块中使用

sleep:可以在任意地方使用

Lock

公平锁:十分公平,必须先来后到,先进入的线程先执行

不公平锁:十分不公平,可以插队,当前面的线程需要执行的时间很长的时候,后面的线程可以插队**(默认不公平锁)**

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-umyUkvZy-1636546509443)(C:UsersNeihanAppDataRoamingTyporatypora-user-imagesimage-20211110143059977.png)]

Synchronized与Lock的区别

1、Synchronized 是java关键字,Lock是类

2、Synchronized 无法获取判断锁的状态,Lock可以判断

3、Synchronized 会自动释放锁,Lock必须手动加锁释放锁

4、Synchronized 线程 A (获得锁->阻塞)线程 B(等待) Lock 就不一定会等下去,Lock有一个trylock去尝试获得锁,不会造成长久的等待。

4、Synchronized 是可重入锁,不可以中断,非公平的,Lock 可重入,可以判断锁,可以自己设置公平锁非公平锁

5、 Synchronized 适合锁少量代码同步问题,Lock适合锁大量的同步代码

生产者消费者的关系

synchronized版本

if 判断有虚假唤醒,处理方法用 while

结论:就是用if判断的话,唤醒后线程会从wait之后的代码开始运行,但是不会重新判断if条件,直接继续运行if代码块之后的代码,而如果使用while的话,也会从wait之后的代码运行,但是唤醒后会重新判断循环条件,如果不成立再执行while代码块之后的代码块,成立的话继续wait。
这也就是为什么用while而不用if的原因了,因为线程被唤醒后,执行开始的地方是wait之后

package com.neihan.pc;
public class ConsumeAndProduct {

    public static void main(String[] args) {
        Data data = new Data();

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

        new Thread(() ->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } 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{

    Integer number = 0;
    
    public synchronized void increment() throws InterruptedException {
        //判断等待
       

        while (0!= number){
            this.wait();
        }

        //操作
        number++;
        System.out.println(Thread.currentThread().getName()+"==>"+number);

        //通知
        this.notifyAll();

    }


    public synchronized void decrement() throws InterruptedException {

        //判断
  
        while (0== number){
            this.wait();
        }

        // *** 作
        number--;
        System.out.println(Thread.currentThread().getName()+"==>"+number);

        //通知
        this.notifyAll();
    }

}

Lock版本

package com.neihan.pc;


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

public class LockCAP {

    public static void main(String[] args) {
        Data2 data = new Data2();

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

        new Thread(() ->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } 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 Data2{

    Integer number = 0;

    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    public  void increment() throws InterruptedException {
        //加锁

        lock.lock();
        try {

            while (0!= number){
                //等待
                condition.await();
            }

            //操作
            number++;
            System.out.println(Thread.currentThread().getName()+"==>"+number);

            //通知
            condition.signalAll();

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


    public  void decrement() throws InterruptedException {

        //加锁
        lock.lock();
        try {

            while (0== number){
                //等待
                condition.await();
            }
            // *** 作
            number--;
            System.out.println(Thread.currentThread().getName()+"==>"+number);
            //通知
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //解锁
            lock.unlock();
        }
    }
}
Condition的优势

指定唤醒线程

精准的通知和唤醒的线程!

如果我们要指定通知的下一个进行顺序怎么办呢? 我们可以使用Condition来指定通知进程

package com.neihan.pc;

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


public class JUCPC {
    public static void main(String[] args) {
        Data3 data3 = new Data3();
        new Thread(()->{

            for (int i = 0; i < 10; i++) {
                try {
                    data3.printA();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        },"A").start();

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

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


class Data3{

    private Lock lock = new ReentrantLock();
    Condition condition1 = lock.newCondition();
    Condition condition2= lock.newCondition();
    Condition condition3= lock.newCondition();

    Integer number = 1;  // 1A 2B 3C

    public void printA() throws InterruptedException {

        //加锁
        lock.lock();

        try {
            while (number != 1){
                //等待
                condition1.await();
            }

            number = 2;

            System.out.println(Thread.currentThread().getName()+"=>AAAAAAAAA");
            //唤醒 B
            condition2.signal();
            
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //解锁
            lock.unlock();
        }
    }

    public void printB() throws InterruptedException {

        //加锁
        lock.lock();

        try {
            while (number != 2){
                //等待
                condition2.await();
            }

            number  =3 ;

            //唤醒 C
            System.out.println(Thread.currentThread().getName()+"=>BBBBBBBBBBBB");
            condition3.signal();

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //解锁
            lock.unlock();
        }
    }

    public void printC() throws InterruptedException {

        //加锁
        lock.lock();

        try {
            while (number != 3){
                //等待
                condition3.await();
            }
            
            number = 1;
            
            //唤醒 A
            System.out.println(Thread.currentThread().getName()+"=>CCCCCCCCCCCCCC");
            condition1.signal();

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //解锁
            lock.unlock();
        }
    }
}
8锁现象

synchronized 锁的是当前的对象

package com.neihan.lock8;

import java.util.concurrent.TimeUnit;


public class Test1 {

    public static void main(String[] args) throws InterruptedException {
        Send send1 = new Send();
        Send send2= new Send();

        new Thread(()->{

            try {
                send1.sendSms();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        },"A").start();


        new Thread(()->{
            send2.sendEmail();
        },"B").start();

    }
}


class Send{

    
    public synchronized void sendSms() throws InterruptedException {

        //增加延迟 1 s
        TimeUnit.SECONDS.sleep(1);

        System.out.println("发送短信");

    }

    public synchronized void sendEmail(){
        System.out.println("发送邮箱");
    }
}

static synchronized(静态同步函数)

静态同步函数锁的是类的 Class

package com.neihan.lock8;

import java.util.concurrent.TimeUnit;


public class Test2 {

    public static void main(String[] args) {
        Send2 send = new Send2();

        new Thread(()->{

            try {
                send.sendSms();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        },"A").start();


        new Thread(()->{
            send.sendEmail();
        },"B").start();
    }


}


class Send2{

    
    public static synchronized void sendSms() throws InterruptedException {

        //增加延迟 1 s
        TimeUnit.SECONDS.sleep(1);

        System.out.println("发送短信");

    }

    
    public  synchronized void sendEmail(){
        System.out.println("发送邮箱");
    }

}

如果我们使用一个静态同步方法、一个同步方法、一个对象调用顺序是什么?

原因:因为一个锁的是Class类的模板,一个锁的是对象的调用者。所以不存在等待,直接运行。

集合不安全 List(不安全)

不安全的 触发 java.util.ConcurrentModificationException 异常 因为底层没有使用 synchronized

解决方案:

1、使用 new Vector<>()
2、Collections.synchronizedList(new ArrayList<>());
3、CopyOnWriteArrayList<>();

CopyonWriteArrayList 比 Vector 好在哪?

Vector 和 CopyonWriteArrayList 的区别
因为 Vector 使用了 synchronized 进行修饰,只要是使用了 synchronized 修饰的效率特别低下。
CopyonWriteArrayList 没有使用 底层是使用的是 Lock锁 写入时复制 效率会更加高效! COW 计算机程序设计领域的一种优化策略

package com.neihan.unsafe;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;


public class TestList {

    public static  void main(String[] args) {


        List list = new CopyOnWriteArrayList<>();

        

        for (int i = 1; i <=10; i++) {

            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            }).start();
        }
    }
}
Map(不安全)

默认加载因子是0.75,默认的初始容量是16

new HashMap<>(16,0.75);

package com.neihan.unsafe;

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


public class TestMap {

    public static void main(String[] args) {

        // 默认等价什么?new HashMap<>(16,0.75);

        //处理方式  Collections.synchronizedMap()
        //处理方式  new ConcurrentHashMap<>();

        Map map = new ConcurrentHashMap<>();

        map.put("A","A");
        map.put("S","B");
        map.put("B","c");
        System.out.println(map);

       

    }
}


Set(不安全)

HashSet底层是什么?

hashSet底层就是一个HashMap;

public HashSet() {
    map = new HashMap<>();
}
package com.neihan.unsafe;

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


public class TestSet {

    public static void main(String[] args) {

        Set set = new  HashSet<>();

        

        for (int i = 1; i <=20; i++) {

            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(set);
            }).start();
        }
    }
}
 
Callable 

1、可以有返回值

2、可以抛出异常

3、方法不同 run() / call()

Callable

泛型就是返回值

启动需要 FutureTask 适配器 使得Callable 与 Runnable 有关联

package com.neihan.callable;

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


public class TestCallable {

    public static void main(String[] args) throws ExecutionException, InterruptedException {


        MyThread myThread = new MyThread();

        //使用 FutureTask 适配器 使得Callable 与 Runnable 有关联
        FutureTask futureTask = new FutureTask(myThread);

        new Thread(futureTask).start();

        //输出返回值  可能出现阻塞 可以把获取返回值放到最后一行,或者异步通信, 不阻塞 
        System.out.println(futureTask.get());

    }

}

class MyThread implements Callable{

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

三大工具类 CountDownLatch

new CountDownLatch(5);

创建的时候 指定初始值

package com.neihan.add;

import java.util.concurrent.CountDownLatch;


public class CountDownLatchDemo {


    public static void main(String[] args) throws InterruptedException {


        CountDownLatch count = new CountDownLatch(5);

        for (int i = 1; i <=5; i++) {

            new Thread(()->{
                //减一
                count.countDown();
                System.out.println(Thread.currentThread().getName());
            },String.valueOf(i)).start();


        }

        //等待归零向下执行
        count.await();

        System.out.println("close");

    }
}
CyclicBarrier
package com.neihan.add;

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


public class TestCyclicBarrier {

    public static void main(String[] args) {
        
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
            System.out.println("集结龙珠完成,召唤神龙");
        });


        for (int i = 1; i <=7; i++) {

            final Integer temp = i;
            new Thread(()->{

                System.out.println("第"+temp+"龙珠");

                try {
                    //等待次数到达
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }

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

        }
    }
}
Semaphore
package com.neihan.add;

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


public class TestSemaphore {

    public static void main(String[] args) {

        

        Semaphore semaphore = new Semaphore(3);

        for (int i = 1; i <=6; i++) {

            new Thread(()->{
                
                try {

                    semaphore.acquire(); //得到
                    System.out.println(Thread.currentThread().getName()+"抢到车位");
                    TimeUnit.SECONDS.sleep(2);  //休眠 2s
                    System.out.println(Thread.currentThread().getName()+"离开到车位");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    //释放
                    semaphore.release();
                }

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

        }

    }
}

原理

semaphore.acquire(); 得到了资源,如果资源已经使用完,就等待资释放后再进行使用

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

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

读写锁

所以如果我们不加锁的情况,多线程的读写会造成数据不可靠的问题。

我们也可以采用synchronized这种重量锁和轻量锁 lock去保证数据的可靠。

但是这次我们采用更细粒度的锁:ReadWriteLock 读写锁来保证

package com.neihan.add;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;


public class TestReadWriteLock {

    public static void main(String[] args) {
        MyCacheLock cache = new MyCacheLock();

        for (int i = 1; i <=10; i++) {
            final Integer temp = i;
            new Thread(()->{

                cache.write(temp+"",temp+"");

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

        for (int i = 1; i <=10; i++) {
            final Integer temp = i;
            new Thread(()->{
                cache.read(temp+"");

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


    }
}

class MyCache{

    private volatile Map map = new HashMap<>();

    public void write(String key,String value){

        System.out.println(Thread.currentThread().getName()+"写入"+key);

        map.put(key,value);
        System.out.println(Thread.currentThread().getName()+"写入完成");

    }

    public void read(String key){
        System.out.println(Thread.currentThread().getName()+"读取"+key);
        map.get(key);
        System.out.println(Thread.currentThread().getName()+"读取完成");
    }

}

class MyCacheLock{

    private volatile Map map = new HashMap<>();
    private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();


    public void write(String key,String value){
        //写锁
        readWriteLock.writeLock().lock();

        try {
            System.out.println(Thread.currentThread().getName()+"写入"+key);
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+"写入完成");

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //释放锁
            readWriteLock.writeLock().unlock();
        }
    }


    public void read(String key){

        //读锁
        readWriteLock.readLock().lock();

        try {
            System.out.println(Thread.currentThread().getName()+"读取"+key);
            map.get(key);
            System.out.println(Thread.currentThread().getName()+"读取完成");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //释放读锁
            readWriteLock.readLock().unlock();
        }
    }
}
阻塞队列 BlockQueue

是 Collection 的一个子类

什么情况下使用阻塞队列

多线程并发、线程池 的情况下使用

BlockQueue 有四组Api

1、抛出异常

2、不抛出异常,有返回值

3、一直阻塞

4、等待 超时阻塞

package com.neihan.queue;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;


public class TestQueue {

    public static void main(String[] args) throws InterruptedException {
        test04();
    }


    
    public static void test01(){

        //队列需要初始化大小
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.add("a"));
        System.out.println(blockingQueue.add("b"));
        System.out.println(blockingQueue.add("c"));
        // java.lang.IllegalStateException: Queue full 异常
        //System.out.println(blockingQueue.add("c"));


        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        //java.util.NoSuchElementException 异常
        //System.out.println(blockingQueue.remove());


    }


    
    public static void test02(){

        //队列需要初始化大小
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);

        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.offer("b"));
        System.out.println(blockingQueue.offer("c"));
        System.out.println(blockingQueue.offer("a"));


        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());


    }

    
    public static void test03() throws InterruptedException {

        //队列需要初始化大小
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);

        //一直阻塞不会返回
        blockingQueue.put("a");
        blockingQueue.put("b");
        blockingQueue.put("c");

        //如果队列满了的话此时put不进去值,咋此处一直阻塞,程序不会停止
        //blockingQueue.put("d");

        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        //如果队列满了的话此时put不进去值,咋此处一直阻塞,程序不会停止
        System.out.println(blockingQueue.take());
    }

    
    public static void test04() throws InterruptedException {

        //队列需要初始化大小
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.offer("b"));
        System.out.println(blockingQueue.offer("c"));

        //等待 两秒 如果队列还是满的话就不再等待
        System.out.println(blockingQueue.offer("d",2, TimeUnit.SECONDS));


        System.out.println(blockingQueue.poll(2,TimeUnit.SECONDS));
        System.out.println(blockingQueue.poll(2,TimeUnit.SECONDS));
        System.out.println(blockingQueue.poll(2,TimeUnit.SECONDS));
        //取值,两秒没取到就不再等待
        System.out.println(blockingQueue.poll(2,TimeUnit.SECONDS));
    }
}

同步队列

同步队列 没有 容量 也可以视为 容量为一的队列;

进去一个元素必须等进去的元素出来其他的元素才能进入

put方法 和 take方法;

SynchronousQueue 的take是使用了lock锁保证线程安全的。

package com.neihan.queue;

import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;


public class TestSynchronousQueue {

    public static void main(String[] args) {

        SynchronousQueue synchronousQueue = new SynchronousQueue<>();

        new Thread(()->{

            try {
                System.out.println(Thread.currentThread().getName()+" put 1");
                synchronousQueue.put("1");
                System.out.println(Thread.currentThread().getName()+" put 2");
                synchronousQueue.put("2");
                System.out.println(Thread.currentThread().getName()+" put 3");
                synchronousQueue.put("3");

            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }).start();

        new Thread(()->{

            try {
                TimeUnit.SECONDS.sleep(3);
                System.out.println(synchronousQueue.take()+"take()");
                TimeUnit.SECONDS.sleep(3);
                System.out.println(synchronousQueue.take()+"take()");
                TimeUnit.SECONDS.sleep(3);
                System.out.println(synchronousQueue.take()+"take()");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }).start();

    }
}
线程池

三大方式,七大参数,四种拒绝策略

池化技术

程序的运行,本质:占用系统的资源!我们需要去优化资源的使用 ===> 池化技术

线程池、JDBC的连接池、内存池、对象池 等等。。。。

资源的创建、销毁十分消耗资源

池化技术:事先准备好一些资源,如果有人要用,就来我这里拿,用完之后还给我,以此来提高效率。

线程池的好处

1、降低资源的消耗

2、提高响应速度

3、方便管理

实现线程复用、可以控制最大并发数、管理线程

package com.neihan.pool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class Dome01 {

    public static void main(String[] args) {
        //ExecutorService threadPool = Executors.newSingleThreadExecutor();  //单个线程
        //ExecutorService threadPool = Executors.newFixedThreadPool(5);  //固定多少个线程 或者说最大多少个线程
        ExecutorService threadPool = Executors.newCachedThreadPool();  //遇强则强,遇弱则弱


        try {
            for (int i = 0; i < 100; i++) {

                //使用线程池之后,使用线程池来创建线程
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+ " ok");
                });

            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //使用完线程池一定要关闭
            threadPool.shutdown();
        }
    }
}

线程池的三大方法
  • ExecutorService threadPool = Executors.newSingleThreadExecutor(); 单个线程
  • ExecutorService threadPool = Executors.newFixedThreadPool(5); //固定多少个线程 或者说最大多少个线程
  • ExecutorService threadPool = Executors.newCachedThreadPool(); //遇强则强,遇弱则弱
线程池的七大参数

1、核心线程

2、最大线程

3、阻塞队列

4、线程工厂

5、超时数

6、超时单位

7、拒绝策略

 
拒绝策略 

1. new ThreadPoolExecutor.AbortPolicy(): //该拒绝策略为:银行满了,还有人进来,不处理这个人的,并抛出异常

超出最大承载,就会抛出异常:队列容量大小+maxPoolSize

2. new ThreadPoolExecutor.CallerRunsPolicy(): //该拒绝策略为:哪来的去哪里 main线程进行处理

3. new ThreadPoolExecutor.DiscardPolicy(): //该拒绝策略为:队列满了,丢掉异常,不会抛出异常。

4. new ThreadPoolExecutor.DiscardOldestPolicy(): //该拒绝策略为:队列满了,尝试去和最早的进程竞争,不会抛出异常

如何设置线程池的大小

1、CPU密集型:电脑的核数是几核就选择几;选择maximunPoolSize的大小

I/O密集型:

在程序中有15个大型任务,io十分占用资源;I/O密集型就是判断我们程序中十分耗I/O的线程数量,大约是最大I/O数的一倍到两倍之间。

四大函数式接口

新时代的程序员:lambda表达式、链式编程、函数式接口、Stream流式计算

Function 函数型接口

函数式接口 只有一个方法的接口 并带有 @FunctionalInterface

package com.neihan.function;

import java.util.function.Function;


public class Demo01 {

    public static void main(String[] args) {

       

        // lambda 表达式
        Function function = (str)->{
            return str;
        };

        System.out.println(function.apply("neihan niu bi lambda"));

    }

}

断定型接口Predicate

断定型接口 只能传入一个参数,返回值只能是Boolean

package com.neihan.function;

import java.util.function.Predicate;


public class Dome02 {

    public static void main(String[] args) {

     

        Predicate predicate = (str)->{ return str.isEmpty();};

        System.out.println(predicate.test(""));
    }
}

Supplier供给型接口

没有参数 只有返回值

package com.neihan.function;

import java.util.function.Supplier;


public class Demo03 {

    public static void main(String[] args) {
     

        Supplier supplier = ()->{return "Neihan Niu bi ";};
        System.out.println(supplier.get());
    }

}

消费性接口Consumer

消费性接口 只有参数没有返回值

package com.neihan.function;

import java.util.function.Consumer;


public class Demo04 {
    public static void main(String[] args) {
        Consumer stringConsumer = new Consumer(){
            @Override
            public void accept(String o) {
                System.out.println(o);
            }
        };


        stringConsumer.accept("neihan ");
    }

}

stream流试计算
package com.neihan.function;

import java.util.Arrays;
import java.util.List;


public class testStream {

    public static void main(String[] args) {

        Student student1 = new Student(16,"a",1);
        Student student2 = new Student(26,"b",2);
        Student student3 = new Student(25,"c",6);
        Student student4 = new Student(23,"d",4);

        List students = Arrays.asList(student1, student2, student3, student4);
                //链式编程  stream流式计算
                students.stream()
                .filter(student ->{return student.getId() % 2 ==0;})
                .filter(student -> {return student.getAge() > 23;} )
                .map(student -> {return student.getName().toUpperCase();})  //转换大写
                .sorted((s1,s2)->{return s2.compareTo(s1);})   //排序 倒叙输出
                .limit(1)   // 分页,显示的页数
                .forEach(System.out::printf);
    }
}

package com.neihan.function;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;


public class StreamHome01 {

    public static void main(String[] args) {

        List integers = Arrays.asList(2,3,4,6,60,7);
        String collect = integers
                .stream()
                .map(Object::toString)
                .sorted((s1, s2) -> {
                    return s2.compareTo(s1);
                })

                .filter((s) -> {
                    return Integer.parseInt(s) % 2 == 0;
                })
                .collect(Collectors.joining(""));

        String sz [] = new String[collect.length()];

        for (int i = 0; i < collect.length(); i++) {
            sz[i] = collect.substring(i,i+1);
        }

        Arrays.stream(sz)
                .sorted((i1,i2)->{return i2.compareTo(i1);})
                .forEach(System.out::printf);
    }
}
ForkJoin

ForkJoin 在JDK1.7,并行执行任务!提高效率~。在大数据量速率会更快!

大数据中:MapReduce 核心思想->把大任务拆分为小任务!

如何使用ForkJoin?
  • 1、通过ForkJoinPool来执行 - 2、计算任务 execute(ForkJoinTask task) - 3、计算类要去继承ForkJoinTask; ForkJoin 的计算类
package com.neihan.forkjoin;

import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;


@Data

@NoArgsConstructor
public class ForkJoinDemo extends RecursiveTask {

    private Long start;
    private Long end;
    private Long temp =10000L;  //临界值

    public ForkJoinDemo(Long start, Long end) {
        this.start = start;
        this.end = end;
    }



    @Override
    protected Long compute() {

        if((end - start ) < temp){
            Long sum  = 0L;
            for (Long i = start; i < end; i++) {
                sum+= i;
            }
            return sum;

        }else{
            //使用Forkjoin计算
            long middle = (start + end) / 2;
            ForkJoinDemo forkJoin1 = new ForkJoinDemo(start,middle);
            //拆分任务 把任务压入队列
            forkJoin1.fork();
            ForkJoinDemo forkJoin2 = new ForkJoinDemo(middle+1,end);
            forkJoin2.fork();

            return forkJoin1.join() + forkJoin2.join();
        }
    }
}

测试类

package com.neihan.forkjoin;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;


public class TestForkjoin {

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        //test01(); 2928
        test02();  //4043
        //test03(); //198
    }

    private static void test01(){
        long start  = System.currentTimeMillis();
        long sum = 0L;

        for (Long i = 1L; i <= 10_0000_0000L; i++) {

            sum+= i;

        }
        Long end = System.currentTimeMillis();

        System.out.println("sum= "+sum+"时间:"+(end-start));

    }

    public static void test02() throws ExecutionException, InterruptedException {

        Long start = System.currentTimeMillis();

        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask task = new ForkJoinDemo(0L, 10_0000_0000L);

        ForkJoinTask submit = forkJoinPool.submit(task);//提交任务

        Long sum = submit.get();

        Long end = System.currentTimeMillis();

        System.out.println("sum="+sum+"时间:"+(end-start)/2);

    }

    
    public static void test03(){
        Long start = System.currentTimeMillis();

        Long sum = LongStream.range(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);

        Long end  = System.currentTimeMillis();
        System.out.println("sum="+sum+"时间:"+(end-start));

    }
}
异步回调

Future 设计的初衷:对将来的某个事件结果进行建模!

package com.neihan.future;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;


public class TestFuture {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        test02();

    }


    public void test01() throws ExecutionException, InterruptedException {
        CompletableFuture future = CompletableFuture.runAsync(() -> {

            //发起一个异步任务
            try {
                TimeUnit.SECONDS.sleep(2);

                System.out.println("这是一个异步的消息");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName());

        });
        System.out.println("Main线程");
        System.out.println("future.get() = " + future.get());
    }


    
    public static void test02() throws ExecutionException, InterruptedException {

        CompletableFuture future = CompletableFuture.supplyAsync(() -> {

            //发起一个异步任务
            try {
                TimeUnit.SECONDS.sleep(2);

                System.out.println("这是一个异步的消息");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            int i = 10 /0;

            System.out.println(Thread.currentThread().getName());

            return 1024;   // 成功的返回值
        });


        System.out.println(future.whenComplete((t, u) -> {

            System.out.println(t);    // t 是正常返回的结果
            System.out.println(u);    // u 是错误的信息

        }).exceptionally((e -> {
            System.out.println("e.getMessage() = " + e.getMessage());
            return 500;   //返回error 的信息
        })).get());

    }
}

但是我们平时都使用CompletableFuture

JMM 什么是JMM?

JMM:JAVA内存模型,不存在的东西,是一个概念,也是一个约定!

关于JMM的一些同步的约定:

1、线程解锁前,必须把共享变量立刻刷回主存;

2、线程加锁前,必须读取主存中的最新值到工作内存中;

3、加锁和解锁是同一把锁;

线程中分为 工作内存、主内存

8种 *** 作:

  • Read(读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用; - load(载入):作用于工作内存的变量,它把read *** 作从主存中变量放入工作内存中;

  • Use(使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令;

  • assign(赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中; -

  • store(存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用; - write(写入):作用于主内存中的变量,它把store *** 作从工作内存中得到的变量的值放入主内存的变量中; -

  • lock(锁定):作用于主内存的变量,把一个变量标识为线程独占状态;

  • unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定;

  • JMM对这8种 *** 作给了相应的规定*:
  • - 不允许read和load、store和write *** 作之一单独出现。即使用了read必须load,使用了store必须write- 不允许线程丢弃他最近的assign *** 作,即工作变量的数据改变了之后,必须告知主存- 不允许一个线程将没有assign的数据从工作内存同步回主内存- 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是对变量实施use、store *** 作之前,必须经过assign和load *** 作- 一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解锁- 如果对一个变量进行lock *** 作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新load或assign *** 作初始化变量的值- 如果一个变量没有被lock,就不能对其进行unlock *** 作。也不能unlock一个被其他线程锁住的变量- 对一个变量进行unlock *** 作之前,必须把此变量同步回主内存

遇到问题:程序不知道主存中的值已经被修改过了!

对Volatile 的理解

Volatile是 Java 提供的 轻量级 的同步机制

1、保证可见性

2、不保证原子性

3、禁止指令重排机制

保证可见性

volatile 没加这个关键字的话主存发生了改变但是Thread()线程没收到通知,还是读取到的是没修改过的数据 一直死循环

package com.neihan.jmm;

import java.util.concurrent.TimeUnit;


public class JMMDemo01 {

    private static  volatile int sum =1;    //volatile 没加这个关键字的话主存发生了改变但是Thread()线程没收到通知,还是读取到的是没修改过的数据

    public static void main(String[] args) throws InterruptedException {
        new Thread(()->{

            while (sum==1){

            }

        }).start();

        TimeUnit.SECONDS.sleep(1);

        sum=0;

        System.out.println("main");
    }
}

不保证原子性

如果不加lock和synchronized ,怎么样保证原子性?

使用原子类

这些类的底层都直接和 *** 作系统挂钩!是在内存中修改值。

package com.neihan.jmm;

import java.util.concurrent.atomic.AtomicInteger;


public class VDemo {

    private static  volatile  AtomicInteger count = new AtomicInteger();  
    
    public static void main(String[] args) {


        for (int i = 0; i < 20; i++) {

            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }).start();
        }

        while (Thread.activeCount() > 2 ){
            Thread.yield();
        }

        System.out.println(Thread.currentThread().getName()+""+"count="+count);
    }


    public static void add(){
      

        count.incrementAndGet();
    }

}

禁止指令重排

什么是指令重排?

我们写的程序,计算机并不是按照我们自己写的那样去执行的

源代码–>编译器优化重排–>指令并行也可能会重排–>内存系统也会重排–>执行

int x=1; //1
int y=2; //2
x=x+5;   //3
y=x*x;   //4

//我们期望的执行顺序是 1_2_3_4  可能执行的顺序会变成2134 1324
//可不可能是 4123? 不可能的
1234567

可能造成的影响结果:前提:a b x y这四个值 默认都是0

|线程A|线程B
|------
|x=a|y=b
|b=1|a=2

正常的结果: x = 0; y =0;

|线程A|线程B
|------
|b=1|a=2
|x=a|y=b

可能在线程A中会出现,先执行b=1,然后再执行x=a;

在B线程中可能会出现,先执行a=2,然后执行y=b;

那么就有可能结果如下:x=2; y=1.

volatile可以避免指令重排:

volatile中会加一道内存的屏障,这个内存屏障可以保证在这个屏障中的指令顺序。

内存屏障:CPU指令。作用:

1、保证特定的 *** 作的执行顺序;

2、可以保证某些变量的内存可见性(利用这些特性,就可以保证volatile实现的可见性)

总结
  • volatile可以保证可见性;- 不能保证原子性- 由于内存屏障,可以保证避免指令重排的现象产生
    面试官:那么你知道在哪里用这个内存屏障用得最多呢?单例模式
玩转单例模式

饿汉式、DCL懒汉式

饿汉模式

单例模式私有构造

饿汉顾名思义 很饿 什么东西都先创建好

package com.neihan.jmm;


public class Hungry {

    //可能会浪费空间
    private byte[] data1 = new byte[1024*1024];
    private byte[] data2 = new byte[1024*1024];
    private byte[] data3 = new byte[1024*1024];

    private Hungry(){

    }

    private final static Hungry hungry = new Hungry();

    private static Hungry getInstance(){

        return hungry;
        
    }
}
DCL懒汉式
package com.neihan.jmm;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;


public class LazyMan {


    private static boolean neihan = false;   //红绿灯

    private LazyMan(){
        synchronized (LazyMan.class){

            if(neihan == false){
                neihan=true;
            }else{
                throw new RuntimeException("小伙子不要试图使用反射来搞破坏!!!");
            }

        }

        System.out.println(Thread.currentThread().getName()+"ok");
    }

    private volatile static LazyMan lazyMan;

    private static LazyMan getInstance(){
        if (lazyMan ==null){


        synchronized (LazyMan.class){

            if(lazyMan==null){   //单线程下是OK 的  但是多线程是不行的  需要加锁  加锁后java中有反射!!!!
                lazyMan = new LazyMan();
            }
            }
        }
        return lazyMan;
    }

    public static void main(String[] args) throws Exception {

     


        //反射
        //LazyMan instance1 = LazyMan.getInstance();

        Field neihan = LazyMan.class.getDeclaredField("neihan");



        Constructor declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);   //这个时候无视了私有的构造器,使用反射创建对象
        LazyMan instance2 = declaredConstructor.newInstance();

        neihan.setAccessible(true);
        neihan.set(instance2,false);   //把字段的值改变又可以了
        LazyMan instance1 = declaredConstructor.newInstance();
        System.out.println(instance1);
        System.out.println(instance2);
    }
}
静态内部类
package com.neihan.jmm;


public class Holder {


    private Holder(){

    }

    public static Holder getInstance(){
        return InnerClass.holder;
    }

    public static class InnerClass{
        private static final Holder holder =  new Holder();
    }
}

单例不安全, 因为反射

枚举

package com.neihan.jmm;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;


public enum EnumSingle {

    INSTANCE;

    public EnumSingle getInstance(){
        return INSTANCE;
    }

}

class Test{

    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        EnumSingle instance1 = EnumSingle.INSTANCE;

        Constructor declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
        declaredConstructor.setAccessible(true);

        EnumSingle enumSingle = declaredConstructor.newInstance();
        System.out.println(instance1);

        System.out.println(enumSingle);

    }
}

使用枚举,我们就可以防止反射破坏了。

枚举类型的最终反编译源码:

public final class EnumSingle extends Enum
{
    public static EnumSingle[] values()
    {
        return (EnumSingle[])$VALUES.clone();
    }

    public static EnumSingle valueOf(String name)
    {
        return (EnumSingle)Enum.valueOf(com/ogj/single/EnumSingle, name);
    }

    private EnumSingle(String s, int i)
    {
        super(s, i);
    }

    public EnumSingle getInstance()
    {
        return INSTANCE;
    }

    public static final EnumSingle INSTANCE;
    private static final EnumSingle $VALUES[];

    static 
    {
        INSTANCE = new EnumSingle("INSTANCE", 0);
        $VALUES = (new EnumSingle[] {
            INSTANCE
        });
    }
}
深入理解CAS 什么是CAS?

大厂必须深入研究底层!!!!修内功! *** 作系统、计算机网络原理、组成原理、数据结构

意思就是比较并交换

package com.neihan.cas;

import java.util.concurrent.atomic.AtomicInteger;


public class CASDemo {

    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(1024);

        //public final boolean compareAndSet(int expect, int update)
        
        System.out.println(atomicInteger.compareAndSet(1024, 241));
        System.out.println(atomicInteger.get());

    }
}

Unsafe 类

总结

CAS:比较当前工作内存中的值 和 主内存中的值,如果这个值是期望的,那么则执行 *** 作!如果不是就一直循环,使用的是自旋锁。

缺点:

  • 循环会耗时;- 一次性只能保证一个共享变量的原子性;- 它会存在ABA问题

CAS : ABA 问题 (狸猫换太子)

线程1:期望值是1,要变成2;

线程2:两个 *** 作:

  • 1、期望值是1,变成3- 2、期望是3,变成1
    所以对于线程1来说,A的值还是1,所以就出现了问题,骗过了线程1;
public class casDemo {
    //CAS : compareAndSet 比较并交换
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2020);

        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());

        //boolean compareAndSet(int expect, int update)
        //期望值、更新值
        //如果实际值 和 我的期望值相同,那么就更新
        //如果实际值 和 我的期望值不同,那么就不更新
        System.out.println(atomicInteger.compareAndSet(2021, 2020));
        System.out.println(atomicInteger.get());

        //因为期望值是2020  实际值却变成了2021  所以会修改失败
        //CAS 是CPU的并发原语
		//atomicInteger.getAndIncrement(); //++ *** 作
        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());
    }
}

原子引用

解决ABA问题,对应的思想:就是使用了乐观锁~

带版本号的 原子 *** 作!

使用包装类会有对象引用的问题

Integer 使用了对象缓存机制,默认范围是-128~127,推荐使用静态工厂方法valueOf获取对象实例,而不是new,因为valueOf使用缓存,而new一定会创建新的对象分配新的内存空间。

带版本号的原子 *** 作

package com.neihan.cas;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;


public class ABADemo {


    public static void main(String[] args) {

        //使用包装类会有对象引用的问题
        //Integer 使用了对象缓存机制,默认范围是-128~127,推荐使用静态工厂方法valueOf获取对象实例,而不是new,因为valueOf使用缓存,而new一定会创建新的对象分配新的内存空间。
        AtomicStampedReference atomicStampedReference = new AtomicStampedReference(1,1);

        // CAS 比较交换
        new Thread(()->{
            int stamp = atomicStampedReference.getStamp();     //获得版本号
            System.out.println(Thread.currentThread().getName()+"Stamp=>"+stamp);

            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            
            System.out.println(atomicStampedReference.compareAndSet(
                    1,
                    2,
                    atomicStampedReference.getStamp(),
                    atomicStampedReference.getStamp() + 1));

            System.out.println(Thread.currentThread().getName()+atomicStampedReference.getStamp());

            //修改完之后再修改回来,另一条线程并不知情,看看能不能修改成功
            System.out.println(atomicStampedReference.compareAndSet(
                    2,
                    1,
                    atomicStampedReference.getStamp(),
                    atomicStampedReference.getStamp() + 1));


            System.out.println(Thread.currentThread().getName()+atomicStampedReference.getStamp());


        },"A").start();


        new Thread(()->{

            int stamp = atomicStampedReference.getStamp();

            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName()+"Stamp"+stamp);

            System.out.println(Thread.currentThread().getName()+" "+atomicStampedReference.compareAndSet(1, 6, stamp, atomicStampedReference.getStamp() + 1));

            System.out.println(Thread.currentThread().getName()+"Stamp"+atomicStampedReference.getStamp());

        },"B").start();
    }
}
各种锁的理解 公平锁,非公平锁

1、公平锁:非常公平,不能插队,必须先来后到

public ReentrantLock() {
    sync = new NonfairSync();
}

2、非公平锁:非常不公平,允许插队,可以改变顺序

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

可重入锁

1、Synchonized 锁

synchronized 版本的可重入锁 自动获得内部锁

package com.neihan.lock;


public class Demo01 {

    public static void main(String[] args) {

        Phone phone = new Phone();

        new Thread(()->{
            phone.sms();

        },"A").start();

        new Thread(()->{
            phone.sms();

        },"B").start();

    }
}


class Phone{


    public synchronized void sms(){

        System.out.println(Thread.currentThread().getName()+"==>sms");
        call();   //这里也是一把锁
    }


    public synchronized void call(){

        System.out.println(Thread.currentThread().getName()+"==>call");
    }

}

Lock版本

Lock 版本的重入锁 Lock 锁一定是配对出现,不然就会出现死锁

package com.neihan.lock;

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


public class Demo02 {
    public static void main(String[] args) {
        Phone2 phone2 = new Phone2();

        new Thread(()->{
            phone2.sms();

        },"A").start();

        new Thread(()->{
            phone2.sms();

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


}
class Phone2{


    Lock lock = new ReentrantLock();
    public  void sms(){

        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName()+"==>sms");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        call();   //这里也是一把锁
    }


    public  void call(){

        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName()+"==>call");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}
自旋锁

1、spinlock

public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
    return var5;
}

2、自定义自旋锁

基于CAS 实现

package com.neihan.lock;

import java.util.concurrent.atomic.AtomicReference;


public class SpinlockDemo {

    AtomicReference atomicReference = new AtomicReference();
    //加锁
    public void myLock(){

        Thread thread = Thread.currentThread();

        System.out.println(thread.getName()+" myLock ");


        //自旋锁
        while (!atomicReference.compareAndSet(null,thread)){

        }

    }

    public void myUnlock(){

        Thread thread = Thread.currentThread();
        System.out.println(thread.getName()+"===>myUnlock");

        atomicReference.compareAndSet(thread,null);

    }
}

测试类

package com.neihan.lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;


public class TestSpinLock {
    public static void main(String[] args) {

        SpinlockDemo spinlockDemo = new SpinlockDemo();

        new Thread(()->{

            try {
                spinlockDemo.myLock();
                TimeUnit.SECONDS.sleep(10);

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                spinlockDemo.myUnlock();
            }


        },"A").start();


        new Thread(()->{


            try {
                spinlockDemo.myLock();

                TimeUnit.SECONDS.sleep(2);

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                spinlockDemo.myUnlock();
            }


        },"B").start();

    }
}
死锁
package com.neihan.lock;

import java.util.concurrent.TimeUnit;


public class Deadlock {

    public static void main(String[] args) {
        String lockA = "lockA";
        String lockB = "lockB";

        new Thread(new MyThread(lockA,lockB),"T1").start();
        new Thread(new MyThread(lockB,lockA),"T2").start();

    }


}


class MyThread implements Runnable{

    private String lockA;
    private String lockB;


    public MyThread(String lockA, String lockB) {
        this.lockA = lockA;
        this.lockB = lockB;
    }

    @Override
    public void run() {
        synchronized (lockA){
            System.out.println(Thread.currentThread().getName()+"lock:"+lockA+"=>get "+lockB);

            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            synchronized (lockB){
                System.out.println(Thread.currentThread().getName()+" lock"+lockB+"===> get"+lockA);
            }
        }

    }
}

如何解开死锁

命令:jps -l

1、使用jps定位进程号,jdk的bin目录下: 有一个jps

2、使用jstack 进程进程号 找到死锁信息

一般情况信息在最后:

while (!atomicReference.compareAndSet(null,thread)){

    }

}

public void myUnlock(){

    Thread thread = Thread.currentThread();
    System.out.println(thread.getName()+"===>myUnlock");

    atomicReference.compareAndSet(thread,null);

}

}

**测试类**

```java
package com.neihan.lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;


public class TestSpinLock {
    public static void main(String[] args) {

        SpinlockDemo spinlockDemo = new SpinlockDemo();

        new Thread(()->{

            try {
                spinlockDemo.myLock();
                TimeUnit.SECONDS.sleep(10);

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                spinlockDemo.myUnlock();
            }


        },"A").start();


        new Thread(()->{


            try {
                spinlockDemo.myLock();

                TimeUnit.SECONDS.sleep(2);

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                spinlockDemo.myUnlock();
            }


        },"B").start();

    }
}
死锁
package com.neihan.lock;

import java.util.concurrent.TimeUnit;


public class Deadlock {

    public static void main(String[] args) {
        String lockA = "lockA";
        String lockB = "lockB";

        new Thread(new MyThread(lockA,lockB),"T1").start();
        new Thread(new MyThread(lockB,lockA),"T2").start();

    }


}


class MyThread implements Runnable{

    private String lockA;
    private String lockB;


    public MyThread(String lockA, String lockB) {
        this.lockA = lockA;
        this.lockB = lockB;
    }

    @Override
    public void run() {
        synchronized (lockA){
            System.out.println(Thread.currentThread().getName()+"lock:"+lockA+"=>get "+lockB);

            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            synchronized (lockB){
                System.out.println(Thread.currentThread().getName()+" lock"+lockB+"===> get"+lockA);
            }
        }

    }
}

如何解开死锁

命令:jps -l

1、使用jps定位进程号,jdk的bin目录下: 有一个jps

2、使用jstack 进程进程号 找到死锁信息

一般情况信息在最后:

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5437590.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-11
下一篇 2022-12-11

发表评论

登录后才能评论

评论列表(0条)