文章目录

  • 1.实现线程的几种方式
    • 1.1.继承Thread类
    • 1.2.实现Runnable接口
    • 1.3.实现Callable接口
  • 2.Lambda表达式
  • 3.静态代理模式
  • 4.synchronized锁
  • 5.死锁
  • 6.Lock锁
  • 7.synchronized与Lock区别
  • 8.线程池
  • 9.线程状态
  • 10. wait和sleep的区别
  • 11.生产者与消费者问题
  • 12.什么是JUC
  • 13.JUC版的生产者与消费者问题
  • 14.8锁问题
  • 15.集合的安全问题
    • 15.1.List不安全
    • 15.2.Set不安全
    • 15.3.map不安全
  • 16.Callable
  • 17.常用的辅助类
    • 17.1.CountDownLatch
    • 17.2.CyclicBarrier
    • 17.3.Semaphore
  • 18.读写锁
  • 19.阻塞队列
  • 20.线程池
    • 20.1.三大方法
    • 20.2.七大参数
      • 20.2.1.CPU密集型和IO密集型
    • 20.3.四种拒绝策略
  • 21.四大函数式接口
  • 22.Stream流
  • 23.JMM
  • 24.Volatile
  • 25.CAS
  • 26.原子引用
  • 27.各种锁
    • 27.1.公平锁,非公平锁
    • 27.2.可重入锁
    • 27.3.自旋锁

1.实现线程的几种方式

1.1.继承Thread类

  • 子类继承Thread类具备多线程能力
  • 启动线程:子类对象.start()
  • 不建议使用:避免OOP单继承局限性
public class Test1 extends Thread{@Overridepublic void run() {for (int i = 0; i <20 ; i++) {System.out.println("线程"+i);}}public static void main(String[] args) {Test1 test1 =new Test1();test1.start();for (int i = 0; i <20 ; i++) {System.out.println("主线程"+i);}}
}

1.2.实现Runnable接口

  • 实现接口Runnable具有多线程能力
  • 启动线程:传入目标对象+Thread对象.start()
  • 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
public class Test2 implements Runnable{@Overridepublic void run() {for (int i = 0; i <20 ; i++) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"---"+i);}}public static void main(String[] args) {Test2 test2 = new Test2();new Thread(test2,"线程1").start();new Thread(test2,"线程2").start();}
}

1.3.实现Callable接口

1.实现Callable接口

2.重写call方法,需要返回值类型

3.创建目标对象

4.创建执行任务:ExecutorService executorService = Executors.newFixedThreadPool(3);

5.提交执行:Future result1 = ser.submit(1)

6.获取结果:boolean r1= result1.get()

7.关闭服务:ser.shutdownNow()

public class Test3 implements Callable<Boolean> {@Overridepublic Boolean call() throws Exception {for (int i = 0; i <20 ; i++) {System.out.println(Thread.currentThread().getName()+"-----"+i);}return true;}public static void main(String[] args) throws ExecutionException, InterruptedException {Test3 t1 = new Test3();Test3 t2 = new Test3();Test3 t3 = new Test3();//创建执行任务:ExecutorService executorService = Executors.newFixedThreadPool(3);//提交执行Future<Boolean> submit1 = executorService.submit(t1);Future<Boolean> submit2 = executorService.submit(t2);Future<Boolean> submit3 = executorService.submit(t3);// 获取结果boolean r1= submit1.get();boolean r2= submit2.get();boolean r3= submit3.get();//关闭服务:executorService.shutdownNow();}
}

2.Lambda表达式

public class Test4 {public static void main(String[] args) {Lambda lambda ;//lambda表达式实现接口lambda = (a,b) -> {System.out.println((a+b));};lambda.print(1,1);}
}
//函数式接口
interface Lambda{void print(int a,int b);
}

总结:

  • lambda表达式只有一行代码的情况下才能简化称为一行,如果有多行,那么就用代码块包裹。
  • 使用lambda表达式的前提必须是函数式接口(只有一个方法)
  • 多个参数也可以去掉参数类型,要去掉就都去掉,必须加上括号

3.静态代理模式

  • 真实对象和代理对象都要实现同一个接口
  • 代理对象要代理真实角色

好处:

  • 代理对象可以做很多真实对象做不了的事情
  • 真实对象专注做自己的事情

静态代理模式,婚庆公司举例:

public class Test5 {public static void main(String[] args) {new WeddingCompany(new You()).happyMarry();}}
interface Marry{void happyMarry();
}
//角色,你去结婚
class You implements Marry{@Overridepublic void happyMarry() {System.out.println("你在结婚中");}
}
//代理角色,婚庆公司,帮助你结婚
class WeddingCompany implements Marry{private Marry target;public WeddingCompany(Marry target) {this.target = target;}@Overridepublic void happyMarry() {before();target.happyMarry();after();}private void after() {System.out.println("结婚后收钱");}private void before() {System.out.println("结婚前准备");}}

线程的实现原理就是基于静态代理模式。

new WeddingCompany(new You()).happyMarry();new Thread(()-> System.out.println("我爱你"));

Thread也实现了Runnable接口。

Thread就是一个代理对象,代理中间的真实对象Runnable接口。

4.synchronized锁

synchronized方法:锁的是当前类的class。

public class Test6 {public static void main(String[] args) {BuyTickets b1= new BuyTickets();new Thread(b1,"张三").start();new Thread(b1,"李四").start();new Thread(b1,"赵五").start();}
}
class BuyTickets implements  Runnable{private int ticketsNum = 10;private boolean flag = true;//外部停止方式@Overridepublic void run() {while (flag){try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}buy();}}private synchronized void buy(){if (ticketsNum<=0){flag=false;return;}System.out.println(Thread.currentThread().getName()+"拿到了"+ticketsNum--);}
}

synchronized代码块:synchronized(锁的对象){

​ 要加锁的内容

}

private  void buy(){synchronized (ticketsNum) {if (ticketsNum <= 0) {flag = false;return;}System.out.println(Thread.currentThread().getName() + "拿到了" + ticketsNum--);}}

锁的对象是进行增删改的对象。

5.死锁

当某个同步块同时拥有“两个以上对象的锁时”,就可能会放生死锁问题

两个对象互相占着对方的资源,产生僵持。

产生死锁的四个必要条件

  1. 互斥条件:一个资源每次只能被一个进程使用
  2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
  3. 不剥夺条件:进程已获得的资源,在未使用完前,不能强行剥夺
  4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

6.Lock锁

  • 从JDK5.0开始, java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用Lock充当。
  • java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象
  • ReentrantLock类实现了Lock,他拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock可重用锁,可以显式加锁,释放锁。
    //new一个锁对象ReentrantLock lock = new ReentrantLock();private  void buy(){//加锁try {lock.lock();if (ticketsNum <= 0) {flag = false;return;}System.out.println(Thread.currentThread().getName() + "拿到了" + ticketsNum--);}finally {//释放锁lock.unlock();}}

7.synchronized与Lock区别

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

8.线程池

  • JDK5.0起提供了线程池相关API:ExecutorService和Excutors
  • ExecutorService:真正的线程池接口。常见子类ThreadPoolExcutor
    • void execute(Runnable command) :执行任务/命令,没有返回值,一般用来执行Runnable
    • Future submit(Callable task) :执行任务,有返回值,一般用来执行Callable
  • Excutors:工具类,线程池的工厂类,用于创建并返回不同类型的线程池

9.线程状态

    public enum State {/*** 新生*/NEW,/**运行*/RUNNABLE,/**阻塞*/BLOCKED,/*等待*/WAITING,/**超时等待*/TIMED_WAITING,/**死亡*/TERMINATED;}

10. wait和sleep的区别

都是线程休眠

1.来自不同的类

wait是Object类中的

sleep是Thread类中的

2.关于锁的释放

wait会释放锁

sleep不会释放锁

3.使用范围不同

sleep在任何地方都可以使用

wait只有在同步代码块中可以使用

4.是否需捕获异常

wait不需要捕获异常

sleep需要捕获异常

11.生产者与消费者问题

/*
生产者消费者问题*/
public class Test7 {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.decrement();} catch (InterruptedException e) {e.printStackTrace();}}},"C").start();new Thread(()->{for (int i = 0; i <10 ; i++) {try {data.increment();} catch (InterruptedException e) {e.printStackTrace();}}},"D").start();}
}
class Data{private static Integer num = 0;public synchronized void increment() throws InterruptedException {//注意这里要用while来判断,不能用if,if只判断一次会引起虚假唤醒问题while (num!=0){//等待this.wait();}//唤醒其他线程this.notifyAll();num++;System.out.println(Thread.currentThread().getName()+"=>"+num);}public synchronized void decrement() throws InterruptedException {while (num==0){this.wait();}this.notifyAll();num--;System.out.println(Thread.currentThread().getName()+"=>"+num);}}

线程也可以唤醒,而不会被通知,中断或超时,即所谓的虚假唤醒。

等待应该发生在循环中,不应用if判断。

12.什么是JUC

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O4X3h0YS-1602919849079)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201015102153149.png)]

juc就是,java.util.cocurrent 包

13.JUC版的生产者与消费者问题

Lock替换synchronized方法和语句的使用, Condition取代了对象监视器方法的使用。 await,signal。

一个Condition实例本质上绑定到一个锁。 要获得特定Condition实例的Condition实例,请使用其newCondition()方法。

