JUC并发编程(上)

JUC 是Java原生的并发包,和一些常用的工具类

JUC并发编程(上)_第1张图片

线程与进程

一个进程可以包含多个线程,一个进程至少有一个线程。

并发与并行

并发:多个线程操作同一个资源,交替执行的过程。
并行:多个线程同时执行,只有在多核Cpu下才能完成!

线程有6种状态

1、NEW 新建
2、RUNABLE 运行
3、BLOCKED 阻塞
4、WAITING 等待
5、 TIMED_WAITING 延时等待
6、TERMINATED 终止

Wait和Sleep的区别

wait是一个Object类,sleep是一个Thread类
sleep不会释放锁,wait可以释放锁,
wait和notify是一组,一般在线程通信中使用,
sleep就是一个单独的方法,在哪里都可以使用,
sleep需要补货异常

在JUC编程中,线程休眠使用

TimeUnit.SECONDS.Sleep(3)

Synchronized锁的使用

package com.coding.demo01;

// 传统的 Synchronized 是一个关键字
// Synchronized 方法 和 Synchronized 块

/*
 * 我们的学习是基于企业级的开发进行的;
 * 1、架构:高内聚,低耦合
 * 2、套路:线程操作资源类,资源类是单独的
 */
public class Demo01 {
    public static void main(String[] args) throws InterruptedException {
        // 1、新建资源类
        Ticket ticket = new Ticket();

        // 2、线程操纵资源类
        new Thread(new Runnable() {
            public void run() {
                for (int i = 1; i <=40; i++) {
                    ticket.saleTicket();
                }
            }
        },"A").start();


        new Thread(new Runnable() {
            public void run() {
                for (int i = 1; i <=40; i++) {
                    ticket.saleTicket();
                }
            }
        },"B").start();

        new Thread(new Runnable() {
            public void run() {
                for (int i = 1; i <=40; i++) {
                    ticket.saleTicket();
                }
            }
        },"C").start();

    }
}

// 单独的资源类,属性和方法!
// 这样才能实现复用!
class Ticket{

    private int number = 30;

    // 同步锁,厕所 =>close=>
    // synchronized 这是一个关键字
    public synchronized void saleTicket(){
        if (number>0){
            System.out.println(Thread.currentThread().getName() + "卖出第"+(number--)+"票,还剩:"+number);
        }
    }

}

Lock锁的使用

package com.coding.demo01;

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

/*
 * JUC之后的操作
 * Lock锁 + lambda表达式!
 */
public class Demo02 {
    public static void main(String[] args) {
        // 1、新建资源类
        Ticket2 ticket = new Ticket2();
        // 2、线程操作资源类 , 所有的函数式接口都可以用 lambda表达式简化!
        // lambda表达式 (参数)->{具体的代码}
        new Thread(()->{for (int i = 1; i <= 40 ; i++) ticket.saleTicket();},"A").start();
        new Thread(()->{for (int i = 1; i <= 40 ; i++) ticket.saleTicket();},"B").start();
        new Thread(()->{for (int i = 1; i <= 40 ; i++) ticket.saleTicket();},"C").start();

    }
}


// 依旧是一个资源类
class Ticket2{
    // 使用Lock,它是一个对象
    // ReentrantLock 可重入锁:回家:大门 (卧室门,厕所门...)
    // ReentrantLock 默认是非公平锁!
    // 非公平锁: 不公平 (插队,后面的线程可以插队)
    // 公平锁: 公平(只能排队,后面的线程无法插队)
    private Lock lock = new ReentrantLock();

    private int number = 30;

