JUC并发编程(第一部分)

JUC并发编程(第一部分)

准备工作

创建一个maven工程,然后检查并修改以下配置在这里插入图片描述

1.JUC

学习方式:源码+官方文档
jdk参考文档在这里插入图片描述
业务:普通的线程代码
Runnable:没有返回值, 效率比Callable相对较低
Callable接口在这里插入图片描述

在这里插入图片描述

2.线程和进程

进程:是一个程序的集合,一个进程可以包含多个线程。
java默认的线程:默认线程有两个 main线程,和GC线程
java可以开启一个线程吗?
答案是:不能。
源码

 public Thread(ThreadGroup group, Runnable target, String name,
                  long stackSize, boolean inheritThreadLocals) {
        this(group, target, name, stackSize, null, inheritThreadLocals);
    }

    /**
     * Causes this thread to begin execution; the Java Virtual Machine
     * calls the {@code run} method of this thread.
     * <p>
     * The result is that two threads are running concurrently: the
     * current thread (which returns from the call to the
     * {@code start} method) and the other thread (which executes its
     * {@code run} method).
     * <p>
     * It is never legal to start a thread more than once.
     * In particular, a thread may not be restarted once it has completed
     * execution.
     *
     * @throws     IllegalThreadStateException  if the thread was already started.
     * @see        #run()
     * @see        #stop()
     */
    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 */
            }
        }
    }

有native方法,调用底层的c++ java实在虚拟机上面运行的
private native void start0();

3.并发,并行

并发编程:并发,并行
并发:多线程操作同一资源
cpu一核:模拟多线程,快速交替
并行:多个人一起走
cpu多核:多个线程同时执行,线程池

利用代码获取本机cpu


package com.zzuli;

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

        //查看线程有几种状态
            Thread.State    //这是一个枚举类型
    }
}

并发编程的本质就是:提高CPU的利用率

4.简单回顾多线程

线程有几种状态
6中状态

以下是源码

public enum State {
     //新生
        NEW,

//运行
        RUNNABLE,
//阻塞
        BLOCKED,

 //等待
        WAITING,
//超时等待
      
        TIMED_WAITING,

//终止

 TERMINATED;
       
    }

wait/sleep的区别
来自不同的类
1,wait属于Object类
2,sleep属于Thread类

关于锁的释放
wait会释放锁,sleep不会释放锁

使用范围不同
wait必须再同步代码块中
sleep可以再任何地方

是否需要捕获异常

wait不需要捕获异常
sleep需要捕获异常

5.Lock锁

//使用线程 进行卖票操作实现使用传统的synchronized

真正的多线程开发,公司的开发,降低耦合性 线程就是一个单独的资源类,没有任何附属的操作
1,属性,方法

public class saleStaticDemo02 {

    public static void main(String[] args) {
        //并发,多个线程操作同一个资源类
        Ticket ticket=new Ticket();
        //lambda表达式    (参数)->{代码}
        new Thread(()->{
            for (int i=1; i<60;i++){
                ticket.sale();
            }
        }).start();
        new Thread(()->{
            for (int i=1; i<60;i++){
                ticket.sale();
            }
        }).start();
        new Thread(()->{
            for (int i=1; i<60;i++){
                ticket.sale();
            }
        }).start();
    }

}
//资源类  oop
class  Ticket{
    //属性
    private int number=50;
    //买票的方式
    public synchronized  void sale(){
        if (number>0){
            System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "票,剩余" + number);
        }
    }
        }

Lock接口在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
公平锁:十分公平,先到先执行
非公平锁:不公平,可以插队(默认开启)

lock锁使用三部曲 (使用步骤)
1,创建实现类 new ReentrantLock();
2, 在业务方法中加锁
3,在finally中解锁

代码实现

package com.zzuli;

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

public class saleTiketDemo03 {