class Data{private static Integer num = 0;//创建锁对象ReentrantLock lock = new ReentrantLock();//创建Condition对象,用它其中的方法await 和 signal 来代替wait 和notifyCondition condition = lock.newCondition();public  void increment() throws InterruptedException {try{//上锁lock.lock();//注意这里要用while来判断,不能用if,if只判断一次会引起虚假唤醒问题while (num!=0){//等待condition.await();}num++;condition.signal();System.out.println(Thread.currentThread().getName()+"=>"+num);}finally {//释放锁lock.unlock();}}public  void decrement() throws InterruptedException {try {lock.lock();while (num==0){condition.await();}num--;condition.signal();System.out.println(Thread.currentThread().getName()+"=>"+num);}finally {lock.unlock();}}}

对比wait和notify,condition可以精准通知和唤醒线程

/*** 顺序通知线程执行,condition精准唤醒线程* ABCD*/
public class Test2 {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();new Thread(()->{for (int i = 0; i <10 ; i++) {data3.printD();}},"D").start();}
}
class Data3{private ReentrantLock lock = new ReentrantLock();Condition condition1 = lock.newCondition();Condition condition2 = lock.newCondition();Condition condition3 = lock.newCondition();Condition condition4 = lock.newCondition();private int num = 1;public void printA(){lock.lock();try {while (num!=1){condition1.await();}System.out.println(Thread.currentThread().getName()+"->"+num);num = 2;//唤醒B线程condition2.signal();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}public void printB(){lock.lock();try {while (num!=2){condition2.await();}System.out.println(Thread.currentThread().getName()+"->"+num);num = 3;//唤醒C线程condition3.signal();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}public void printC(){lock.lock();try {while (num!=3){condition3.await();}System.out.println(Thread.currentThread().getName()+"->"+num);num = 4;//唤醒Ccondition4.signal();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}public void printD(){lock.lock();try {while (num!=4){condition4.await();}System.out.println(Thread.currentThread().getName()+"->"+num);num = 1;//唤醒Acondition1.signal();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}
}
A->1
B->2
C->3
D->4
A->1
B->2
C->3
D->4
...

14.8锁问题

关于锁的8个问题

例子

public class Test3 {public static void main(String[] args) {Phone phone = new Phone();new Thread(()->{phone.sendSms();}).start();new Thread(()->{phone.call();}).start();}
}
class Phone{public synchronized void sendSms(){System.out.println("发短信");}public synchronized void call(){System.out.println("打电话");}
}

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

先打印发短信,两个线程共用phone对象同一个锁,先调用发短信则打电话必须等发短信执行完之后才能执行。

2.sedSms延迟4秒,两个线程先打印发短信还是打电话?

先打印发短信,和上题情况相同。

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

先执行普通方法,由于普通方法没有上锁,所以不用等待发短信执行完毕就可先执行。

4.两个对象,两个同步方法,第一个对象执行发短信和打电话,第二个对象执行打电话,先执行发短信还是打电话?

先执行打电话,两个对象对应了两个锁,打电话没有休眠时间,所以比发短信先执行。

5.将两个同步方法都改为静态同步方法,只有一个对象。先执行发短信还是打电话?

先执行发短信, 静态方法在类初始化的时候就会执行,这时候锁的就不是对象了,而是Class,按顺序执行发短信在前同时拥有锁,打电话就得等待。

6.5条件中改为两个对象,先执行发短信还是打电话?

发短信,还是和上题答案一样,锁的是Class而不是对象,就和对象无关了。

7.发短信静态的同步方法,打电话普通的同步方法,先执行发短信还是打电话?

先执行打电话,这时候就有两个锁了,发短信占用的是Class的锁,而打电话占用的是对象的锁,两者互不影响

8.上述条件,两个对象,先执行发短信还是打电话?

还是先执行打电话,和上题解释相同。

15.集合的安全问题

15.1.List不安全

public class Test4 {public static void main(String[] args) {List<String> list = new ArrayList<>();for (int i = 0; i <10 ; i++) {new Thread(()->{list.add(UUID.randomUUID().toString().substring(0,5));System.out.println(list);},String.valueOf(i)).start();}}
}

执行报错:java.util.ConcurrentModificationException

并发下的ArrayList不安全,看add源码会发现没有同步锁

解决方案:

1**.使用vector集合**

vector集合是线程安全的,底层的add方法使用synchronized加上了同步锁

但是效率较低

2.使用Collection.synchronizedList(new ArrayList<>())来创建使集合线程安全

3.使用new CopyOnwriteArrayList<>()来创建

在写入时复制,避免覆盖造成数据问题。

底层add方法采用lock锁,更高效

15.2.Set不安全

上面例子换为Set试一试

public class Test5 {public static void main(String[] args) {Set<String> set = new HashSet<String>();for (int i = 0; i <10 ; i++) {new Thread(()->{set.add(UUID.randomUUID().toString().substring(0,5));System.out.println(set);},String.valueOf(i)).start();}}
}

同样报java.util.ConcurrentModificationException异常,没有多试几次或者增加循环次数。

解决方法:

1.Collections.synchronizedCollection(set)解决

2.new CopyOnWriteArraySet<>()解决。

接着问:HashSet的底层是什么?

HashSet底层就是HashMap

源码:

  public HashSet() {map = new HashMap<>();}
public boolean add(E e) {return map.put(e, PRESENT)==null;}private static final Object PRESENT = new Object();//常量

HashSet的值不重复利用的就是HashMap的键不重复。

15.3.map不安全

public class Test6 {public static void main(String[] args) {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();}}
}

同样报错ConcurrentModificationException,

解决:

1.Collections.synchronizedMap(map)

2.使用ConcurrentHashMap

16.Callable