    public void saleTicket(){
        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 和 Lock 区别

1、Synchronized 是一个关键字、Lock 是一个对象
2、Synchronized 无法尝试获取锁,Lock 可以尝试获取锁,判断
3、Synchronized 会自动释放锁(a线程执行完毕,b如果异常了,也会释放锁),lock锁是手动释放锁!如果你不释放就会死锁。
4、Synchronized (线程A(获得锁,如果阻塞),线程B(等待,一直等待);)lock,可以尝试获取锁,失败了之后就放弃
5、Synchronized 一定是非公平的,但是 Lock 锁可以是公平的,通过参数设置;
6、代码量特别大的时候,我们一般使用Lock实现精准控制,Synchronized 适合代码量比较小的同步问题;

生产者和消费者问题

当有两个线程执行加减的时候,没有问题,但是当有四条线程交替执行的时候,会产生虚假唤醒的问题。此时在资源判断的时候,用while代替if

package com.coding.demo01;

// Synchronized 版
/*
目的: 有两个线程:A  B ,还有一个值初始为0,
       实现两个线程交替执行,对该变量 + 1,-1;交替10次
 */
public class Demo03 {
    public static void main(String[] args) {
        Data data = new Data();

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

        // -1
        new Thread(()->{
            for (int i = 1; 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){ // 判断是否需要等待
            this.wait();
        }
        number++; // 执行
        System.out.println(Thread.currentThread().getName()+"\t"+number);
        // 通知
        this.notifyAll(); //唤醒所有线程
    }

    // -1
    public synchronized void decrement() throws InterruptedException {
        if (number==0){ // 判断是否需要等待
            this.wait();
        }
        number--; // 执行
        System.out.println(Thread.currentThread().getName()+"\t"+number);
        // 通知
        this.notifyAll(); //唤醒所有线程
    }

}
package com.coding.demo01;

// Synchronized 版
/*
目的: 有两个线程:A  B ,还有一个值初始为0,
       实现两个线程交替执行,对该变量 + 1,-1;交替10次
       
传统的 wait 和 notify方法不能实现精准唤醒通知!
 */
public class Demo03 {
    public static void main(String[] args) {
        Data data = new Data();

        // +1
        new Thread(()->{
            for (int i = 1; i <=10 ; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 1; i <=10 ; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();

        // -1
        new Thread(()->{
            for (int i = 1; i <=10 ; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
        new Thread(()->{
            for (int i = 1; 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){ // 判断是否需要等待
            this.wait();
        }
        number++; // 执行
        System.out.println(Thread.currentThread().getName()+"\t"+number);
        // 通知
        this.notifyAll(); //唤醒所有线程
    }

    // -1
    public synchronized void decrement() throws InterruptedException {
        while (number==0){ // 判断是否需要等待
            this.wait();
        }
        number--; // 执行
        System.out.println(Thread.currentThread().getName()+"\t"+number);
        // 通知
        this.notifyAll(); //唤醒所有线程
    }

}

JUC并发编程(上)_第2张图片

package com.coding.demo01;

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

/*
实现线程交替执行!
主要的实现目标:精准的唤醒线程!
    三个线程:A B C
    三个方法:A p5  B p10   C p15 依次循环
 */
public class Demo04 {
    public static void main(String[] args) {
        Data2 data = new Data2();

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

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

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

// 资源类
class Data2{
    private int number = 1; // 1A 2B  3C
    private Lock lock = new ReentrantLock();
    // 实现精准访问
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();

    public void print5() throws InterruptedException {

        lock.lock();

        try {
            // 判断
            while (number!=1){
                condition1.await();
            }
            // 执行
            for (int i = 1; i <= 5; i++) {
                System.out.println(Thread.currentThread().getName() + "\t" + i);
            }
            // 通知第二个线程干活!
            number = 2;
            condition2.signal(); // 唤醒
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock(); // 一定要解锁
        }
    }

    public void print10() throws InterruptedException {
        lock.lock();
        try {
            // 判断
            while (number!=2){
                condition2.await();
            }
            // 执行
            for (int i = 1; i <= 10; i++) {
                System.out.println(Thread.currentThread().getName() + "\t" + i);
            }
            // 通知3干活
            number = 3;
            condition3.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }

    public void print15() throws InterruptedException {
        lock.lock();
        try {
            // 判断
            while (number!=3){
                condition3.await();
            }
            // 执行
            for (int i = 1; i <= 15; i++) {
                System.out.println(Thread.currentThread().getName() + "\t" + i);
            }
            // 通知 1 干活
            number = 1;
            condition1.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}

8锁现象线程彻底理解锁

1、synchronized 修饰的方法,同一个对象调用
package com.coding.lock8;

import java.util.concurrent.TimeUnit;

/*
1、标准的访问情况下,先执行 sendEmail 还是 sendSMS

   答案:sendEmail
   被 synchronized 修饰的方式,锁的对象是方法的调用者,所以说这里两个方法调用的对象是同一个
   先调用的先执行!
 */
public class LockDemo01 {
    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();

        new Thread(()->{
            phone.sendEmail();
        },"A").start();

        //Thread.sleep(200);
        TimeUnit.SECONDS.sleep(2);

        new Thread(()->{
            phone.sendSMS();
        },"B").start();
    }
}

class Phone{
    public synchronized void sendEmail(){
        System.out.println("sendEmail");
    }

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

此时先执行sendEmail,然后再执行sendSMS,同一个对象调用,按谁先调用谁先执行

2、休眠3秒后,执行顺序
package com.coding.lock8;

import java.util.concurrent.TimeUnit;

/*
2、sendEmail休眠3秒后 ,先执行 sendEmail 还是 sendSMS

   答案:sendEmail
   被 synchronized 修饰的方式,锁的对象是方法的调用者,所以说这里两个方法调用的对象是同一个
   先调用的先执行!
 */
public class LockDemo02 {
    public static void main(String[] args) throws InterruptedException {
        Phone2 phone = new Phone2();

        new Thread(()->{
            try {
                phone.sendEmail();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();

        //Thread.sleep(200);
        TimeUnit.SECONDS.sleep(2);

        new Thread(()->{
            phone.sendSMS();
        },"B").start();
    }
}

class Phone2{
    public synchronized void sendEmail() throws InterruptedException {
        TimeUnit.SECONDS.sleep(3);
        System.out.println("sendEmail");
    }

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

此时先执行sendEmail,然后再执行sendSMS

3、增加一个普通方法,没有synchronized修饰,不受锁的影响
package com.coding.lock8;

import java.util.concurrent.TimeUnit;

/*
3、增加一个普通方法,请问先打印那个 sendEmail 还是 hello

   答案:hello
   新增加的这个方法没有 synchronized 修饰,不是同步方法,不受锁的影响!
 */
public class LockDemo03 {
    public static void main(String[] args) throws InterruptedException {
        Phone3 phone = new Phone3();

        new Thread(()->{
            try {
                phone.sendEmail();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();

        //Thread.sleep(200);
        TimeUnit.SECONDS.sleep(1);

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

class Phone3{
    public synchronized void sendEmail() throws InterruptedException {
        TimeUnit.SECONDS.sleep(4);
        System.out.println("sendEmail");
    }

    // 没有 synchronized 没有 static 就是普通方式
    public void hello(){
        System.out.println("hello");
    }
}

不按顺序执行

4、synchronized 修饰的方式,有两个调用者
package com.coding.lock8;

import java.util.concurrent.TimeUnit;

/*
4、两个手机,请问先执行sendEmail 还是 sendSMS
    答案:sendSMS
    被 synchronized  修饰的方式,锁的对象是调用者;我们这里有两个调用者,两个方法在这里是两个锁
 */
public class LockDemo04 {
    public static void main(String[] args) throws InterruptedException {
        Phone4 phone1 = new Phone4();
        Phone4 phone2 = new Phone4();

        new Thread(()->{
            try {
                phone1.sendEmail();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();

        //Thread.sleep(200);
        TimeUnit.SECONDS.sleep(1);

        new Thread(()->{
            phone2.sendSMS();
        },"B").start();
    }
}

class Phone4{
    public synchronized void sendEmail() throws InterruptedException {
        TimeUnit.SECONDS.sleep(3);
        System.out.println("sendEmail");
    }

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

两个调用者,增加了两把锁

5、两个静态同步方法
package com.coding.lock8;

import java.util.concurrent.TimeUnit;

//LockDemo05.Class 模板,只有一个     static
//new LockDemo05(),可以创建多个对象

/*
5、两个静态同步方法,同一个手机请问先执行sendEmail 还是 sendSMS

    答案:sendEmail
    只要方法被 static 修饰,锁的对象就是 Class模板对象,这个则全局唯一!所以说这里是同一个锁
    并不是因为synchronized
 */
public class LockDemo05 {
    public static void main(String[] args) throws InterruptedException {
        Phone5 phone = new Phone5();


        new Thread(()->{
            try {
                phone.sendEmail();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();

        //Thread.sleep(200);
        TimeUnit.SECONDS.sleep(1);

        new Thread(()->{
            phone.sendSMS();
        },"B").start();
    }
}

class Phone5{

    public static synchronized void sendEmail() throws InterruptedException {
        TimeUnit.SECONDS.sleep(3);
        System.out.println("sendEmail");
    }

    public static synchronized void sendSMS(){
        System.out.println("sendSMS");
    }

}

锁class,按顺序执行

6、两个静态方法,两个对象调用
package com.coding.lock8;

import java.util.concurrent.TimeUnit;

//LockDemo05.Class 模板,只有一个     static
//new LockDemo05(),可以创建多个对象

/*
6、两个静态同步方法,两个手机,请问先执行sendEmail 还是 sendSMS

    答案:sendEmail
    只要方法被 static 修饰,锁的对象就是 Class模板对象,这个则全局唯一!所以说这里是同一个锁
    并不是因为synchronized
 */
public class LockDemo06 {
    public static void main(String[] args) throws InterruptedException {
        Phone6 phone = new Phone6();
        Phone6 phone2 = new Phone6();

        new Thread(()->{
            try {
                phone.sendEmail();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();

        //Thread.sleep(200);
        TimeUnit.SECONDS.sleep(1);

        new Thread(()->{
            phone2.sendSMS();
        },"B").start();
    }
}

class Phone6{

    public static synchronized void sendEmail() throws InterruptedException {
        TimeUnit.SECONDS.sleep(3);
        System.out.println("sendEmail");
    }

    public static synchronized void sendSMS(){
        System.out.println("sendSMS");
    }

}

按顺序执行

7、一个普通方法,一个静态同步方法,只有一个调用者
package com.coding.lock8;

import java.util.concurrent.TimeUnit;

//LockDemo05.Class 模板,只有一个     static
//new LockDemo05(),可以创建多个对象

/*
7、一个普通同步方法,一个静态同步方法,只有一个手机,请问先执行sendEmail 还是 sendSMS

    答案:sendSMS
    synchronized 锁的是这个调用的对象
    static 锁的是这个类的Class模板
    这里是两个锁!
 */
public class LockDemo07 {
    public static void main(String[] args) throws InterruptedException {
        Phone7 phone = new Phone7();

        new Thread(()->{
            try {
                phone.sendEmail();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();

        //Thread.sleep(200);
        TimeUnit.SECONDS.sleep(1);

        new Thread(()->{
            phone.sendSMS();
        },"B").start();
    }
}

class Phone7{

    public static synchronized void sendEmail() throws InterruptedException {
        TimeUnit.SECONDS.sleep(3);
        System.out.println("sendEmail");
    }

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

}

不按顺序执行

8、一个普通方法,一个静态同步方法,两个调用者
package com.coding.lock8;

import java.util.concurrent.TimeUnit;

//LockDemo05.Class 模板,只有一个     static
//new LockDemo05(),可以创建多个对象

/*
7、一个普通同步方法,一个静态同步方法,两个手机,请问先执行sendEmail 还是 sendSMS

    答案:sendSMS
    synchronized 锁的是这个调用的对象
    static 锁的是这个类的Class模板
    这里是两个锁!
 */
public class LockDemo08 {
    public static void main(String[] args) throws InterruptedException {
        Phone8 phone = new Phone8();
        Phone8 phone2 = new Phone8();

        new Thread(()->{
            try {
                phone.sendEmail();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();

        //Thread.sleep(200);
        TimeUnit.SECONDS.sleep(1);

        new Thread(()->{
            phone2.sendSMS();
        },"B").start();
    }
}

class Phone8{

    public static synchronized void sendEmail() throws InterruptedException {
        TimeUnit.SECONDS.sleep(3);
        System.out.println("sendEmail");
    }

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

}

不按顺序执行

不安全的集合类

List、Map、Set都是不安全的集合类

List不安全

list不安全,Vector集合时安全的,但是效率低,
我们可以使用Collenctions集合工具类synchronizedList,
也可以使用JUC中的CopyOnWriteArrayList 都是线程安全的。

package com.coding.unsafe;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 故障现象:ConcurrentModificationException 并发修改异常
 * 导致原因:add方法没有锁!
 * 解决方案:
 * 1、List list = new Vector<>(); //jdk1.0 就存在的!效率低
 * 2、List list = Collections.synchronizedList(new ArrayList<>());
 * 3、List list = new CopyOnWriteArrayList<>();
 * 
 * 什么是 CopyOnWrite; 写入是复制 (思想 COW)
 * 多个调用者同时要相同的资源;这个有一个指针的概念。
 * 读写分离的思想:
 */
public class UnSafeList {

    public static void main(String[] args) {
//        List list = Arrays.asList("a", "b", "c");
//        list.forEach(System.out::println);
//        List list = new ArrayList<>();

        List<String> list = new CopyOnWriteArrayList<>();

        for (int i = 1; i <= 30; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,3));
                System.out.println(list);
            },String.valueOf(i)).start();
        }

    }

}
Set不安全

Set不安全,
我们可以使用Collenctions集合工具类synchronizedSet,
也可以使用JUC中的CopyOnWriteArraySet都是线程安全的。

package com.coding.unsafe;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;

// ConcurrentModificationException
public class UnSafeSet {
    public static void main(String[] args) {
        // HashSet 底层是什么 就是 HashMap
        // add,就是 HashMap 的 key;
        Set<String> set = new HashSet<>();

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

        for (int i = 1; i <=30 ; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,3));
                System.out.println(set);
            },String.valueOf(i)).start();
        }
    }
}
Map不安全

也可以使用JUC中的ConcurrentHashMap都是线程安全的。

package com.coding.unsafe;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

//ConcurrentModificationException
public class UnsafeMap {
    public static void main(String[] args) {
        // new HashMap<>() 工作中是这样用的吗? 不是
        // 加载因子0.75f;,容量 16; 这两个值工作中不一定这样用!
        // 优化性能!
        // HashMap 底层数据结构,链表 + 红黑树
        // = = = = = = =
//        Map map = new HashMap<>();
        Map<String, String> map = new ConcurrentHashMap<>();

        // 人生如程序,不是选择就是循环,时常的自我总结十分重要!
        for (int i = 1; i <=30 ; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,3));
                System.out.println(map);
            },String.valueOf(i)).start();
        }

    }
}

你可能感兴趣的:(JUC并发编程)