    public static void main(String[] args) {
        //并发,多个线程操作同一个资源类
        Ticket2 ticket=new Ticket2();
        //lambda表达式    (参数)->{代码}
        new Thread(()->{
            for (int i=1; i<60;i++){
                ticket.sale();
            }
        }).start();
        new Thread(()->{
            for (int i=1; i<60;i++){
                ticket.sale();
            }
        }).start();
        new Thread(()->{
            for (int i=1; i<60;i++){
                ticket.sale();
            }
        }).start();
    }
}



//资源类  oop
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);
            }

        }finally {

            //解锁
            lock.unlock();

        }
    }
}

使用lock和synchronzied同样都可以实现同步操作,他们的区别是什么

1,Synchronzied, 是内置的关键字,Lock是一个java类
2,Synchronzied,无法判断获取锁的状态,Lock可以判断是否获取到了锁,
3,Synchronzied,会自动释放锁,Lock必须手动释放锁,如果不释放锁,就会产生“死锁”
4,Synchronzied,线程1(获得锁,阻塞),线程2(等待,傻傻的等);Lock锁就不一定会一直等下去
5,Synchronzied,可重入锁,不可以中断的,是非公平锁,Lock,可重入锁,可以判断锁,默认为非公平锁(可以修改)
6,Synchronzied,适合锁少量的代码同步问题,Lock适合大量的同步代码

使用Synchronzied 实现生产者消费者问题,并由那些存在的问题

package com.zzuli.proCon;

/*
* 使用 synchronrized   实现生产者 ---- 消费者   模式
*
*
* 线程之间的通信问题
* */

