JUC并发编程【java提高】1

简介: JUC并发编程【java提高】1

JUC并发编程

准备工作

1.新建maven项目juc

导入lombok

<dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.8</version>
        </dependency>
    </dependencies>


2.确定jdk8工作环境




1、什么是JUC

源码+官方文档

JUC就是java.util.concurrent下面的类包,专门用于多线程的开发。

java.util 工具包、包、分类

业务:普通的线程代码 Thread

Runnable 没有返回值、效率相比于Callable相对较低!

2、线程和进程

线程、进程,如果不能使用一句话说出来的,技术不扎实


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


1)进程

一个程序,QQ.exe Music.exe 程序的集合;

一个进程往往可以包含多个线程,至少包含一个!

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


2)线程

开了一个进程 Typora,写字,自动保存(线程负责的)


对于Java而言:Thread、Runable、Callable进行开启线程的。

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

  public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();
        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);
        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }
    private native void start0();

Java是没有权限去开启线程、操作硬件的,这是一个native的一个本地方法,它调用的底层的C++代码。


并发、并行


并发编程:并发(多线程操作同一资源)


CPU 一核,模拟出来多条线程,天下武功,唯快不破,快速交替

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

并行(多个人一起行走)


CPU多核,多个线程可以同时执行。 我们可以使用线程池!

获取cpu的核数

package com.kuang.demo01;
public class Test1 {
    public static void main(String[] args) {
        //获取cpu的核数
        System.out.println(Runtime.getRuntime().availableProcessors());
    }
}

线程有几个状态

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

wait/sleep区别

1、来自不同的类

wait => Object

sleep => Thread

一般情况企业中使用休眠是:

TimeUnit.DAYS.sleep(1); //休眠1天
TimeUnit.SECONDS.sleep(1); //休眠1s

2、关于锁的释放

wait 会释放锁;

sleep睡觉了,不会释放锁;
3、使用的范围是不同的

wait 必须在同步代码块中;

sleep 可以在任何地方睡;
4、是否需要捕获异常

wait是不需要捕获异常;

sleep必须要捕获异常;

3、Lock锁(重点)

传统 Synchronized

package com.kuang.demo01;
//基本的卖票例子
/**
 * 真正的多线程开发,公司中的开发
 * 线程就是一个资源类,没有任何附属的操作
 */
public class SaleTicketDemo01 {
    public static void main(String[] args) {
        //并发,多线程操作同一个资源类,把资源类丢入线程
        Ticket ticket=new Ticket();
        //@FunctionalInterface 函数式接口,jdk1.8 Lambdab表达式(参数)->{代码}
        new Thread(()->{
            for(int i=1;i<40;i++){
                ticket.sale();
            }
        },"A").start();
        new Thread(()->{
            for(int i=1;i<40;i++){
                ticket.sale();
            }
        },"B").start();
        new Thread(()->{
            for(int i=1;i<40;i++){
                ticket.sale();
            }
        },"C").start();
    }
}
//资源类 OOP
class Ticket{
    //属性、方法
    private int number=50;
    //卖票的方式
    public void sale() {
        if (number>0) {
            System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余:"+number);
        }
    }
}

修改

 //卖票的方式
    public synchronized void sale() {
        if (number>0) {
            System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余:"+number);
        }
    }

lock



公平锁:十分公平,先来后到,排队.

非公平锁:不公平,可以插队(默认)

默认是非公平锁,是为了公平,比如一个线程要3s,另一个线程要3h,难道一定要让3h的锁先来就先执行吗?!

package com.kuang.demo01;
//基本的卖票例子
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * 真正的多线程开发,公司中的开发
 * 线程就是一个资源类,没有任何附属的操作
 */