  • Callable接口类似于Runnable ,因为它们都是为其实例可能由另一个线程执行的类设计的。然而,A Runnable不返回结果,也不能抛出被检查的异常。
public class CallableTest {public static void main(String[] args) {MyThread myThread = new MyThread();//FutureTask实现了RunnableFuture接口,RunnableFuture继承了RunnableFutureTask futureTask = new FutureTask<>(myThread);//适配类new Thread(futureTask).start();//结果会被缓存,效率高try {String o = (String) futureTask.get();//可能会产生阻塞,把他放到最后System.out.println(o);} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}
}
class MyThread implements Callable<String>{@Overridepublic String call() throws Exception {System.out.println("call()");return "1024";}
}

细节:

1.有缓存

2.结果可能会需要等待,会阻塞。

17.常用的辅助类

17.1.CountDownLatch

减法计数器

public class CountDownLatchTest {public static void main(String[] args) throws InterruptedException {//总数是6CountDownLatch countDownLatch = new CountDownLatch(6);for (int i = 1; i <=6 ; i++) {new Thread(()->{System.out.println(Thread.currentThread().getName()+"出去了");countDownLatch.countDown();//数量-1},String.valueOf(i)).start();}//等待总数为0时,再执行下面的任务,即所有人出去后再关门。countDownLatch.await();System.out.println("所有人都出去了,关门");//2出去了//1出去了//3出去了//4出去了//5出去了//6出去了//所有人都出去了,关门}
}

17.2.CyclicBarrier

加法计数器

public class CyclicBarrierTest {public static void main(String[] args) {//加法计数器,当计数到7时,输出CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{System.out.println("召唤神龙");});for (int i = 1; i <=7 ; i++) {final  int temp = i;new Thread(()->{System.out.println(Thread.currentThread().getName()+"拿到了"+temp+"个龙珠");try {cyclicBarrier.await();//等待到收集完7颗龙珠} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}},String.valueOf(i)).start();}}//1拿到了1个龙珠//2拿到了2个龙珠//3拿到了3个龙珠//4拿到了4个龙珠//5拿到了5个龙珠//6拿到了6个龙珠//7拿到了7个龙珠//召唤神龙
}

17.3.Semaphore

信号量

多个线程共享资源,同一时间只允许一定数量的线程占用资源。

模拟抢车位

public class SemaphoreTest {public static void main(String[] args) {//模拟抢车位,共3个车位Semaphore semaphore = new Semaphore(3);//6量车在抢for (int i = 1; i <=6 ; i++) {new Thread(()->{try {semaphore.acquire();System.out.println(Thread.currentThread().getName()+"抢到了车位");TimeUnit.SECONDS.sleep(2);semaphore.release();System.out.println(Thread.currentThread().getName()+"离开了车位");}catch (Exception e){e.printStackTrace();}},String.valueOf(i)).start();}}//1抢到了车位//3抢到了车位//2抢到了车位//1离开了车位//6抢到了车位//2离开了车位//3离开了车位//5抢到了车位//4抢到了车位//6离开了车位//4离开了车位//5离开了车位
}

18.读写锁

ReadWriteLock读写锁

读取锁(共享锁):允许多个线程进行读取操作。

写入锁(排他锁):只允许一个线程进行写入操作。

public class ReadWriteLockTest {public static void main(String[] args) {MyCache myCache = new MyCache();for (int i = 1; i <=3 ; i++) {final int temp = i;new Thread(()->{myCache.write(temp+"",temp+"");},String.valueOf(i)).start();}for (int i = 1; i <=3 ; i++) {final int temp = i;new Thread(()->{myCache.read(temp+"");},String.valueOf(i)).start();}}
}
class MyCache{private volatile Map<String,String> map = new HashMap<>();//读写锁private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();//读取public void read(String key){System.out.println(Thread.currentThread().getName()+"读取"+key);map.get(key);System.out.println(Thread.currentThread().getName()+"读取ok");}//写入public void write(String key,String value){System.out.println(Thread.currentThread().getName()+"写入"+key);map.put(key,value);System.out.println(Thread.currentThread().getName()+"写入ok");}//2写入2//2写入ok//1写入1//1写入ok//3写入3//3写入ok//1读取1//1读取ok//2读取2//2读取ok//3读取3//3读取ok
}

19.阻塞队列

BlockingQueue阻塞队列

在多线程并发处理和线程池情况下会使用阻塞队列

方式 抛出异常 有返回值,不抛出异常 阻塞等待 超时等待
添加 add offer put offer(,)
移除 remove poll take poll(,)
查看队首元素 element peek - -
  public static void main(String[] args) {ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);System.out.println(arrayBlockingQueue.add("a"));System.out.println(arrayBlockingQueue.add("b"));System.out.println(arrayBlockingQueue.add("c"));System.out.println(arrayBlockingQueue.add("d"));//抛出异常System.out.println("===============");System.out.println(arrayBlockingQueue.remove());System.out.println(arrayBlockingQueue.remove());System.out.println(arrayBlockingQueue.remove());System.out.println(arrayBlockingQueue.remove());  //抛出异常}
public static void main(String[] args) {ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);System.out.println(arrayBlockingQueue.offer("a"));System.out.println(arrayBlockingQueue.offer("b"));System.out.println(arrayBlockingQueue.offer("c"));System.out.println(arrayBlockingQueue.offer("d"));//返回falseSystem.out.println("===============");System.out.println(arrayBlockingQueue.poll());System.out.println(arrayBlockingQueue.poll());System.out.println(arrayBlockingQueue.poll());System.out.println(arrayBlockingQueue.poll());  //返回null}
 public static void main(String[] args) throws InterruptedException {ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);arrayBlockingQueue.put("a");arrayBlockingQueue.put("b");arrayBlockingQueue.put("c");arrayBlockingQueue.put("d");//队列没有位置了,一直阻塞System.out.println("===============");System.out.println(arrayBlockingQueue.take());System.out.println(arrayBlockingQueue.take());System.out.println(arrayBlockingQueue.take());System.out.println(arrayBlockingQueue.take());  //没有这个元素,一直阻塞}
   public static void main(String[] args) throws InterruptedException {ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);arrayBlockingQueue.offer("a");arrayBlockingQueue.offer("b");arrayBlockingQueue.offer("c");arrayBlockingQueue.offer("d",1,TimeUnit.SECONDS);//等待1s后就退出System.out.println("===============");System.out.println(arrayBlockingQueue.poll());System.out.println(arrayBlockingQueue.poll());System.out.println(arrayBlockingQueue.poll());System.out.println(arrayBlockingQueue.poll(1,TimeUnit.SECONDS));  //等待1s后就退出}

同步队列SynchronousQueue

没有容量,一个元素进来之后,必须等他出来才能进入下一个元素