public class Productor {
    public static void main(String[] args) {
        //创建共享对象
        Data data=new Data();

        //使用lamabda表达式开启线程对象
        /*
        * 开启第一个线程*/
        new Thread(()->{
            for (int  i=1;i<10;i++){
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }).start();
        /*
        * 开启第二线程*/

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

//这是一个资源类
class Data{

    //共享的资源数据
    private int number;

    //业务方法,提供者
    public synchronized  void increment() throws InterruptedException {

        if (number!=0){
            //说明共享资源还有东西,不需要,提供,所以当前线程对象进入的等待状态
            this.wait();
        }

        //当程序走到走到这一步,说明number==0,所以需要提供者提供资源
        number++;
        this.notifyAll();
        System.out.println(Thread.currentThread().getName() + "--->" + number);
    }


    //业务方法,消费者
    public synchronized void decrement() throws InterruptedException {
        if (number==0){
            //说明共享资源没有东西,不能继续消费了,所以当前线程对象进入的等待状态
            this.wait();
        }
        //当程序走到走到这一步,说明number!=0,所以可以继续消费
        number--;
        this.notifyAll();
        System.out.println(Thread.currentThread().getName() + "--->" + number);
    }


}


在这里插入图片描述

存在的问题

以上代码线程是两个线程不会有问题,如果线程是四个或者多个,就会产生虚假唤醒,所以根据官方文档,要将if全部改为while,因为if只会判断一次

修改之后的代码,开启四个线程,并没有发生虚假唤醒的情况

package com.zzuli.proCon;

/*
* 使用 synchronrized   实现生产者 ---- 消费者   模式
*
*
* 线程之间的通信问题
* */


public class Productor {
    public static void main(String[] args) {
        //创建共享对象
        Data data=new Data();

        //使用lamabda表达式开启线程对象

        /*i
        * 开启第一个线程*/
        new Thread(()->{
            for (int  i=1;i<10;i++){
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }).start();

        /*
        * 开启第二线程*/

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

            }
        }).start();
        
        /*
         * 开启第三线程*/
        new Thread(()->{
            for (int  i=1;i<10;i++){
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }).start();
        
        /*
         * 开启第四线程*/
        new Thread(()->{
            for (int  i=1;i<10;i++){
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }).start();
    }
}

//这是一个资源类
class Data{

    //共享的资源数据
    private int number;

    //业务方法,提供者
    public synchronized  void increment() throws InterruptedException {

        while (number!=0){
            //说明共享资源还有东西,不需要,提供,所以当前线程对象进入的等待状态
            this.wait();
        }

        //当程序走到走到这一步,说明number==0,所以需要提供者提供资源
        number++;
        this.notifyAll();
        System.out.println(Thread.currentThread().getName() + "--->" + number);
    }


    //业务方法,消费者
    public synchronized void decrement() throws InterruptedException {
        while (number==0){
            //说明共享资源没有东西,不能继续消费了,所以当前线程对象进入的等待状态
            this.wait();
        }
        //当程序走到走到这一步,说明number!=0,所以可以继续消费
        number--;
        this.notifyAll();
        System.out.println(Thread.currentThread().getName() + "--->" + number);
    }


}

使用JUC 实现生产者消费者问题

在这里插入图片描述
通过Lock找到 Condition(官方文档)在这里插入图片描述

使用JUC实现生产者消费者代码如下

package com.zzuli.lock;

/*
* 使用lock版锁实现生产者和消费者*/


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

public class LockPC {

    public static void main(String[] args) {
        //创建共享对象
       Data data1=new Data();

        //使用lamabda表达式开启线程对象
        new Thread(()->{
            for (int i=1;i<10;i++) {
                data1.increment();
            }
        }).start();



          new Thread(()->{
              for (int i=1;i<10;i++) {
                  data1.decrement();
              }
          }).start();



        new Thread(()->{
            for (int i=1;i<10;i++) {
                data1.increment();
            }
        }).start();


        new Thread(()->{
            for (int i=1;i<10;i++) {
                data1.decrement();
            }
        }).start();

    }

}




class Data{

    //共享的资源数据
    private int number;
    //创建lock锁对象
      Lock lock=new ReentrantLock();
      //通过lock可以找到 condition
    Condition condition=lock.newCondition();

    //condition .await()   等待
    //condition.signalAll() 唤醒全部


    //业务方法提供者
    public void increment(){
        //在执行业务方法之前要使用lock进行上锁
        //为了保证一定释放锁,所以一般就用 try/finally   将业务方法包裹起来,在finally进行释放锁
        lock.lock();
        try{
            //编写业务逻辑代码
            while (number!=0) {
                //程序走到这里说明仓库还有资源,当前前程进入阻塞状态
                condition.await();
            }
            //程序走到这一步,说明number=0需要提供者进行提供
            number++;
            //活像所有其他线程
            condition.signalAll();
            System.out.println(Thread.currentThread().getName()+"----->"+number);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //将锁释放
            lock.unlock();
        }
    }




    //业务方法的消费者
    public void decrement(){
        //首先对业务方法进行上锁
        lock.lock();
        try {

            //添加业务方法
            while(number==0){
                //仓库没有东西了,进入等待状态
                condition.await();
            }
            //程序在这一不说明仓库还有资源,可以进行消费
            number--;
            condition.signalAll();
            System.out.println(Thread.currentThread().getName()+"----->"+number);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {

            //释放锁
            lock.unlock();
        }
    }

}

在这里插入图片描述
我们可以利用conditon的特性,指定哪一个线程开启(唤醒),来完成线程的有序执行

package com.zzuli.condition;

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

public class OrderLock {
    public static void main(String[] args) {
        DataOL data2=new DataOL();


        //开启三个线程,每一个线程都对应一个方法

        new Thread(()->{
            for (int i=1;i<10;i++){
                data2.printA();
            }
        }).start();

        new Thread(()->{
            for (int i=1;i<10;i++){
                data2.printB();
            }
        }).start();

        new Thread(()->{
            for (int i=1;i<10;i++){
                data2.printC();
            }
        }).start();

    }





}


//资源类,低耦合,oop
class DataOL{

    //创建一个共享的数据
    private Integer unmber=1;
    //创建lock锁
    Lock lock=new ReentrantLock();
    //通过lock锁来得到  condition
    Condition condition1=lock.newCondition();
    Condition condition2=lock.newCondition();
    Condition condition3=lock.newCondition();


    //创建打印A的方法

    public void   printA(){
        //给业务方法上锁
        lock.lock();
        try {

            while (unmber!=1){
                //轮不到condition1来进行打印,当前线程进入等待
                condition1.await();
            }
            //程序走到这一步说明此时的number等于A,直接打印就行了
            System.out.println(Thread.currentThread().getName()+"==>"+"AAAAAAAAAAAAAAAAAA");
            //此时将number+1  指定condition2来打印
            unmber=unmber+1;
            //唤醒condition2
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //释放锁
            lock.unlock();
        }
    }





    public void   printB(){
        //给业务方法上锁
        lock.lock();
        try {

            while (unmber!=2){
                //轮不到condition2来进行打印,当前线程进入等待
                condition2.await();
            }
            //程序走到这一步说明此时的number等于2,直接打印就行了
            System.out.println(Thread.currentThread().getName()+"==>"+"BBBBBBBBBBBBBB");
            //此时将number+1  指定condition3来打印
            unmber=3;
            //唤醒condition2
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //释放锁
            lock.unlock();
        }
    }

    public void   printC(){
        //给业务方法上锁
        lock.lock();
        try {

            while (unmber!=3){
                //轮不到condition3来进行打印,当前线程进入等待
                condition3.await();
            }
            //程序走到这一步说明此时的number等于3,直接打印就行了
            System.out.println(Thread.currentThread().getName()+"==>"+"CCCCCCCCCCCCCC");
            //此时将number==1  指定condition1来打印
            unmber=1;
            //唤醒condition1
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //释放锁
            lock.unlock();
        }
    }

}

在这里插入图片描述

6.(8锁现象)

8锁现象其实就是对应不同类型 关于锁的问题。

1,第一种问题

package com.zzuli.eightLock;

import java.util.concurrent.TimeUnit;



/*
* 在标准情况下,两个线程会先输出哪一个     1发信息  2打电话
* 重点:   synchronzied   锁的对象是方法的调用者 一个对象只有一把锁,这个例子中,调用的是同一个对象,用的是同一把锁,排队使用,谁先拿到,谁执行。
* */

public class OneLock {
    public static void main(String[] args) {
        Phone phone=new Phone();

        String name;
        new Thread(()->{
            phone.sendMs();
        }).start();
        //休息一秒
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone.call();
        }).start();

    }

}

class Phone{
    public synchronized void sendMs(){
        System.out.println("发信息");
    }


    public synchronized void call(){
        System.out.println("打电话");
    }
}

2,第二种问题

package com.zzuli.eightLock;

import java.util.concurrent.TimeUnit;



/*
* 在标准情况下,两个线程会先输出哪一个     1发信息  2打电话
* 重点:   synchronzied   锁的对象是方法的调用者 一个对象只有一把锁,这个例子中,调用的是同一个对象,用的是同一把锁,排队使用,谁先拿到,谁执行。
* */

public class OneLock {
    public static void main(String[] args) {
        Phone phone=new Phone();

        String name;
        new Thread(()->{
            phone.sendMs();
        }).start();
        //休息一秒
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone.call();
        }).start();

    }

}