public class SaleTicketDemo02 {
    public static void main(String[] args) {
        //并发,多线程操作同一个资源类,把资源类丢入线程
        Ticket2 ticket=new Ticket2();
        //@FunctionalInterface 函数式接口,jdk1.8 Lambdab表达式(参数)->{代码}
        new Thread(()->{ for(int i=1;i<40;i++) ticket.sale(); },"A").start();
        new Thread(()->{ for(int i=1;i<40;i++) ticket.sale(); },"B").start();
        new Thread(()->{ for(int i=1;i<40;i++) ticket.sale(); },"C").start();
    }
}
// Lock
class Ticket2{
    //属性、方法
    private int number=50;
    Lock lock=new ReentrantLock();
    //卖票的方式
    public synchronized void sale() {
        lock.lock();//加锁
        try {
            //业务代码
            if (number>0) {
                System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余:"+number);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();//解锁
        }
    }
}

Synchronized 与Lock 的区别


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


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


3、Synchronized 会自动释放锁,lock必须要手动加锁和手动释放锁!可能会遇到死锁


4、Synchronized 线程1(获得锁->阻塞)、线程2(等待);lock就不一定会一直等待下去,lock会有一个trylock去尝试获取锁,不会造成长久的等待。


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

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

锁是什么,如何判断锁的是谁

4、生产者和消费者问题

面试高频: 单例模式, 八大排序,生产者消费者,死锁

生产者和消费者问题 Synchronzied 版

package com.kuang.pc;
/**
 * 线程之间的通信问题:生产者和消费者问题!  等待唤醒 通知唤醒
 * 线程交替执行 A B 操作同一个变量 num=0
 * A num+1
 * B num-1
 */
public class A {
    public static void main(String[] args) {
        Data data=new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
    }
}
//等待 业务 通知
class Data{//数字 资源类
    private int number=0;
    //+1
    public synchronized void increment() throws InterruptedException {
        if(number!=0){//0
            //等待
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        //通知其他线程,我+1完毕了
        this.notify();
    }
    //-1
    public synchronized void decrement() throws InterruptedException {
        if(number==0){//1
            //等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        //通知其他线程,我+1完毕了
        this.notify();
    }
}

问题存在,A B C D 4个线程 虚假唤醒

解决方式 ,if 改为while即可,防止虚假唤醒


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


这也就是为什么用while而不用if的原因了,因为线程被唤醒后,执行开始的地方是wait之后


自己的见解

比如超市要抓小偷,

小偷偷了100个东西,

if就是你让小偷只走一次防盗检测门,它报警了,小偷成功的偷走了99个东西

while就是让小偷再走一次,直到不报警

package com.kuang.pc;
/**
 * 线程之间的通信问题:生产者和消费者问题!  等待唤醒 通知唤醒
 * 线程交替执行 A B 操作同一个变量 num=0
 * A num+1
 * B num-1
 */
public class A {
    public static void main(String[] args) {
        Data data=new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
        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{//数字 资源类
    private int number=0;
    //+1
    public synchronized void increment() throws InterruptedException {
        while(number!=0){//0
            //等待
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        //通知其他线程,我+1完毕了
        this.notify();
    }
    //-1
    public synchronized void decrement() throws InterruptedException {
        while(number==0){//1
            //等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        //通知其他线程,我+1完毕了
        this.notify();
    }
}

JUC版的生产者消费者:Clock 版



代码实现

package com.kuang.pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * 线程之间的通信问题:生产者和消费者问题!  等待唤醒 通知唤醒
 * 线程交替执行 A B 操作同一个变量 num=0
 * A num+1
 * B num-1
 */
public class B {
    public static void main(String[] args) {
        Data2 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();
//        condition.await();//等待
//        condition.signalAll();//唤醒全部
    //+1
    public  void increment() throws InterruptedException {
        lock.lock();
        try {
            //业务代码
            while(number!=0){//0
                //等待
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName()+"=>"+number);
            //通知其他线程,我+1完毕了
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    //-1
    public void decrement() throws InterruptedException {
        lock.lock();
        try {
            //业务代码
            while(number==0){//1
                //等待
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName()+"=>"+number);
            //通知其他线程,我+1完毕了
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

任何一个新技术,绝对不仅仅只是覆盖了原来的技术,优势和补充!

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

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

代码测试:

package com.kuang.pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * Description:
 * A 执行完 调用B
 * B 执行完 调用C
 * C 执行完 调用A
 *
 * @author CSDN@日星月云
 * @date 11:31:18 2022年7月13日
 **/
public class C {
    public static void main(String[] args) {
        Data3 data=new Data3();
        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 Data3{// 资源类 lock
    private Lock lock=new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
    private int number=1;// 1A 2B 3C
    public  void printA()  {
        lock.lock();
        try {
            //业务,判断->执行->通知
            while (number!=1){
                //等待
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName()+"=>AAAAAA");
            //唤醒,唤醒指定的人,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()+"=>BBBBBB");
            //唤醒,唤醒指定的人,C
            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()+"=>CCCCCC");
            //唤醒,唤醒指定的人,A
            number=1;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

信号量机制 Semaphore 实现
经典 生产者-消费者线程【操作系统】

5、8锁现象

如何判断锁的是谁!锁到底锁的是谁?

锁会锁住:对象、Class

深刻理解我们的锁

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

package com.kuang.lock8;
import java.util.concurrent.TimeUnit;
/**
 * 8锁就,就是关于锁的8个问题
 * 1、标准情况下,两个线程先打印 发短信 还是 打电话 ?  1/发短信  2/打电话
 */
public class Test1 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(() -> { phone.sendSMs(); }).start();
        //捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> { phone.call(); }).start();
    }
}
class Phone {
    public synchronized void sendSMs() {
        System.out.println("发短信");
    }
    public synchronized void call() {
        System.out.println("打电话");
    }
}

结果:

 1/发短信  2/打电话

为什么? 如果你认为是顺序在前? 这个答案是错误的!

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

package com.kuang.lock8;
import java.util.concurrent.TimeUnit;
/**
 * 8锁就,就是关于锁的8个问题
 * 1、标准情况下,两个线程先打印 发短信 还是 打电话 ?  1/发短信  2/打电话
 * 2、sendSMs延迟4秒,两个线程先打印 发短信 还是 打电话 ?  (4s) 1/发短信  2/打电话
 */
public class Test1 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        //锁的存在
        new Thread(() -> { phone.sendSMs(); }).start();
        //捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> { phone.call(); }).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("打电话");
    }
}

结果:

(4s) 1/发短信  2/打电话

原因:并不是顺序执行,而是synchronized 锁住的对象是方法的调用!对于两个方法用的是同一个锁,谁先拿到谁先执行,另外一个等待

3、增加一个普通方法后!两个线程先打印 发短信 还是 hello ?

package com.kuang.lock8;
import java.util.concurrent.TimeUnit;
/**
 * 3、增加一个普通方法后!两个线程先打印 发短信 还是 hello ? (1s) 1/hello (4s) 2/发短信
 */
public class Test2 {
    public static void main(String[] args) {
        Phone2 phone = new Phone2();
        //锁的存在
        new Thread(() -> { phone.sendSMs(); }).start();
        //捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> { phone.hello(); }).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");
    }
}

结果:

 (1s) 1/hello (4s) 2/发短信

4、两个对象,两个同步方法,先打印 发短信 还是 打电话 ?

package com.kuang.lock8;
import java.util.concurrent.TimeUnit;
/**
 * 3、增加一个普通方法后!两个线程先打印 发短信 还是 hello ? (1s) 1/hello (4s) 2/发短信
 * 4、两个对象,两个同步方法,先打印 发短信 还是 打电话 ?    (4s) 1/发短信 2/打电话
 */
public class Test2 {
    public static void main(String[] args) {
        //两个对象,两个调用者,两个锁
        Phone2 phone1 = new Phone2();
        Phone2 phone2 = new Phone2();
        //锁的存在
        new Thread(() -> { phone1.sendSMs(); }).start();
        //捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> { phone2.call(); }).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");
    }
}

结果:

(4s) 1/发短信 2/打电话

5、增加两个静态的同步方法,只有一个对象,先打印 发短信 还是 打电话 ?

package com.kuang.lock8;
import java.util.concurrent.TimeUnit;
/**
 * 5、增加两个静态的同步方法,只有一个对象,先打印 发短信 还是 打电话 ? (4s) 1/发短信 2/打电话
 */
public class Test3 {
    public static void main(String[] args) {   
        Phone3 phone = new Phone3();
        //锁的存在
        new Thread(() -> { phone.sendSMs(); }).start();
        //捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> { phone.call(); }).start();
    }
}
//Phone唯一放入一个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("打电话");
    }
}

结果:

(4s) 1/发短信 2/打电话

6、两个对象!增加两个静态的同步方法,只有一个对象,先打印 发短信 还是 打电话 ?

package com.kuang.lock8;
import java.util.concurrent.TimeUnit;
/**
 * 5、增加两个静态的同步方法,只有一个对象,先打印 发短信 还是 打电话 ? (4s) 1/发短信 2/打电话
 * 6、两个对象!增加两个静态的同步方法,只有一个对象,先打印 发短信 还是 打电话 ?  (4s) 1/发短信 2/打电话
 */
public class Test3 {
    public static void main(String[] args) {
        //两个对象的Class类模板只有一个,static,锁的是Class
        Phone3 phone1 = new Phone3();
        Phone3 phone2 = new Phone3();
        //锁的存在
        new Thread(() -> { phone1.sendSMs(); }).start();
        //捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> { phone2.call(); }).start();
    }
}
//Phone唯一放入一个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("打电话");
    }
}

结果:

(4s) 1/发短信 2/打电话

7、1个静态同步方法,1个普通同步方法,一个对象,先打印 发短信 还是 打电话 ?

package com.kuang.lock8;
import java.util.concurrent.TimeUnit;
/**
 * 7、1个静态同步方法,1个普通同步方法,一个对象,先打印 发短信 还是 打电话 ? (1s)1/打电话(4s) 2/发短信
 */
public class Test4 {
    public static void main(String[] args) {
        Phone4 phone = new Phone4();
        //锁的存在
        new Thread(() -> { phone.sendSMs(); }).start();
        //捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> { phone.call(); }).start();
    }
}
//Phone唯一放入一个class对象
class Phone4 {
    //静态同步方法 锁的是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("打电话");
    }
}

结果:

(1s)1/打电话(4s) 2/发短信 

8、1个静态同步方法,1个普通同步方法,两个对象,先打印 发短信 还是 打电话 ?

package com.kuang.lock8;
import java.util.concurrent.TimeUnit;
/**
 * 7、1个静态同步方法,1个普通同步方法,一个对象,先打印 发短信 还是 打电话 ? (1s)1/打电话(4s) 2/发短信
 * 8、1个静态同步方法,1个普通同步方法,两个对象,先打印 发短信 还是 打电话 ? (1s)1/打电话(4s) 2/发短信
 */
public class Test4 {
    public static void main(String[] args) {
        Phone4 phone1 = new Phone4();
        Phone4 phone2 = new Phone4();
        //锁的存在
        new Thread(() -> { phone1.sendSMs(); }).start();
        //捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> { phone2.call(); }).start();
    }
}
//Phone唯一放入一个class对象
class Phone4 {
    //静态同步方法 锁的是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("打电话");
    }
}

结果:

(1s)1/打电话(4s) 2/发短信 

小结

new 出来的 this 是具体的一个对象

static Class 是唯一的一个模板

6、集合类不安全

list 不安全

package com.kuang.unsafe;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
//java.util.ConcurrentModificationException 并发修改异常
public class ListTest {
    public static void main(String[] args) {
        List<String> list=new ArrayList<>();
        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();
        }
    }
}


解决方案:

package com.kuang.unsafe;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
//java.util.ConcurrentModificationException 并发修改异常
public class ListTest {
    public static void main(String[] args) {
        //并发下 ArrayList 不安全的
        /**
         * 解决方案:
         * 1、 List<String> list=new Vector<>();
         * 2、 List<String> list= Collections.synchronizedList(new ArrayList<>());
         * 3、 List<String> list= new CopyOnWriteArrayList<>();
         */
        //CopyOnWrite写入是复制 COW 计算机程序设计领域的一种优化策略;
        //多个线程调用的时候,list,读取的时候,固定的,写入(覆盖)
        //在写入的时候,避免覆盖,造成数据问题
        //读写分离
        //CopyOnWriteArrayList 比 Vector 牛逼在哪里
        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();
        }
    }
}
相关文章
|
1月前
|
Java 编译器 开发者
深入理解Java内存模型(JMM)及其对并发编程的影响
【9月更文挑战第37天】在Java的世界里,内存模型是隐藏在代码背后的守护者,它默默地协调着多线程环境下的数据一致性和可见性问题。本文将揭开Java内存模型的神秘面纱,带领读者探索其对并发编程实践的深远影响。通过深入浅出的方式,我们将了解内存模型的基本概念、工作原理以及如何在实际开发中正确应用这些知识,确保程序的正确性和高效性。
|
2月前
|
安全 Java API
JAVA并发编程JUC包之CAS原理
在JDK 1.5之后,Java API引入了`java.util.concurrent`包(简称JUC包),提供了多种并发工具类,如原子类`AtomicXX`、线程池`Executors`、信号量`Semaphore`、阻塞队列等。这些工具类简化了并发编程的复杂度。原子类`Atomic`尤其重要,它提供了线程安全的变量更新方法,支持整型、长整型、布尔型、数组及对象属性的原子修改。结合`volatile`关键字,可以实现多线程环境下共享变量的安全修改。
|
26天前
|
存储 消息中间件 安全
JUC组件实战:实现RRPC(Java与硬件通过MQTT的同步通信)
【10月更文挑战第9天】本文介绍了如何利用JUC组件实现Java服务与硬件通过MQTT的同步通信(RRPC)。通过模拟MQTT通信流程,使用`LinkedBlockingQueue`作为消息队列,详细讲解了消息发送、接收及响应的同步处理机制,包括任务超时处理和内存泄漏的预防措施。文中还提供了具体的类设计和方法实现,帮助理解同步通信的内部工作原理。
JUC组件实战:实现RRPC(Java与硬件通过MQTT的同步通信)
|
8天前
|
存储 缓存 安全
Java内存模型(JMM):深入理解并发编程的基石####
【10月更文挑战第29天】 本文作为一篇技术性文章,旨在深入探讨Java内存模型(JMM)的核心概念、工作原理及其在并发编程中的应用。我们将从JMM的基本定义出发,逐步剖析其如何通过happens-before原则、volatile关键字、synchronized关键字等机制,解决多线程环境下的数据可见性、原子性和有序性问题。不同于常规摘要的简述方式,本摘要将直接概述文章的核心内容,为读者提供一个清晰的学习路径。 ####
25 2
|
2月前
|
存储 缓存 安全
【Java面试题汇总】多线程、JUC、锁篇(2023版)
线程和进程的区别、CAS的ABA问题、AQS、哪些地方使用了CAS、怎么保证线程安全、线程同步方式、synchronized的用法及原理、Lock、volatile、线程的六个状态、ThreadLocal、线程通信方式、创建方式、两种创建线程池的方法、线程池设置合适的线程数、线程安全的集合?ConcurrentHashMap、JUC
【Java面试题汇总】多线程、JUC、锁篇(2023版)
|
2月前
|
Java 开发者
深入探索Java中的并发编程
本文将带你领略Java并发编程的奥秘,揭示其背后的原理与实践。通过深入浅出的解释和实例,我们将探讨Java内存模型、线程间通信以及常见并发工具的使用方法。无论是初学者还是有一定经验的开发者,都能从中获得启发和实用的技巧。让我们一起开启这场并发编程的奇妙之旅吧!
30 5
|
2月前
|
算法 安全 Java
Java中的并发编程是如何实现的?
Java中的并发编程是通过多线程机制实现的。Java提供了多种工具和框架来支持并发编程。
18 1
|
2月前
|
缓存 监控 Java
Java中的并发编程:理解并应用线程池
在Java的并发编程中,线程池是提高应用程序性能的关键工具。本文将深入探讨如何有效利用线程池来管理资源、提升效率和简化代码结构。我们将从基础概念出发,逐步介绍线程池的配置、使用场景以及最佳实践,帮助开发者更好地掌握并发编程的核心技巧。
|
5月前
|
Java C++
关于《Java并发编程之线程池十八问》的补充内容
【6月更文挑战第6天】关于《Java并发编程之线程池十八问》的补充内容
49 5
|
4月前
|
安全 Java 开发者
Java中的并发编程:深入理解线程池
在Java的并发编程中,线程池是管理资源和任务执行的核心。本文将揭示线程池的内部机制,探讨如何高效利用这一工具来优化程序的性能与响应速度。通过具体案例分析,我们将学习如何根据不同的应用场景选择合适的线程池类型及其参数配置,以及如何避免常见的并发陷阱。
55 1