文章目录
JUC编程
1、线程和进程
线程、进程
**进程:**一个可运行的程序。
一个进程可以包含多个线程,至少包含一个线程。
java默认的线程有2个:mian主线程、gc垃圾回收线程。
**线程:**进程中的一个执行任务。
**java不能开启线程,**而是通过本地方法native~ java没有权限开线程。
并发、并行
并发编程:并发、并行。
**并发:**多个线程操作同一个资源。
- cpu一核(一瞬间只能处理一个东西),模拟出来多条线程,通过快速交替,来实现多条线程(其实这是一种假象)。
**并行:**意思就是多个人一起行走。
- cpu多核:多个线程可以同时执行;可以使用线程池操作。
补充:查看cpu核数的代码:
system.out.println(Runtime/getRuntime().availableProcessors())
并发编程的本质:充分利用cpu的资源。
线程有几个状态
new 新生
Runnable 运行
BLOCKED 阻塞
WAITING 等待,死等
TUMED_WAITING 超时等待
TERMINATED 终止
wait/sleep 区别
1、来自不同的类
wait=>object
sleep=>Thread
2、关于锁的释放
wait会释放锁
sleep不会释放(抱着锁睡觉了,就不会释放了)
3、使用的范围是不同的
wait:必须在同步代码块中
sleep:可以在任何地方
4、是否需要捕获异常
wait不需要捕获异常
sleep必须捕获异常
2、lock锁(重点)
传统synchronized
package com.xiao.demo01;
//基本的卖票例子
/**
*真正的多线程开发公司中的开发,降低偶合性
* 线程就是一个单独的资源类,没有任何附属的操作
* 资源类里面只有:属性,方法
*/
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 = 50;
//卖票的方式
public synchronized void sale(){
if (number > 0){
System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余"+number);
}
}
}
lock接口
package com.xiao.demo01;
import jdk.nashorn.internal.ir.Block;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SaleTicketDemo02 {
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();
}
}
//lock三部曲
//1、new ReentrantLock();
//2、 lock.lock(); 加锁
//3、lock.unlock(); 解锁
class Ticket2{
//属性,方法
private int number = 50;
Lock lock = new ReentrantLock();
//卖票的方式
public void sale(){
lock.lock();
try {
//业务代码
if (number > 0){
System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余"+number);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
公平锁:十分公平,可以先来后到
**非公平锁:**十分不公平,可以插队(默认非公平锁)
synchronized和lock区别
- synchronized 内置的java关键字,lock是一个java类
- synchronized 无法判断获取锁的状态,lock可以判断是否获得了锁
- synchronized 会自动释放锁,lock必须要手动解锁,如果不释放锁,就会死锁
- synchronzied线程1(获得锁,阻塞)、线程2(等待,傻傻的等);lock锁就不一定会等待下去;try尝试
- synchronized可重入锁,不可以中断的,非公平;lock,可重入锁,可以判断锁,非公平(可以自己设置)
- synchronized适合锁少量的代码同步问题;lock锁大量的同步代码。
问题来了锁是什么?如何判断锁的是谁?
(后面有答案)
3、生产者和消费者问题
生产者和消费者问题 synchronized版 ( wait notify)
package com.xiao.demo01;
public class SaleTicketDemo03 {
public static void main(String[] args) {
A a = new A();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
a.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"Q").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
a.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"W").start();
}
}
class A{
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();
}
}
问题存在:四个线程的情况下这么写还安全吗?
解决方法:将a类中的if判断改为while判断
因为if只判断一次,所以该用while,防止虚假唤醒
package com.xiao.demo01;
public class SaleTicketDemo03 {
public static void main(String[] args) {
A a = new A();
new Thread(()->{
for (int i = 0; i < 40; i++) {
try {
a.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"Q").start();
new Thread(()->{
for (int i = 0; i < 40; i++) {
try {
a.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"W").start();
new Thread(()->{
for (int i = 0; i < 40; i++) {
try {
a.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"e").start();
new Thread(()->{
for (int i = 0; i < 40; i++) {
try {
a.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"r").start();
}
}
class A{
private int number = 0;
public synchronized void increment () throws InterruptedException {
while (number!=0){
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
this.notifyAll();
}
public synchronized void decrement () throws InterruptedException {
while (number==0){
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
this.notifyAll();
}
}
juc版的生产者和消费者问题 (lock await signal)
package com.xiao.demo01;
import jdk.nashorn.internal.ir.Block;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SaleTicketDemo03 {
public static void main(String[] args) {
A a = new A();
new Thread(()->{
for (int i = 0; i < 40; i++) {
try {
a.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"Q").start();
new Thread(()->{
for (int i = 0; i < 40; i++) {
try {
a.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"W").start();
new Thread(()->{
for (int i = 0; i < 40; i++) {
try {
a.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"e").start();
new Thread(()->{
for (int i = 0; i < 40; i++) {
try {
a.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"r").start();
}
}
class A{
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 (Exception 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 (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
任何一个新的技术,绝对不是仅仅只有覆盖了原来的技术,优势和补充
Condition 精准的通知和唤醒线程
package com.xiao.demo01;
/**
* a执行完调用b,b执行完调用c,c执行完调用a
*/
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) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.printA();
}
},"A").start();
new Thread(()->{ for (int i = 0; i < 10; i++) {
data.printB();
}},"B").start();
new Thread(()->{ for (int i = 0; i < 10; i++) {
data.printC();
}},"C").start();
}
}
class Data {
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
private int number = 1;//1a 2b 3c
public void printA(){
lock.lock();
try {
//业务,判断 执行 通知
while (number!=1){
//等待
condition1.await();
}
System.out.println(Thread.currentThread().getName()+"aaaaa");
//唤醒,唤醒指定的人b
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()+"bbbbb");
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()+"ccccc");
number=1;
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
4、8锁现象
如何判断锁的是谁,永远的知道什么是锁,锁到底锁的是谁。
package com.xiao.demo01;import java.util.concurrent.TimeUnit;/** * 8锁,就是关于锁的8个问题 * 1、标准情况下,两个线程先打印发短信还是打电话 发短信。打电话 */public class Test { public static void main(String[] args) { Prone prone = new Prone(); new Thread(()->{ prone.sendSms(); },"A").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(()->{prone.call();},"B").start(); }}class Prone{ //synchronized锁的对象是方法的调用者 //两个方法用的是同一个锁,谁先拿到就谁执行 public synchronized void sendSms(){ try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("发短信"); } public synchronized void call(){ System.out.println("打电话"); }}
package test;import java.util.concurrent.TimeUnit;/** * 3、增加了一个普通方法后,先执行发短信还是hello 普通方法 * 4、两个对象,两个同步方法,发短信还是打电话? 打电话 */public class Test2 { public static void main(String[] args) { //两个对象 Prone2 prone1 = new Prone2(); Prone2 prone2 = new Prone2(); new Thread(()->{prone1.sendSms();},"A").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(()->{prone2.call();},"B").start(); }}class Prone2{ //synchronized锁的对象是方法的调用者 public synchronized void sendSms(){ 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 test;import java.util.concurrent.TimeUnit;/** * 7、一个静态的同步方法,一个普通的同步方法,一个对象 先哪个? 打电话 * 8、一个静态的同步方法,一个普通的同步方法,两个对象 先哪个? 打电话 */public class Test4 { public static void main(String[] args) { //两个对象 Prone4 prone1 = new Prone4(); Prone4 prone2 = new Prone4(); new Thread(()->{ prone1.sendSms(); },"A").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(()->{prone2.call();},"B").start(); }}//phone3唯一的一个class对象class Prone4{ //静态同步方法 锁的class模版 public static synchronized void sendSms(){ try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("发短信"); } //普通同步方法 锁的调用着 public synchronized void call(){ System.out.println("打电话"); }}
小结
new :this具体的一个手机
static : class唯一的一个模版
5、集合类不安全
list不安全
package com.xiao.demo01;import java.util.*;import java.util.concurrent.CopyOnWriteArrayList;//java.util.ConcurrentModificationException 并发修改异常public class ListTest { public static void main(String[] args) { //并发下 ArrayList 不安全的 /** * 解决方案 * 1、Vector解决 * 2、List<String> list = Collections.synchronizedList(new ArrayList<>()); * 3、List<String> list = new CopyOnWriteArrayList<>(); */ //CopyOnWrite 写入时复制 cow 计算机程序设计领域的一种优化策略 //多个线程使用的时候,list,读取的时候,固定的,写入,覆盖 //在写入的时候避免覆盖,造成数据问题 //读写分离 //CopyOnWrite比Vector好在前一个不用synchronized,效率相比就会更快 List<String> list = new CopyOnWriteArrayList<>(); for (int i = 1; i < 10; i++) { new Thread(()->{ list.add(UUID.randomUUID().toString().substring(0,5)); System.out.println(list); },String.valueOf(i)).start(); } }}
set不安全
package com.xiao.demo01;import java.util.Collections;import java.util.HashSet;import java.util.Set;import java.util.UUID;import java.util.concurrent.CopyOnWriteArraySet;/** * 同理可证 java.util.ConcurrentModificationException * 1、Set<String> set = Collections.synchronizedSet(new HashSet<>()); * 2、Set<String> set = new CopyOnWriteArraySet<>(); */public class SetTest { public static void main(String[] args) { // Set<String> set = new HashSet<>(); // Set<String> set = Collections.synchronizedSet(new HashSet<>()); Set<String> set = new CopyOnWriteArraySet<>(); for (int i = 1; i < 30; i++) { new Thread(()->{ set.add(UUID.randomUUID().toString().substring(0,5)); System.out.println(set); },String.valueOf(i)).start(); } }}
hashSet底层是什么? (就是hashmap)
public HashSet() { map = new HashMap<>();}//add set本质就是map key是无妨重复的public boolean add(E e) { return map.put(e, PRESENT)==null;}
6、Callable(简单)
1、可以有返回值
2、可以抛出异常
3、方法不同run()/call()
代码测试
callable通过runable的实现类futuretask来沟通到thread
package com.xiao.demo01;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().start(); //怎么启动Callable MyThread thread = new MyThread(); FutureTask futureTask = new FutureTask(thread); //适配类 new Thread(futureTask,"A").start(); new Thread(futureTask,"B").start();//结果会缓存,效率高 Integer o = (Integer) futureTask.get();//获取callable的返回结果 // 这个get方法可能产生堵塞,把它放到最后 //使用异步 System.out.println(o); }}class MyThread implements Callable<Integer>{ @Override public Integer call() { System.out.println("call()"); return 1024; }}
7、常用的辅助类
7.1、CountDownLatch
package com.xiao.demo01;import java.util.concurrent.CountDownLatch;public class CountDownLatchDemo { public static void main(String[] args) throws InterruptedException { //总数是6 CountDownLatch countDownLatch = new CountDownLatch(6); for (int i = 1; i <= 6; i++) { new Thread(()->{ System.out.println(Thread.currentThread().getName()+"go out"); countDownLatch.countDown(); },String.valueOf(i)).start(); } countDownLatch.await();//等待计数器归零,然后再向下执行 System.out.println("Close Door"); }}
原理:
countDownLatch.countDown();数量-1
countDownLatch.await();//等待计数器归零,然后再向下执行
每次有线程调用countdown()数量-1,假设计数器为0,countDownLatch.await();就会被唤醒,继续执行
7.1、CyclicBarrier
package com.xiao.demo01;import java.util.concurrent.BrokenBarrierException;import java.util.concurrent.CyclicBarrier;public class CyclicBarrierDemo { public static void main(String[] args) { /** * 集齐7颗龙珠召唤神龙 * */ //召唤龙珠的线程 CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{ System.out.println("召唤神龙成功"); }); for (int i = 0; i < 7; i++) { final int temp = i; //Lambda 能操作到i吗? new Thread(()->{ System.out.println(Thread.currentThread().getName()+"收集"+temp+"龙珠"); try { cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } }).start(); } }}
7.3、Semaphore
package com.xiao.demo01;import java.util.concurrent.Semaphore;import java.util.concurrent.TimeUnit;public class SemaphoreDemo { public static void main(String[] args) { Semaphore semaphore = new Semaphore(3); for (int i = 1; i <=6 ; i++) { new Thread(()->{ //acquire得到 try { semaphore.acquire(); System.out.println(Thread.currentThread().getName()+"抢到车位"); TimeUnit.SECONDS.sleep(2); System.out.println(Thread.currentThread().getName()+"离开车位"); } catch (InterruptedException e) { e.printStackTrace(); }finally { //release释放 semaphore.release(); } },String.valueOf(i)).start(); } }}
原理:
acquire得到
,假设如果已经满了,等待,等待被释放为止
release释放,会将当前的信号量释放-1,然后唤醒等待的线程
作用:多个共享资源互斥的使用,并发限流,控制最大的线程数
8、读写锁
ReadWriteLock
read读可以被多线程同时读
Write写的时候只能有一个线程去写
package com.xiao.demo01;import java.util.HashMap;import java.util.Map;import java.util.concurrent.locks.ReadWriteLock;import java.util.concurrent.locks.ReentrantReadWriteLock;/** * 独占锁(写锁)一次只能被一个线程占有 * 共享锁(读锁)多个线程可以同时占有 * ReadWriteLock * 读-读 可以共存 * 读-写 不能共存 * 写-写 不能共存 */public class ReadWriteLockDemo { public static void main(String[] args) { MyCacheLock myCache = new MyCacheLock(); for (int i = 1; i <= 5; i++) { final int temp = i; new Thread(()->{ myCache.put(temp+"",temp+""); },String.valueOf(i)).start(); } for (int i = 1; i <= 5; i++) { final int temp = i; new Thread(()->{ myCache.get(temp+""); },String.valueOf(i)).start(); } }}//加锁的class MyCacheLock{ private volatile Map<String,Object> map = new HashMap<>(); //读写锁,更加细粒度的控制 private ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); //存,写,写入的时候只希望同时只有一个线程写 public void put(String key,Object value){ readWriteLock.writeLock().lock(); try { System.out.println(Thread.currentThread().getName()+"写入"+key); map.put(key, value); System.out.println(Thread.currentThread().getName()+"写入OK"); } catch (Exception e) { e.printStackTrace(); }finally { readWriteLock.writeLock().unlock(); } } //取,读,所有人都可读 public void get(String key){ readWriteLock.readLock().lock(); try { System.out.println(Thread.currentThread().getName()+"读取"+key); Object o = map.get(key); System.out.println(Thread.currentThread().getName()+"读取OK"); } catch (Exception e) { e.printStackTrace(); } finally { readWriteLock.readLock().unlock(); } }}/** * 自定义缓存 */class MyCache{ private volatile Map<String,Object> map = new HashMap<>(); //存,写 public void put(String key,Object value){ System.out.println(Thread.currentThread().getName()+"写入"+key); map.put(key, value); System.out.println(Thread.currentThread().getName()+"写入OK"); } //取,读 public void get(String key){ System.out.println(Thread.currentThread().getName()+"读取"+key); Object o = map.get(key); System.out.println(Thread.currentThread().getName()+"读取OK"); }}
9、阻塞队列
阻塞队列
BlockingQueue不是新的东西
什么情况下我们会使用阻塞队列:多线程并发处理、线程池
学会使用队列
添加、移除
四组API
方式 | 抛出异常 | 不抛出异常/有返回值 | 阻塞等待 | 超时等待 |
---|---|---|---|---|
添加 | add | offer() | put | offer(,,) |
移除 | remove | poll() | take | poll(,) |
判断队列首部 | element | peek | - | - |
抛出异常
package com.xiao.demo01;import java.util.concurrent.ArrayBlockingQueue;public class Test1 { public static void main(String[] args) { test1(); } /** * 抛出异常 */ public static void test1(){ 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"));报错 arrayBlockingQueue.remove(); System.out.println(arrayBlockingQueue.element()); arrayBlockingQueue.remove(); arrayBlockingQueue.remove(); //arrayBlockingQueue.remove();报错 }}
不抛出异常
package com.xiao.demo01;import java.util.concurrent.ArrayBlockingQueue;public class Test1 { public static void main(String[] args) { test2(); } public static void test2(){ 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"));//不抛出异常,返回boolean System.out.println(arrayBlockingQueue.poll()); System.out.println(arrayBlockingQueue.peek()); System.out.println(arrayBlockingQueue.poll()); System.out.println(arrayBlockingQueue.poll()); System.out.println(arrayBlockingQueue.poll()); }}
阻塞等待
package com.xiao.demo01;import javax.lang.model.type.ArrayType;import java.util.concurrent.ArrayBlockingQueue;public class Test1 { public static void main(String[] args) throws InterruptedException { test3(); } public static void test3() throws InterruptedException { ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3); arrayBlockingQueue.put("a"); arrayBlockingQueue.put("b"); arrayBlockingQueue.put("c"); arrayBlockingQueue.put("d");//队列没有位置了,一直等待 arrayBlockingQueue.take(); arrayBlockingQueue.take(); arrayBlockingQueue.take(); arrayBlockingQueue.take(); arrayBlockingQueue.take(); arrayBlockingQueue.take(); }}
超时等待
package com.xiao.demo01;import javax.lang.model.type.ArrayType;import java.sql.Time;import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.TimeUnit;public class Test1 { public static void main(String[] args) throws InterruptedException { test4(); } public static void test4() throws InterruptedException { ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3); arrayBlockingQueue.offer("a"); arrayBlockingQueue.offer("b"); arrayBlockingQueue.offer("c"); arrayBlockingQueue.offer("d",1 ,TimeUnit.SECONDS);//等待,超过两面就推出 System.out.println(arrayBlockingQueue.poll()); System.out.println(arrayBlockingQueue.poll()); System.out.println(arrayBlockingQueue.poll()); System.out.println(arrayBlockingQueue.poll(2, TimeUnit.SECONDS)); }}
SynchronousQueue同步队列
没有容量,进去一个元素,必须等待取出来之后,才能再往里面放一个与元素
put、take
package com.xiao.demo01;import java.util.concurrent.SynchronousQueue;import java.util.concurrent.BlockingQueue;import java.util.concurrent.TimeUnit;/** * 同步队列 * 和其他的BlockQueue不一样,SynchronousQueue不存储元素 * put了一个元素,必须从里面先take取出来,否则不能再put进去值 */public class SynchronousQueueDemo { public static void main(String[] args) { BlockingQueue<String> arrayBlockingQueue = new SynchronousQueue<>();//同步队列 new Thread(()->{ try { System.out.println(Thread.currentThread().getName()+"put 1"); arrayBlockingQueue.put("1"); System.out.println(Thread.currentThread().getName()+"put 2"); arrayBlockingQueue.put("2"); System.out.println(Thread.currentThread().getName()+"put 3"); arrayBlockingQueue.put("3"); } catch (InterruptedException e) { e.printStackTrace(); } },"T1").start(); new Thread(()->{ try { TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName()+arrayBlockingQueue.take()); TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName()+arrayBlockingQueue.take()); TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName()+arrayBlockingQueue.take()); } catch (InterruptedException e) { e.printStackTrace(); } },"T2").start(); }}
10、线程池(重点)
线程池:三大方法,7大参数,4中拒绝策略
池化技术
程序的运行,本质:占用系统的资源!我们就要优化资源的使用=>池化技术
池化技术:事先准备好一些资源,有人要用,就要来这里拿,用完之后还给我。
线程池的好处:
- 降低资源消耗
- 提高响应的速度(不用重复创建,销毁)
- 方便管理
作用:线程复用、可以控制最大并发数,管理线程
线程池三大方法:
package com.xiao.demo01;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;//Executors工具类:三大方法//使用了线程池之后,使用线程池来创建线程public class poll { public static void main(String[] args) { // ExecutorService service = Executors.newSingleThreadExecutor();//单个线程 final ExecutorService service = Executors.newFixedThreadPool(5);//创建一个固定的线程池大小// Executors.newCachedThreadPool()//可伸缩的(遇强则强,与弱则弱) try { for (int i = 0; i < 10; i++) { service.execute(() -> { System.out.println(Thread.currentThread().getName() + "OK"); }); } // 线程池用完,程序结束,关闭线程池 } catch (Exception e) { e.printStackTrace(); } finally { service.shutdown(); } } }
7大参数
源码分析
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 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;}
手动创建一个线程池/自定义线程池
4中拒绝策略
package com.xiao.demo01;import java.util.concurrent.*;//new ThreadPoolExecutor.AbortPolicy());//银行满了,还有人进来,不处理这个人的,抛出异常//new ThreadPoolExecutor.CallerRunsPolicy());//哪里来的去哪里//new ThreadPoolExecutor.DiscardPolicy());//队列满了,不会抛出异常//new ThreadPoolExecutor.DiscardOldestPolicy());//队列满了,//尝试去和最早的竞争(看它是不是要结束了,如果结局就跟上,否则还是抛出)//Executors工具类:三大方法public class poll { public static void main(String[] args) { //自定义线程池 ExecutorService service = new ThreadPoolExecutor( 2, 5, 3, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardOldestPolicy());//队列满了, // 尝试去和最早的竞争(看它是不是要结束了,如果结局就跟上,否则还是抛出) try { //RejectedExecutionException超出异常 for (int i = 1; i <= 112 ; i++) { service.execute(() -> { System.out.println(Thread.currentThread().getName() + " OK"); }); } // 线程池用完,程序结束,关闭线程池 } catch (Exception e) { e.printStackTrace(); } finally { service.shutdown(); } } }
拓展
了解:io密集型,cpu密集型:(调优)
//最大线程到底该如何定义?//1、CPU 密集型,几核的可以保持cpu的效率更高System.out.println(Runtime.getRuntime().availableProcessors());//获得cpu的核数//2、IO 密集型,判断你的程序中十分耗io的线程有什么个,只要大于它的一倍的数就可以// 程序 15个大型任务 io十分占用资源
11、四大函数式接口(重点、简单、必须掌握)
新时代的程序员:lambda表达式,链式编程,函数式接口,Stream流式计算
函数式接口
@FunctionalInterfacepublic interface Runnable { public abstract void run();}//超级多FunctionalInterface//简化编程模型,再新版本的框架底层大量应用//比如foreach(消费者类的函数式接口)
四大原生函数式接口:java.util.function里面的:
- Consumer
- Function
- Predicate
- Supplier
代码测试
Funtion函数式接口
package com.xiao.demo01;/** * Function函数型接口,有一个输入参数,有一个输出 * 只要是函数式接口,可以用lambda表达式简化 */import java.util.function.Function;public class fnDemo { public static void main(String[] args) { //工具类,输出输入的值 Function function = (str)->{return str;};// Function function = new Function<String,String>() {// @Override// public String apply(String str) {// return str;// }// }; System.out.println(function.apply("daw")); }}
断定型接口
package com.xiao.demo01;/** *断定型接口:有一个输入参数,返回值只能是布尔值 */import java.util.function.Predicate;public class fnDemo { public static void main(String[] args) { //判断字符串是否为空// Predicate<String> predicate = new Predicate<String>() {// @Override// public boolean test(String str) {// return str.isEmpty();// }// }; Predicate<String> predicate1 = (str)->{ return str.isEmpty(); }; System.out.println(predicate1.test("")); }}
Consumer消费型接口
package com.xiao.demo01;import java.util.function.Consumer;/** *消费型接口:只有输入,没有返回值 */public class fnDemo { public static void main(String[] args) {// Consumer<String> consumer = new Consumer<String>() {// @Override// public void accept(String o) {// System.out.println(o);// }// }; Consumer<String> consumer = (str)->{ System.out.println(str); }; consumer.accept("sasw"); }}
Supplier 供给型接口
package com.xiao.demo01;import java.util.function.Supplier;/** *供给型接口,没有参数,只有返回值 */public class fnDemo { public static void main(String[] args) {// Supplier supplier = new Supplier<Integer>() {// @Override// public Integer get() {// return 1024;// }// }; Supplier supplier =()->{ return 1024;}; System.out.println(supplier.get()); }}
12、Stream流式计算
什么是Stream流式计算
大数据:存储+计算
用集合、Mysql存储,本质就是存储东西的
计算都应该交给流来操作
java.util.stream
package Demo;import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIConversion;import java.util.Arrays;import java.util.List;import java.util.Locale;import java.util.stream.Stream;/** * 题目要求:一分钟完成次题,只能用一行代码事先 * 现在有5个用户!筛选! * 1、 ID必须是偶数 * 2、年龄大于23岁 * 3、用户名转为大写字母 * 4、用户名字母倒着排序 * 5、只输出一个用户 */public class Demo01 { public static void main(String[] args) { User u1 = new User(1,"a",21); User u2 = new User(2,"b",22); User u3 = new User(3,"c",23); User u4 = new User(4,"d",24); User u5 = new User(5,"e",25); //集合就是存储 List<User> list = Arrays.asList(u1, u2, u3, u4, u5); //计算交给Stream流 //lambda表达式、链式编程、函数式接口、Stream流式计算都包含了 list.stream().filter((u)->{return u.getId()%2==0;}) .filter(u->{return u.getAge()>23;}) .map(u->{return u.getName().toUpperCase();}) .sorted((uu1,uu2)->{return uu2.compareTo(uu1);}) .limit(1) .forEach(System.out::println); }}
13、ForkJoin
什么是ForkJoin
ForkJoin在jdk1.7出来,并行执行任务!提高效率,大数据量
大数据: Map Reduce(把大任务拆分为小人物)
ForkJoin特点:工作窃取
ForkJoin
package com.xiao;import java.util.concurrent.RecursiveTask;/** * 求和计算的任务 * 如何使用forkJoin * 1、forkjoinPool 通过它来执行 * 2、计算任务ForkJoinTask * 3、计算类要继承RecursiveTask */public class Demo extends RecursiveTask<Long> { private long start; private long end; //临界值 private long temp = 10000l; public Demo(long start, long end) { this.start = start; this.end = end; } //计算方法 @Override protected Long compute() { if ((end - start) < temp) { Long num = 0L; for (Long i = start; i < end; i++) { num += i; System.out.println(num); } } else { //分支合并计算 long middle = (start + end) / 2;//中间值 Demo task1 = new Demo(start, middle); task1.fork();//拆分任务,把任务压入线程队列 Demo task2 = new Demo(middle + 1, end); task1.fork();//拆分任务,把任务压入线程队列 return task1.join() + task2.join(); } return null; }}package com.xiao;import com.sun.javafx.scene.control.skin.TextAreaSkin;import java.util.concurrent.ExecutionException;import java.util.concurrent.ForkJoinPool;import java.util.concurrent.ForkJoinTask;import java.util.stream.LongStream;public class test { public static void main(String[] args) throws ExecutionException, InterruptedException { test3(); } public static void test1(){ //普通 //sum=时间838 long sum = 0; long start = System.currentTimeMillis(); for (long i = 1l; i < 1000000000l; i++) { sum+=i; } long end = System.currentTimeMillis(); System.out.println("sum="+sum+"时间"+(end-start)); } public static void test2() throws ExecutionException, InterruptedException { //使用forkjoin long start = System.currentTimeMillis(); ForkJoinPool forkJoinPool = new ForkJoinPool(); ForkJoinTask<Long> task = new Demo(0L,1000000000); ForkJoinTask<Long> submit = forkJoinPool.submit(task);//提交任务 Long sum = submit.get(); long end = System.currentTimeMillis(); System.out.println("sum="+sum+"时间"+(end-start)); } public static void test3(){ long start = System.currentTimeMillis(); //stream并行流 long sum = LongStream.range(0L, 1000000000L).parallel().reduce(0, Long::sum); long end = System.currentTimeMillis(); System.out.println("sum="+"时间"+(end-start)); }}
14、异步回调
j.u.c.future
Future 设计的初衷:对未来的某个事件的结果进行建模
package com.xiao.future;import java.util.concurrent.CompletableFuture;import java.util.concurrent.ExecutionException;import java.util.concurrent.Future;import java.util.concurrent.TimeUnit;/** * 异步调用:CompletableFuture * //异步执行 * //成功回调 * //失败回调 * */public class Demo01 { public static void main(String[] args) throws ExecutionException, InterruptedException {// //没有返回值的异步回调// CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{// try {// TimeUnit.SECONDS.sleep(2);// } catch (InterruptedException e) {// e.printStackTrace();// }// System.out.println(Thread.currentThread().getName()+"runAsync=>void");// });// System.out.println("1111");// completableFuture.get();//获取执行结果 //有返回值的异步回调 //ajax,成功和失败的回调 //返回的是错误信息 CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(()->{ System.out.println(Thread.currentThread().getName()+"sunAsync=>Integer"); int i = 10/0; return 1024; }); completableFuture.whenComplete((t,u)->{ System.out.println("t"+t);//正常的返回结果 System.out.println("u"+u);//打印错误信息 }).exceptionally((e)->{ System.out.println(e.getMessage()); return 233; }).get(); }}
15、JMM
请你谈谈你对Volatile的理解
Volatile是java虚拟机提供的轻量的同步机制
- 保证可见性
- 不保证原子性
- 禁止指令重排
那问题来了,怎么保证可见性?那就要和JMM有关联了
什么是JMM
JMM java内存模型,不存在的东西,是一个概念、约定!
关于JMM的一些同步的约定:
- 线程解锁前,必须把共享变量立刻刷会内存
- 线程加锁前,必须读取内存中的最新值到工作内存中
- 加锁和解锁是同一把锁
线程:工作内存、主内存
8种操作:
内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于double和long类型的变量来说,load、store、read和write操作在某些平台上允许例外)
-
lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
-
unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
-
read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用
-
load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
-
use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令
-
assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中
-
store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用
-
write (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中
JMM对这八种指令的使用,制定了如下规则:
-
不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须write
-
不允许线程丢弃他最近的assign操作,即工作变量的数据改变了之后,必须告知主存
-
不允许一个线程将没有assign的数据从工作内存同步回主内存
-
一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是怼变量实施use、store操作之前,必须经过assign和load操作
-
一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解锁
-
如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新load或assign操作初始化变量的值
-
如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量
-
对一个变量进行unlock操作之前,必须把此变量同步回主内存
问题:程序不知道住内存的值修改被修改过了
package com.xiao.TVolatile;import java.util.concurrent.TimeUnit;public class JMMDemo { private static int num = 0; public static void main(String[] args) { new Thread(()->{ while (num==0){ } }).start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } num=1; System.out.println(num); }}
16、Volatile
1.保证可见性
package com.xiao.TVolatile;import java.util.concurrent.TimeUnit;public class JMMDemo { //不加volatile 程序就会死循环 //加 volatile 可以保证可见性 private volatile static int num = 0; public static void main(String[] args) { new Thread(()->{//线程1 对住内存的变化不知道的 while (num==0){ } }).start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } num=1; System.out.println(num); }}
2.不保证原子性
原子性,不可分割
线程A在执行任务的时候,不能被打扰的,也不能被分割,要么同时成功,要么同时失败
package com.xiao.TVolatile;//不保证原子性public class VDemo02 { //Volatile private static int num = 0; public static void add(){ num++; } public static void main(String[] args) { //理论上num结果应该是20000 for (int i = 1; i <= 20 ; i++) { new Thread(()->{ for (int j = 0; j < 1000; j++) { add(); } }).start(); } while (Thread.activeCount()>2){//mian gc Thread.yield(); } System.out.println(Thread.currentThread().getName()+""+num); }}
如果不加lock和synchronized怎么样保证原子性?
使用原子类,解决原子性问题:在 j.u.c.a中的AtomicInteger
package com.xiao.TVolatile;import java.util.concurrent.atomic.AtomicInteger;//不保证原子性public class VDemo02 { //Volatile private static AtomicInteger num = new AtomicInteger(); public static void add(){ //num++;//不是一个原子性操作 num.getAndIncrement();//AtomicInter +1方法 CAS } public static void main(String[] args) { //理论上num结果应该是20000 for (int i = 1; i <= 20 ; i++) { new Thread(()->{ for (int j = 0; j < 1000; j++) { add(); } }).start(); } while (Thread.activeCount()>2){//mian gc Thread.yield(); } System.out.println(Thread.currentThread().getName()+""+num); }}
这些类的底层都直接喝操作系统挂钩!
getAndIncrement()不是单独的++,而是在内存中修改值。
指令重排
什么是指令重拍:你写的程序,计算机并不是按照你写的那样去执行的
源代码->编译器优化的重排->指令并行可能会重排->内存系统也可能会重排-> 执行
处理器在进行指令重排的时候,会考虑数据之间的依赖性!
Volatile可以避免重排:
内存屏障、CUP指令、作用:
- 保证特定的操作的执行顺序
- 可以保证某些变量的内存可见性(利用这些特性volatile实现了可见性)
就是如果加了Volatile,那么在这个上和下一层加一层内存屏障,就不会进行重排。
**小结:**Volatile可以保持可见性,不能保证原子性,由于内存屏障,可以保证避免指令重排的现象产生。
17、彻底玩转单例模式
懒汉式、DCL懒汉式
饿汉式
package com.xiao.single;
//饿汉式单例
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 byte[] data4 = new byte[1024*1024];
private Hungry(){
}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
懒汉式 DCL
package com.xiao.single;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
//懒汉式单例模式
public class LazyMan {
private static boolean xiao = false;
private LazyMan() {
synchronized (LazyMan.class){
if (xiao==false){
xiao = true;
}else {
throw new RuntimeException("不是试图使用反射破坏异常");
}
}
}
private volatile static LazyMan lazyMan;
//加volatile保证原子性
public static LazyMan getInstance() {
if (lazyMan == null) {//双重检测锁模式的懒汉式单例 简称DCL懒汉式 有问题的!
synchronized (LazyMan.class) {
if (lazyMan == null) {
lazyMan = new LazyMan();//不能保证原子性
}
}
}
//加锁
return lazyMan;
}
//反射
public static void main(String[] args) throws Exception {
//LazyMan instance = LazyMan.getInstance();
Field xiao = LazyMan.class.getDeclaredField("xiao");
xiao.setAccessible(true);
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
LazyMan instance = declaredConstructor.newInstance();
xiao.set(instance,false);
LazyMan instance2 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance2);
}
}
// //多线程并发
// public static void main(String[] args) {
// for (int i = 0; i < 10; i++) {
// new Thread(()->{
// LazyMan.getInstance();
// }).start();
// }
// }
//
//}
/**
* 1、分配内存空间
* 2、执行构造方法,初始化对象
* 3、把这个对象指向这个空间
*/
静态内部类
package com.xiao.single;
public class Holder {
private Holder(){
}
public static Holder getInstance(){
return InnerClass.HOLDER;
}
public static class InnerClass{
private static final Holder HOLDER = new Holder();
}
}
单例不安全,因为有反射
枚举:没有无参构造,有 有参构造
18、深入了解CAS
什么是CAS
package com.xiao.CAS;import java.util.concurrent.atomic.AtomicInteger;public class CASDemo { //CAS compareAndSet比较并交换 public static void main(String[] args) { AtomicInteger atomicInteger = new AtomicInteger(2020); //期望、更新 // public final boolean compareAndSet(int expect, int update) //如果我期望的值达到了,那么就更新,否则不更新 CAS是CPU的并发源语 System.out.println(atomicInteger.compareAndSet(2020, 2021)); System.out.println(atomicInteger.get()); System.out.println(atomicInteger.compareAndSet(2020, 2021)); System.out.println(atomicInteger.get()); }}
Unsafe类
java无法操作内存,但是可以调用c++、native,c++可以操作内存!java的后门,可以通过这个类操作内存
**CAS:**比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就一直循环。
缺点:
- 循环会耗时
- 一次性只能保证一个共享变量的原子性
- ABA问题
CAS : ABA问题(狸猫换太子)
package com.xiao.CAS;
import java.util.concurrent.atomic.AtomicInteger;
public class CASDemo {
//CAS compareAndSet比较并交换
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2020);
//对于我们平时写的sql来说:乐观锁
//期望、更新
// public final boolean compareAndSet(int expect, int update)
//如果我期望的值达到了,那么就更新,否则不更新 CAS是CPU的并发源语
//捣乱的线程
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(2021, 2020));
System.out.println(atomicInteger.get());
//期望的线程
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
}
}
19、原子引用
解决aba问题:引入原子引用
java.util.concurrent.atomic Class AtomicReference
带版本好的原子操作
注意:
integer使用了对象缓存机制,默认范围是-128~127,推荐使用静态工厂方法valueof获取对象实例,而不是new,因为valueof使用缓存,而new一定会创建新的对象分配新的内存空间。
package com.xiao.CAS;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicStampedReference;
public class CASDemo {
//CAS compareAndSet比较并交换
static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1,1);
public static void main(String[] args) {
//AtomicStampedReference注意,如果泛型是一个包装类注意对象的引用问题,不能超Integer这个范围,不然版本号这个数字就会刷新
new Thread(()->{
int stamp = atomicStampedReference.getStamp();//获得版本号
System.out.println("a1=>"+stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicStampedReference.compareAndSet(1, 2,
atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
System.out.println("a2=>"+atomicStampedReference.getStamp());
System.out.println(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());
},"B").start();
}
}
20、各种锁的理解
1、公平锁、非公平锁:
非常公平:不能插队,必须先来后到!
非常不公平:可以插队(默认都是非公平)
2、可重入锁:
可重入锁(递归锁):拿到了外面的锁,也就可以自动获得里面的锁
package com.xiao;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Demo02 {
public static void main(String[] args) {
phone phone = new phone();
new Thread(()->{
phone.sms();
},"a").start();
new Thread(()->{
phone.sms();
},"b").start();
}
}
class phone{
Lock lock = new ReentrantLock();
public void sms(){
lock.lock();//细节问题,两把钥匙 lock锁必须配对,否则就会死锁
try {
System.out.println(Thread.currentThread().getName()+"sms");
call();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void call(){
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+"call");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
package com.xiao.CAS;
//Synchronized
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");
}
}
3、自旋锁
自定义自旋锁
package com.xiao.CAS;
import java.util.concurrent.atomic.AtomicReference;
/**
* 自旋锁
*/
public class Demo01{
AtomicReference<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);
}
}
package com.xiao;
import com.xiao.CAS.Demo01;
import java.util.concurrent.TimeUnit;
public class Demo02 {
public static void main(String[] args) throws InterruptedException {
// ReentrantLock reentrantLock = new ReentrantLock();
// reentrantLock.lock();
// reentrantLock.unlock();
Demo01 lock = new Demo01();
new Thread(()->{
lock.myLock();
try {
TimeUnit.SECONDS.sleep(5);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.myunLock();
}
},"t1").start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
lock.myLock();
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.myunLock();
}
},"t2").start();
}
}
4、死锁
package com.xiao.CAS;
import java.util.concurrent.TimeUnit;
/**
*
*/
public class Demo01{
public static void main(String[] args) {
String lockA = "LOCKA";
String lockB = "LOCKB";
new Thread(new MyThread(lockB,lockA),"a").start();
new Thread(new MyThread(lockA,lockB),"b").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+"=>lockb");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockb){
System.out.println(Thread.currentThread().getName()+"lock"+locka+"=>locka");
}
}
}
}
解决问题
1、使用jps-l定位进程号
2、使用 jstack进程号查看进程信息