    public static void main(String[] args) {//没有容量,一个元素进来之后,必须等他出来才能进入下一个元素SynchronousQueue<String> queue = new SynchronousQueue<>();new Thread(()->{try {System.out.println(Thread.currentThread().getName()+" put 1");queue.put("1");System.out.println(Thread.currentThread().getName()+" put 2");queue.put("2");System.out.println(Thread.currentThread().getName()+" put 3");queue.put("3");} catch (InterruptedException e) {e.printStackTrace();}},"t1").start();new Thread(()->{try {TimeUnit.SECONDS.sleep(2);System.out.println(Thread.currentThread().getName()+" take "+queue.take());TimeUnit.SECONDS.sleep(2);System.out.println(Thread.currentThread().getName()+" take "+queue.take());TimeUnit.SECONDS.sleep(2);System.out.println(Thread.currentThread().getName()+" take "+queue.take());} catch (InterruptedException e) {e.printStackTrace();}},"t2").start();}//t1 put 1//t2 take 1//t1 put 2//t2 take 2//t1 put 3//t2 take 3

20.线程池

三大方法,7大参数,4种拒绝策略。

线程池的好处

1.降低资源的消耗

2.提高响应的速度

3.方便管理

线程复用,可以控制最大并发数,管理线程

20.1.三大方法

newSingleThreadExecutor()

newFixedThreadPool(3)

newCachedThreadPool()

  public static void main(String[] args) {//ExecutorService executorService = Executors.newSingleThreadExecutor();//单个线程池// ExecutorService executorService = Executors.newFixedThreadPool(3);//指定线程池大小ExecutorService executorService = Executors.newCachedThreadPool();//遇强则强,遇弱则弱,最大可达到21亿try {for (int i = 0; i <10 ; i++) {executorService.execute(()->{System.out.println(Thread.currentThread().getName()+"执行");});}} finally {executorService.shutdown();}}

20.2.七大参数

看一下三大方法的底层源码

public static ExecutorService newSingleThreadExecutor() {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));}public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());}
public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());}

可以看到这三个方法最后都反返回了一个叫ThreadPoolExecutor方法

进去看一看

7大参数就是说的ThreadPoolExecutor中的7大参数

