JUC与锁

什么是JUC

JUC并发的三个包:

JUC并发编程:并发、并行

并发(多线程操作同一个资源)  : CPU一核,模拟出来多条线程,天下武功,唯快不破,快速交替

并行(多个人一起走)  : CPU多核,多个线程可以同时执行

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

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

线程和进程:

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

线程:开了一个进程Typora,写字,自动保存(线程负责的)  对于Java而言:Thread、Runnable、Callable  Java无法开启线程   本质:调用底层的C++,Java无法直接操作硬件

线程状态://新生  NEW,  //运行  RUNNABLE, //阻塞  BLOCKED, //等待,死死地等  WAITING, //超时等待  TIMED_WAITING, //终止  TERMINATED;

wait/sleep区别:

1、来自不同的类 :   wait=>Object        sleep=>Thread

2、关于锁的释放 :  wait 会释放锁,sleep睡觉了,抱着锁睡觉,不会释放!

3、使用的范围是不同的 :  wait 必须在同步代码块中    sleep 可以在任何地方睡

4、是否需要捕获异常 : wait 需要捕获异常(中断异常)   sleep 必须要捕获异常

Lock锁(同步锁):

public class SaleTicketDemo02{
public static void main(String[] args) {
//并发:多线程操作同一个资源类,把资源类丢入线程
Ticket2 ticket = new Ticket2();
//@FunctionalInterface 函数式接口,jdk1.8 lambda表达式(参数)->{代码}
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、finally=> lock.unlock(); //解锁
class Ticket2 {
//属性、方法
private int number = 30;
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锁区别:

1、synchronized 内置的Java关键字,Lock 是一个Java类

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

3、synchronized 会自动释放锁,Lock 必须要手动释放锁!如果不释放锁,死锁

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

5、synchronized 可重入锁,不可以中断的,非公平;Lock 可重入锁,可以判断锁,非公平(可以自己设置);

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

生产者和消费者问题

public class B{
public static void main(String[] args) {
Data2 data = new Data2();
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.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{//数字 资源类
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();//通过Lock找到condition
//    condition.await();//等待
// condition.signalAll();//唤醒全部
//+1
public void increment() throws InterruptedException {
lock.lock();
try{
//业务代码
while (number!=0){
//等待
condition.await();
}
number++;
//通知其他线程,我+1完毕了
System.out.println(Thread.currentThread().getName() + "=>" + number);
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
//-1
public synchronized void decrement() throws InterruptedException {
lock.lock();
try{
while (number==0){
//等待
condition.await();
}
number--;
//通知其他线程,我-1完毕了
System.out.println(Thread.currentThread().getName() + "=>" + number);
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}

8锁现象

如何判断锁的是谁!永远的知道什么锁,锁到底锁的是谁! 对象、Class

/**
* 8锁,就是关于锁的8个问题
* 1、标准情况下,两个线程先打印 发短信还是 打电话?1/发短信 2/打电话
* 2、sendSms延迟4秒,两个线程先打印 发短信还是 打电话?1/发短信 2/打电话
*/
public class Test1 {
public static void main(String[] args) {
Phone phone = new Phone();
//锁的存在
new Thread(()->{
phone.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
},"B").start();
}
}
class Phone{
//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("打电话");
}
}
/**
* 3、增加了一个普通方法后!先执行发短信还是hello?普通方法
* 4、两个对象,两个同步方法, 发短信还是 打电话? //打电话
*/
public class Test2 {
public static void main(String[] args) {
//两个对象,两个调用者,两把锁!
Phone2 phone1 = new Phone2();
Phone2 phone2 = new Phone2();
//锁的存在
new Thread(()->{
phone1.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.call();
},"B").start();
}
}
class Phone2{
//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");
}
}
/**
* 5、增加两个静态的同步方法,只有一个对象,先打印发短信还是打电话? 发短信
* 6、两个对象增加两个静态的同步方法,只有一个对象,先打印发短信还是打电话? 发短信
*/
public class Test3 {
public static void main(String[] args) {
//两个对象的Class类模板只有一个,static,锁的是Class
Phone3 phone1 = new Phone3();
Phone3 phone2 = new Phone3();
//锁的存在
new Thread(()->{
phone1.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.call();
},"B").start();
}
}
//Phone3 唯一的一个class 对象
class Phone3{
//synchronized 锁的对象是方法的调用者!
//static 静态方法
//类一加载就有了!锁的是Class模板
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public static synchronized void call(){
System.out.println("打电话");
}
}
/**
* 7、1个静态的同步方法,1个普通的同步方法,一个对象,先打印发短信还是打电话? 打电话
* 8、1个静态的同步方法,1个普通的同步方法,两个对象,先打印发短信还是打电话? 打电话
*/
public class Test4 {
public static void main(String[] args) {
//两个对象的Class类模板只有一个,static,锁的是Class
Phone4 phone1 = new Phone4();
Phone4 phone2 = new Phone4();
//锁的存在
new Thread(()->{
phone1.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.call();
},"B").start();
}
}
//Phone3 唯一的一个class 对象
class Phone4{
//静态的同步方法
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("打电话");
}
}

读写锁:读可以被多个线程同时读,写的时候只能用一个线程去写

实现类:ReentrantReadWriteLock

A ReadWriteLock维护一对关联的locks ,一个用于只读操作,一个用于写入。 read lock可以由多个阅读器线程同时进行,只要没有作者。 write lock是独家的。

/**
* 独占锁(写锁)一次只能被一个线程占有
* 共享锁(读锁)多个线程可以同时占有
* 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");
}
}

可重入锁(递归锁)

public class Demo02  {
public static void main(String[] args) {
Phone2 phone = new Phone2();
new Thread(()->{
phone.sms();
},"A").start();
new Thread(()->{
phone.sms();
},"B").start();
}
}
class Phone2{
Lock lock = new ReentrantLock();
public void sms(){
lock.lock();//细节问题:lock.lock(); lock.unlock();//lock 锁必须配对,否则就会死在里面
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "sms");
call();//这里也有锁
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
lock.unlock();
}
}
public synchronized void call(){
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "call");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}

自旋锁

/**
* 自旋锁
*/
public class SpinlockDemo {
//int 0
//Thread null
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);
}
}
public class TestSpinLock {
public static void main(String[] args) {
// ReentrantLock reentrantLock = new ReentrantLock();
// reentrantLock.lock();
// reentrantLock.unlock();
//底层使用的自旋锁CAS
SpinlockDemo lock = new SpinlockDemo();
new Thread(()->{
lock.myLock();
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.myUnLock();
}
},"T1").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
lock.myLock();
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.myUnLock();
}
},"T2").start();
}
}

死锁(四要数)

public class DeadLockDemo {
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);

}
}
}
}

解决问题

1、使用 'jps -l' 定位进程号

2、使用' jstack 进程号' 找到死锁问题

排查死锁问题: 1、日志    2、堆栈

posted @ 2021-04-02 23:03  星忄守候  阅读(49)  评论(0)    收藏  举报