class Phone{
    public synchronized void sendMs(){

        //打印之前  睡眠四秒

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

        System.out.println("发信息");
    }


    public synchronized void call(){
        System.out.println("打电话");
    }
}

3,第三种问题

package com.zzuli.eightLock;

import java.util.concurrent.TimeUnit;


/*
*   在这种情况下,首先打印出来的是    1,hello  2,发信息
*   sendMs()方法使用了锁,但是 hello()方法没有锁,所以并不会排队执行,所以间隔1秒之后输出hello
* */


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

    String name;
    new Thread(()->{
        phone.sendMs();
    }).start();
    //休息一秒
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    new Thread(()->{
        phone.hello();
    }).start();

}

}

class Phone2{
    public synchronized void sendMs(){

        //打印之前  睡眠四秒

        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");
    }
}

4,第四种问题

package com.zzuli.eightLock;

import java.util.concurrent.TimeUnit;

/*
*   在这个例子中,创建了两个不同对象,也就是有了两个不同的锁,所以不需要排队执行,只有当两个对象 却只有一把锁的时候,才需要排队执行。
* 1,打电话  2  发信息
*
* */
public class ThreeLock {
    public static void main(String[] args) {

        //在这个方法中,创建了两个对象访问不同的方法。

        Phone3 phone=new Phone3();
        Phone3 phone3=new Phone3();

        String name;
        new Thread(()->{
            phone.sendMs();
        }).start();
        //休息一秒
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone3.call();
        }).start();

    }

}