  • int corePoolSize,//核心线程数
  • int maximumPoolSize,//最大线程数
  • long keepAliveTime,//超时等待时间
  • TimeUnit unit,//超时单位
  • BlockingQueue workQueue,//阻塞队列
  • ThreadFactory threadFactory,//线程工厂,一般不用动
  • RejectedExecutionHandler handler//拒绝策略
   public ThreadPoolExecutor(int corePoolSize,//核心线程数int maximumPoolSize,//最大线程数long keepAliveTime,//超时等待时间TimeUnit unit,//超时单位BlockingQueue<Runnable> workQueue,//阻塞队列ThreadFactory threadFactory,//线程工厂,一般不用动RejectedExecutionHandler handler//拒绝策略) {if (corePoolSize < 0 ||maximumPoolSize <= 0 ||maximumPoolSize < corePoolSize ||keepAliveTime < 0)throw new IllegalArgumentException();if (workQueue == null || threadFactory == null || handler == null)throw new NullPointerException();this.acc = System.getSecurityManager() == null ?null :AccessController.getContext();this.corePoolSize = corePoolSize;this.maximumPoolSize = maximumPoolSize;this.workQueue = workQueue;this.keepAliveTime = unit.toNanos(keepAliveTime);this.threadFactory = threadFactory;this.handler = handler;}

举一个银行排队的例子来说明创建线程池的七大参数问题。

说明:

  1. 正常情况下银行柜台只开1号和2号窗口(corePoolSize核心线程数),最多有5个柜台同事开放(maximumPoolSize最大线程数)

  2. 在1号和2号窗口都有人的时候,剩下的人进入等候区(BlockingQueue阻塞队列),在等候区满的时候开启3,4,5号柜台。

  3. 在3,4,5窗口连续1小时没有人进入的 时候关闭3,4,5号窗口(keepAliveTime超时时间,TimeUnit超时单位)

  4. 在5个柜台全部开放且等候区满的情况下依然有人进入,这时候选择拒绝此人,拒绝此人的方法就是RejectedExecutionHandler拒绝策略

在阿里巴巴开发手册中明确要求不允许使用Excutors创建线程。而是通过ThreadPoolExecutor来创建,参数自己设置。

手动创建一个线程池案例

    public static void main(String[] args) {ExecutorService executorService=  new ThreadPoolExecutor(2,5,3,TimeUnit.SECONDS,new LinkedBlockingDeque<>(3),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());try {for (int i = 0; i <5 ; i++) {executorService.execute(()->{System.out.println(Thread.currentThread().getName()+"执行");});}} finally {executorService.shutdown();}}

当i的最大值为5时会有2个线程在执行

当i的最大值为6时会有3个线程在执行

当i的最大值为7时会有4个线程在执行

当i的最大值为8时会有5个线程在执行

当i的最大值为9时会抛出异常

所以由此可知线程池最大允许线程执行个数为阻塞队列大小+最大线程数

20.2.1.CPU密集型和IO密集型

面试题:最大线程到底该如何定义?

1.cpu密集型:cpu是几核就定义最大线程数是几。

2.IO密集型:判断你程序中十分耗IO的线程,假设有10个,那就可以设置2倍于他的最大线程数。

20.3.四种拒绝策略

在上述例子中,使用了默认的拒绝策略AbortPolicy,还有三种拒绝策略

AbortPolicy:

  • 阻塞队列满了,不处理,并抛出异常。

CallerRunsPolicy:

  • 阻塞队列满了,哪里来的去哪里,从main线程来的就交个main线程执行

DisCardOldesPolicy:

  • 阻塞队列满了,丢掉任务不管他并且不会抛出异常

DiscardPolicy:

  • 阻塞队列满了,尝试和最早进入线程池的线程竞争,不会抛出异常

21.四大函数式接口

函数式接口:只有一个方法的接口。

Function函数式接口:有一个输入参数,有一个输出参数

Function<String,String> function = (str)->{return str;};System.out.println(function.apply("123"));

断定型函数式接口:有一个输入参数,返回值只能是布尔值

Predicate<String> predicate = (str)->{return false;};
System.out.println(predicate.test("123"));

消费型接口:只有输入参数,没有返回值

Consumer<String> consumer = (str)->{System.out.println(str);};consumer.accept("123");

供给型接口:没有参数,只有返回值

Supplier<String> supplier = ()->{return "123";};System.out.println(supplier.get());

22.Stream流

什么是stream流?

存储应交给集合和数据库来做

计算交给流来做

案例

题目要求:

1分钟内完成此题,只能用1行代码实现!

现在有5个用户!筛选:

1、ID 必须是偶数

2、年龄必须大于23岁

3、用户名转为大写字母

4、用户名字母倒着排序

5、只输出1个用户!

public static void main(String[] args) {User u1 = new User(1,"a",21);User u2 = new User(2,"b",26);User u3 = new User(3,"c",24);User u4 = new User(4,"d",25);User u5 = new User(5,"e",26);List<User> list = Arrays.asList(u1,u2,u3,u4,u5);list.stream().filter(user->{return user.getId()%2==0;})//id为偶数.filter(user->{return user.getAge()>23;})//年龄大于23岁.map(user ->{return user.getName().toUpperCase();})//字母转换为大写.sorted((user1,user2)->{return user2.compareTo(user1);})//倒序排序.limit(1)//只允许一个输出.forEach(System.out::println);//遍历}

23.JMM

请你谈谈对Volatile的理解

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

1.保证可见性

2.不能保证原子性

3.禁止指令重排

什么是JMM

Java内存模型,不存在的东西,是一种概念,约定。

关于JMM一些同步的约定:

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

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

3.加锁和解锁是同一把锁

8种操作:

问题:程序不知道主内存里的值已经修改过了

24.Volatile

1.保证可见性

private volatile static int num = 0;public static void main(String[] args) throws InterruptedException {new Thread(()->{while (num==0){}}).start();TimeUnit.SECONDS.sleep(1);num = 1;System.out.println(num);}

num加了volatile保证可见性,如果不加volatile线程就监测不到主内存中的num的值已经修改了,就会陷入死循环。

2.不保证原子性

private volatile static int num;public static void add(){num++;}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){//main,gcThread.yield();//线程礼让}System.out.println(num);}

理论上上述代码输出结果应为20000,实际上

这是由于volatile不能保证原子性。

那么如何保证原子性?

在java.util.concurrent.atomic下提供了原子类,可以保证原子性。

改造上述代码

    private volatile static AtomicInteger num=new AtomicInteger();public static void add(){//   num++;//不是一个原子性操作num.getAndIncrement();//+1方法}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){//main,gcThread.yield();//线程礼让}System.out.println(num);}

结果:

3.禁止指令重排

指令重排:程序并不会按照写的顺序来执行,计算机会重排

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

处理器在进行指令重排的时候,考虑:数据之间的依赖性!

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

正常的结果:x=0,y=0;但是可能由于指令重排

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

结果为:x=2,y=1.

volatile可以避免指令重排:

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

1.保证特定的操作的执行顺序

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

25.CAS

CAS是原子类保证其原子性的原理。

CompareAndSet

源码:

如果期望的值符合则更新并且返回true,反之返回false。

代码举例:

public static void main(String[] args) {AtomicInteger atomicInteger = new AtomicInteger(1);//参数1:期望值,参数2:更新的值System.out.println(atomicInteger.compareAndSet(1, 2));System.out.println(atomicInteger);atomicInteger.getAndIncrement();System.out.println(atomicInteger.compareAndSet(1, 2));System.out.println(atomicInteger);}

Unsafe类

Java无法操作内存

Java可以通过native调用C++操作内存。

Unsafe是java提供的操作内存的后门。

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

缺点:

1.循环会耗时。

2.一次性只能保证一个共享变量的原子性。

3.ABA问题。

ABA问题

26.原子引用

解决ABA问题,引入原子引用,对应的思想:乐观锁

原子引用在CAS基础上添加了版本号,记录对对象的修改操作。

即使内存中的值相同,版本号不同则会返回false

    public static void main(String[] args) {//参数1:初始值,参数2:初始版本号AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1,1);new Thread(()->{int stamp = atomicStampedReference.getStamp();//获得版本号System.out.println("a1=>"+stamp);try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}//参数:期望值,更新值,期望版本号,更新版本号atomicStampedReference.compareAndSet(1,2,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);System.out.println("a2=>"+atomicStampedReference.getStamp());atomicStampedReference.compareAndSet(2,1,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);System.out.println("a3=>"+atomicStampedReference.getStamp());},"a").start();new Thread(()->{int stamp = atomicStampedReference.getStamp();//获得版本号System.out.println("b1=>"+stamp);try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(atomicStampedReference.compareAndSet(1, 6, stamp, stamp + 1));System.out.println("b2=>"+atomicStampedReference.getStamp());},"b1").start();}

27.各种锁

27.1.公平锁,非公平锁

公平锁:不允许插队,先来后到

非公平锁:允许插队。

非公平锁比公平锁效率更高,例如两个人上厕所,一个大便一个小便,虽然大便的先来,但是小便的先上厕所效率更高。

synchronized 是非公平锁

lock默认非公平锁,但可以手动设置

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

27.2.可重入锁

拿到了外面的锁,就可以拿到里面的锁,自动获得。

synchronized

public class Demo01 {public static void main(String[] args) {Phone1 phone1 = new Phone1();new Thread(()->{phone1.sms();}).start();new Thread(()->{phone1.sms();}).start();}
}
class Phone1 {public synchronized void sms(){System.out.println(Thread.currentThread().getName()+"sms");call();//这里也有一把锁}public synchronized void call(){System.out.println(Thread.currentThread().getName()+"call");}
}

lock

class Phone1 {private ReentrantLock lock = new ReentrantLock();public  void sms(){lock.lock();System.out.println(Thread.currentThread().getName()+"sms");call();//这里也有一把锁lock.unlock();}public  void call(){lock.lock();System.out.println(Thread.currentThread().getName()+"call");lock.unlock();}
}

结果一样。

27.3.自旋锁

spinLock

当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环

自定义一个锁测试

public class SpinLockDemo {//int 0, Thread nullAtomicReference<Thread> atomicReference = new AtomicReference<>();//加锁public void myLock(){Thread thread = Thread.currentThread();System.out.println(Thread.currentThread().getName()+"==》myLock");//自旋锁,如果获取不到锁就一直循环等待while (!atomicReference.compareAndSet(null,thread)){}}public void myUnlock(){Thread thread = Thread.currentThread();System.out.println(Thread.currentThread().getName()+"==》myUnlock");atomicReference.compareAndSet(thread,null);}
}
    public static void main(String[] args) throws InterruptedException {SpinLockDemo spinLockDemo = new SpinLockDemo();new Thread(()->{spinLockDemo.myLock();try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}spinLockDemo.myUnlock();},"t1").start();TimeUnit.SECONDS.sleep(1);new Thread(()->{spinLockDemo.myLock();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}spinLockDemo.myUnlock();},"t2").start();}

t2必须等待t1将锁释放后才能执行解锁。

多线程基础与JUC进阶笔记相关推荐

  1. JavaSE自学笔记Real_008(多线程基础)

    JavaSE自学笔记Real_008(多线程基础) 线程的优先级设置(priority) 线程的优先级用数字表示,范围是1到10(在范围之外会报错) Thread.MIN_PRIORITY = 1 T ...

  2. java多线程基础学习[狂神说java-多线程笔记]

    java多线程基础学习 一.线程简介 1.类比 2.程序进程线程 3.线程的核心概念 二.线程的实现(重点) 调用方法与调用多线程的区别 Thread 类 1.thread使用方法 2. 代码实现 3 ...

  3. Docker基础、进阶笔记,为k8s的学习预预热

    标题 Docker基础.进阶笔记,为k8s的学习预预热 笔记来源于视频: 狂神docker基础篇 狂神docker进阶篇 笔记中图片有些取自于:这位博主的两篇docker笔记中的一些图片 百度云笔记工 ...

  4. MySQL数据库基础到进阶笔记整理包含事务、SQL优化、锁等内容

    写在前面 本文是在学习MySQL数据库时整理的笔记,可供初学者学习 是在https://dhc.pythonanywhere.com/entry/share/?key=12e4a7324f68371d ...

  5. Java语言基础(Java自我进阶笔记二)

    Java语言基础(Java自我进阶笔记二) 一. 什么是Java 的主类结构? 1. #mermaid-svg-xWTL2A8kDyyRPexH .label{font-family:'trebuch ...

  6. 阿里内部发布最新版Java进阶笔记,金九银十看这份文档就够了

    大家都说程序员这个职业薪资高.待遇好,现在是程序员"跳槽"的黄金时期,你准备好了吗?有没有给自己定个小目标?是30K.40K,还是更高?短期内提高Java 核心能力最快.最有效的方 ...

  7. Java零基础学习全套视频笔记

    Java零基础学习全套视频笔记 一.Java基础 1.注释 注释并不会被执行,是给我们写代码的人看的,防止项目结构代码太多忘记代码相关功能. 书写注释是一个非常好的习惯,平时写代码也一定要注意规范. ...

  8. python基础和第三方库 笔记(python基础完结包括高级用法,第三方库持续更新中...)

    python基础 注:本笔记面向有一定基础的人 本笔记是本人快速复习python过程中记录的,不适合零基础的人学习python的主工具,可以作为辅工具,本笔记记录了入门阶段常用操作,如有错误的地方,希 ...

  9. 【尚硅谷/周阳】JUC学习笔记

    JUC学习笔记[尚硅谷/周阳] 本文章基于B站视频教程[juc 与 jvm 并发编程 Java 必学_阳哥- 尚硅谷]进行整理记录,仅用于个人学习,交流使用. 目录标题 JUC学习笔记[尚硅谷/周阳] ...

最新文章

  1. GNU parted的指令說明[转]
  2. 【新星计划】Matlab绘制分岔图
  3. 信息系统项目管理师--项目整体管理
  4. linux测试函数耗时tick数,C语言中怎样测验函数执行时间
  5. python课程设计矩阵对角线之和_在python中如何求矩阵对角线上下元素的和?
  6. 华为P50系列已适配鸿蒙OS 2.0 Beta2:有望4月亮相
  7. Libevent源码解析
  8. Eclipse使用(入门教程)
  9. 开源软件与自由软件的区别
  10. 索引缓存_如何重置您的Google桌面索引缓存
  11. QGIS设置点图层样式并导出SLD文件
  12. Dapr for dotnet | 服务调用-Service invoke(HTTP协议)
  13. C#winform窗体背景音乐播放总结
  14. 西工大PAMI论文:发布大规模人群计数/定位基准平台
  15. Kelvin Test
  16. 光之触角——光敏电阻、光敏二极管、光敏三极管与光照发生器
  17. 通过URL下载HTML页面
  18. 使用easyExcel批量导入导出数据
  19. 会议管理系统怎么加录播服务器,Teams会议视频怎么录制?这三种方法你知道吗...
  20. 自媒体辅助工具,辅助你打造爆款内容

热门文章

  1. tf.name_scope()与tf.variable_scope()
  2. 区块链BaaS云服务(9)索尼 区块链通用数据库 BCDB
  3. java渡劫期(32)----java进阶(ssm整合项目实战----房屋出租系统(渡劫失败))
  4. linux kernel中的中断处理流程
  5. IMAP和POP3的相关知识与区别
  6. android判断是否被点击方法,[Android]父View如何知道子View被点击了
  7. WIN10 关闭驱动签名
  8. 2020-11-22(操作系统——页面置换算法)
  9. PsSetCreateProcessNotifyRoutineEx进程监控框架
  10. HDU2034 人见人爱A-B(C++)