class Phone3{
    public synchronized void sendMs(){

        //打印之前  睡眠四秒

        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,第五种问题

package com.zzuli.eightLock;

import java.util.concurrent.TimeUnit;


/*
* 在同步线程中,方法中都加入了static关键字,在加载类之前,就已经存在了类模板(phone.getClassLoader),而且只有一个,因为类模板只有一个锁
* 所以锁也就只有一个,而且两个方法都加了static关键字,所以就要同步排队,   1,发信息   2,打电话
* */

public class FourLock {
    public static void main(String[] args) {
        Phone4 phone=new Phone4();
        String name;
        new Thread(()->{
            phone.sendMs();
        }).start();
        //休息一秒
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone.call();
        }).start();

    }

}

class Phone4{

    //加入了static关键子,
    public  static synchronized void sendMs(){

        //打印之前  睡眠四秒

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

        System.out.println("发信息");
    }

    public static synchronized void call(){
        System.out.println("打电话");
    }

    public void hello(){
        System.out.println("hello");
    }
}
 


6,第六种问题

package com.zzuli.eightLock;

import java.util.concurrent.TimeUnit;

/*
* 创建两个对象,两个static 关键字,因为两个方法都有static关键字,所以类模板只有一个,所以类模板只有一把锁,还是需要排队执行,1,发信息 2,打电话
* */

public class FiveLock {
    public static void main(String[] args) {
        Phone5 phone=new Phone5();
        Phone5 phone5=new Phone5();
        String name;
        new Thread(()->{
            phone.sendMs();
        }).start();
        //休息一秒
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone5.call();
        }).start();

    }

}

class Phone5{

    //加入了static关键子,
    public  static synchronized void sendMs(){

        //打印之前  睡眠四秒

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

        System.out.println("发信息");
    }

    public static synchronized void call(){
        System.out.println("打电话");
    }

    public void hello(){
        System.out.println("hello");
    }
}


7,第七种问题


package com.zzuli.eightLock;

import java.util.concurrent.TimeUnit;

/*
*
* 一个带有static关键字的方法, 一个普通的同步方法,两个锁是不同的,一个锁的是class模板, 一个是对象锁,两个锁是不相同的的,所以不存在同步排队问题
* 只有时间延迟问题。  1,打电话   2发信息
* */

public class SixLock {

    public static void main(String[] args) {
        Phone6 phone=new Phone6();

        String name;
        new Thread(()->{
            phone.sendMs();
        }).start();
        //休息一秒
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone.call();
        }).start();

    }

}

class Phone6{

    //加入了static关键子,
    public  static synchronized void sendMs(){

        //打印之前  睡眠四秒

        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");
    }
}

8,第八种问题

package com.zzuli.eightLock;

import java.util.concurrent.TimeUnit;

/*
* 创建两个对象,方法 一个带有static关键字的方法,一个普通的同步方法,一个锁的是class模板,一个锁的是对象,不是同一把锁,所以不存在同步排队问题
* 1,打电话  2,发信息
*
* */


public class ServenLock {
    public static void main(String[] args) {
        Phone7 phone=new Phone7();
        Phone7 phone7=new Phone7();

        String name;
        new Thread(()->{
            phone.sendMs();
        }).start();
        //休息一秒
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone7.call();
        }).start();

    }

}

class Phone7{

    //加入了static关键子,
    public  static synchronized void sendMs(){

        //打印之前  睡眠四秒

        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");
    }
}

7.集合类不安全

1,List不安全

list在多线程的情况下会触发的情况
java.util.ConcurrentModificationException 并发修改异常
解决方案:

// 并发下list是不安全的
/*
* 解决方案:
*1,使用vector Vector list= new Vector();(vector由symchronzied 修饰 是线程安全的,但是效率低下)
*2,使用工具类将不安全的转化为安全 List list= Collections.synchronizedList(new ArrayList<>());
*3, 写入时复制 List list= new CopyOnWriteArrayList<>(); 使用首选方案
* */

public class Listtest {
        public static void main(String[] args) {
        //CopyOnWrite   写入时复制   Cow  计算你设计时的一种优化策略
        //对线程调用的时候,对于这个唯一的list,读取的时候是固定的,写入的时候会有覆盖,而                //       copeOnWrite 可以避免写入覆盖,以免 造成数据问题
        List<Object> list= new CopyOnWriteArrayList<>();
        //开启十个线程,去跑   拉姆达表达式

        for (int i=1;i<11;i++) {
            new Thread(() -> {

                //随机生成一些字符串
                list.add("abc"+new Random().nextInt(8888)
                 );
                System.out.println(list);
            }).start()
        }
    }
}

2,set不安全
/*
*

  • java.util.ConcurrentModificationException 并发修改异常
  • 解决HashSet线程不安全问题
  • 1,使用工具类,将不安全的集合转换为安全的集合 Set set= Collections.synchronizedSet(new HashSet());
  • 2,使用JUC编程,读写时复制 Set set=new CopyOnWriteArraySet<>();解决线程不安全问题
  • */

代码验证

package com.zzuli.unsafe;

import java.util.Collections;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
/*
*
* java.util.ConcurrentModificationException 并发修改异常
* 解决HashSet线程不安全问题
* 1,使用工具类,将不安全的集合转换为安全的集合   Set<String> set=  Collections.synchronizedSet(new HashSet<String>());
* 2,使用JUC编程,读写时复制     Set<String> set=new CopyOnWriteArraySet<>();解决线程不安全问题
* */



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


        //创建一个set集合

      //  Set<String> set=new HashSet<String>();
     //Set<String> set=  Collections.synchronizedSet(new HashSet<String>());
        Set<String> set=new CopyOnWriteArraySet<>();

        //开启多个线程来进行 set接入
        for(int i=1;i<30;i++){
            //使用拉姆达表达式开启线程
            new Thread(()->{
                //给set集合加入元素
                set.add("abc"+new Random().nextInt(88888));
                //输出set
                System.out.println(set);
            }).start();
        }

    }
}

HashSet的本质是什么
源码介绍:

public HashSet() {
        map = new HashMap<>();
    }
//HashSet中add方法源码
//从源码中来看,set就是map集合中的key,并且不可重复
 public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
// PRESENT源码
//官方定义为一个不变的值
private static final Object PRESENT = new Object();

3,map不安全

回顾map源码

在工作中并不会这样用hashMap
//默认等价是什么 new HashMap<16 ,0.75>

    //初始化容量  加载因子
  //  Map<String ,String>   map = Collections.synchronizedMap(new HashMap<>())=

在这里插入图片描述

/*java.util.ConcurrentModificationException 并发修改异常
解决方案
1,使用Collection工具将不安全的map转化为 线程安全 Map<String ,String> map = Collections.synchronizedMap(new HashMap<>());
2,JUC并发编程解决线程不安全 Map<String ,String> map=new ConcurrentReaderHashMap();==

代码实现

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

        //在工作中并不会这样用hashMap
        //默认等价是什么          new HashMap<16 ,0.75>

        //初始化容量  加载因子
      //  Map<String ,String>   map = Collections.synchronizedMap(new HashMap<>());
        Map<String ,String>   map=new ConcurrentReaderHashMap();
        //开启多条线程,


        for (int i=1;i<20;i++){
            //使用拉姆达表达式开启线程
        new Thread(()->{
            map.put("abc"+new Random().nextInt(88888),"abc");
            System.out.println(map);
        }).start();

    }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值