栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

JUC多线程

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JUC多线程

1.java中叫锁,操作系统称为monitor(监视器)

2.虚假唤醒问题

需求:对数字0交替执行+1和-1操作

备注:synchronized写法中wait()是等待,notify()是唤醒。lock写法中await()是等待,signal()是唤醒。

有问题的代码:

package com.atguigu.sync;

//第一步 创建资源类,定义属性和操作方法
class Share {
    //初始值
    private int number = 0;
    //+1的方法
    public synchronized void incr() throws InterruptedException {
        //第二步 判断 干活 通知
        if(number != 0) { //判断number值是否是0,如果不是0,等待
            this.wait(); //在哪里睡,就在哪里醒
        }
        //如果number值是0,就+1操作
        number++;
        System.out.println(Thread.currentThread().getName()+" :: "+number);
        //通知其他线程
        this.notifyAll();
    }

    //-1的方法
    public synchronized void decr() throws InterruptedException {
        //判断
        if(number != 1) {
            this.wait();
        }
        //干活
        number--;
        System.out.println(Thread.currentThread().getName()+" :: "+number);
        //通知其他线程
        this.notifyAll();
    }
}

public class ThreadDemo1 {
    //第三步 创建多个线程,调用资源类的操作方法
    public static void main(String[] args) {
        Share share = new Share();
        //创建线程
        new Thread(()->{
            for (int i = 1; i <=10; i++) {
                try {
                    share.incr(); //+1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"AA").start();

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

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

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

AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
CC :: 1
BB :: 0
AA :: 1
BB :: 0
CC :: 1
BB :: 0
AA :: 1
DD :: 0
BB :: -1
CC :: 0
CC :: 1
BB :: 0
DD :: -1
AA :: 0
AA :: 1
DD :: 0
CC :: 1
DD :: 0
AA :: 1
DD :: 0
CC :: 1
DD :: 0
CC :: 1
DD :: 0
CC :: 1
DD :: 0
CC :: 1
DD :: 0
CC :: 1
DD :: 0

Process finished with exit code 0
 

 解释:

        wait()方法会释放锁。

        new ReentrantLock();        ----        可重入锁

 

        当AA抢到线程后会+1,此时为1。之后唤醒其他线程,若此时CC抢到,由于CC判断if(number!=1),CC线程进行等待,同时会释放锁。若此时AA抢到,则AA在if判断处也进行等待,同时会释放锁。若CC又抢到,即CC被唤醒,由于wait()是在哪里睡在哪里醒,此时被唤醒后,CC线程在wait()方法后执行,就执行了+1,此时为2。这就是虚假唤醒,出问题了。此处为出问题的其中一种情况,其他情况会出现-1等负数出现的情况。

解决方案:

        if关键词改为while关键词。wait()是在哪里睡就在哪里醒,醒来后if就不生效了,while会使无论何时行,while都会执行。

代码如下1:

package com.atguigu.sync;

//第一步 创建资源类,定义属性和操作方法
class Share {
    //初始值
    private int number = 0;
    //+1的方法
    public synchronized void incr() throws InterruptedException {
        //第二步 判断 干活 通知
        while (number != 0) { //判断number值是否是0,如果不是0,等待
            this.wait(); //在哪里睡,就在哪里醒
        }
        //如果number值是0,就+1操作
        number++;
        System.out.println(Thread.currentThread().getName()+" :: "+number);
        //通知其他线程
        this.notifyAll();
    }

    //-1的方法
    public synchronized void decr() throws InterruptedException {
        //判断
        while (number != 1) {
            this.wait();
        }
        //干活
        number--;
        System.out.println(Thread.currentThread().getName()+" :: "+number);
        //通知其他线程
        this.notifyAll();
    }
}

public class ThreadDemo1 {
    //第三步 创建多个线程,调用资源类的操作方法
    public static void main(String[] args) {
        Share share = new Share();
        //创建线程
        new Thread(()->{
            for (int i = 1; i <=10; i++) {
                try {
                    share.incr(); //+1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"AA").start();

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

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

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

代码如下2(另一种正确写法:不用synchronized,用lock):

package com.atguigu.lock;

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

//第一步 创建资源类,定义属性和操作方法
class Share {
    private int number = 0;

    //创建Lock
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    //+1
    public void incr() throws InterruptedException {
        //上锁
        lock.lock();
        try {
            //判断
            while (number != 0) {
                condition.await();
            }
            //干活
            number++;
            System.out.println(Thread.currentThread().getName()+" :: "+number);
            //通知
            condition.signalAll();
        }finally {
            //解锁
            lock.unlock();
        }
    }

    //-1
    public void decr() throws InterruptedException {
        lock.lock();
        try {
            while(number != 1) {
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName()+" :: "+number);
            condition.signalAll();
        }finally {
            lock.unlock();
        }
    }
}

public class ThreadDemo2 {

    public static void main(String[] args) {
        Share share = new Share();
        new Thread(()->{
            for (int i = 1; i <=10; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"AA").start();
        new Thread(()->{
            for (int i = 1; i <=10; i++) {
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"BB").start();

        new Thread(()->{
            for (int i = 1; i <=10; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"CC").start();
        new Thread(()->{
            for (int i = 1; i <=10; i++) {
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"DD").start();
    }

}

打印如下:

       

AA :: 1
BB :: 0
CC :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
DD :: 0
CC :: 1
DD :: 0
CC :: 1
DD :: 0
CC :: 1
DD :: 0
CC :: 1
DD :: 0
CC :: 1
DD :: 0
CC :: 1
DD :: 0
CC :: 1
DD :: 0
CC :: 1
DD :: 0
CC :: 1
DD :: 0

Process finished with exit code 0
 

 升级:定制化通信,指定线程运行顺序

 

package com.atguigu.lock;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

//第一步 创建资源类
class ShareResource {
    //定义标志位
    private int flag = 1;  // 1 AA     2 BB     3 CC

    //创建Lock锁
    private Lock lock = new ReentrantLock();

    //创建三个condition
    private Condition c1 = lock.newCondition();
    private Condition c2 = lock.newCondition();
    private Condition c3 = lock.newCondition();

    //打印5次,参数第几轮
    public void print5(int loop) throws InterruptedException {
        //上锁
        lock.lock();
        try {
            //判断
            while(flag != 1) {
                //等待
                c1.await();
            }
            //干活
            for (int i = 1; i <=5; i++) {
                System.out.println(Thread.currentThread().getName()+" :: "+i+" :轮数:"+loop);
            }
            //通知
            flag = 2; //修改标志位 2
            c2.signal(); //通知BB线程
        }finally {
            //释放锁
            lock.unlock();
        }
    }

    //打印10次,参数第几轮
    public void print10(int loop) throws InterruptedException {
        lock.lock();
        try {
            while(flag != 2) {
                c2.await();
            }
            for (int i = 1; i <=10; i++) {
                System.out.println(Thread.currentThread().getName()+" :: "+i+" :轮数:"+loop);
            }
            //修改标志位
            flag = 3;
            //通知CC线程
            c3.signal();
        }finally {
            lock.unlock();
        }
    }

    //打印15次,参数第几轮
    public void print15(int loop) throws InterruptedException {
        lock.lock();
        try {
            while(flag != 3) {
                c3.await();
            }
            for (int i = 1; i <=15; i++) {
                System.out.println(Thread.currentThread().getName()+" :: "+i+" :轮数:"+loop);
            }
            //修改标志位
            flag = 1;
            //通知AA线程
            c1.signal();
        }finally {
            lock.unlock();
        }
    }
}

public class ThreadDemo3 {
    public static void main(String[] args) {
        ShareResource shareResource = new ShareResource();
        new Thread(()->{
            for (int i = 1; i <=10; i++) {
                try {
                    shareResource.print5(i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"AA").start();

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

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

 
AA :: 1 :轮数:1
AA :: 2 :轮数:1
AA :: 3 :轮数:1
AA :: 4 :轮数:1
AA :: 5 :轮数:1
BB :: 1 :轮数:1
BB :: 2 :轮数:1
BB :: 3 :轮数:1
BB :: 4 :轮数:1
BB :: 5 :轮数:1
BB :: 6 :轮数:1
BB :: 7 :轮数:1
BB :: 8 :轮数:1
BB :: 9 :轮数:1
BB :: 10 :轮数:1
CC :: 1 :轮数:1
CC :: 2 :轮数:1
CC :: 3 :轮数:1
CC :: 4 :轮数:1
CC :: 5 :轮数:1
CC :: 6 :轮数:1
CC :: 7 :轮数:1
CC :: 8 :轮数:1
CC :: 9 :轮数:1
CC :: 10 :轮数:1
CC :: 11 :轮数:1
CC :: 12 :轮数:1
CC :: 13 :轮数:1
CC :: 14 :轮数:1
CC :: 15 :轮数:1
AA :: 1 :轮数:2
AA :: 2 :轮数:2
AA :: 3 :轮数:2
AA :: 4 :轮数:2
AA :: 5 :轮数:2
BB :: 1 :轮数:2
BB :: 2 :轮数:2
BB :: 3 :轮数:2
BB :: 4 :轮数:2
BB :: 5 :轮数:2
BB :: 6 :轮数:2
BB :: 7 :轮数:2
BB :: 8 :轮数:2
BB :: 9 :轮数:2
BB :: 10 :轮数:2
CC :: 1 :轮数:2
CC :: 2 :轮数:2
CC :: 3 :轮数:2
CC :: 4 :轮数:2
CC :: 5 :轮数:2
CC :: 6 :轮数:2
CC :: 7 :轮数:2
CC :: 8 :轮数:2
CC :: 9 :轮数:2
CC :: 10 :轮数:2
CC :: 11 :轮数:2
CC :: 12 :轮数:2
CC :: 13 :轮数:2
CC :: 14 :轮数:2
CC :: 15 :轮数:2
AA :: 1 :轮数:3
AA :: 2 :轮数:3
AA :: 3 :轮数:3
AA :: 4 :轮数:3
AA :: 5 :轮数:3
BB :: 1 :轮数:3
BB :: 2 :轮数:3
BB :: 3 :轮数:3
BB :: 4 :轮数:3
BB :: 5 :轮数:3
BB :: 6 :轮数:3
BB :: 7 :轮数:3
BB :: 8 :轮数:3
BB :: 9 :轮数:3
BB :: 10 :轮数:3
CC :: 1 :轮数:3
CC :: 2 :轮数:3
CC :: 3 :轮数:3
CC :: 4 :轮数:3
CC :: 5 :轮数:3
CC :: 6 :轮数:3
CC :: 7 :轮数:3
CC :: 8 :轮数:3
CC :: 9 :轮数:3
CC :: 10 :轮数:3
CC :: 11 :轮数:3
CC :: 12 :轮数:3
CC :: 13 :轮数:3
CC :: 14 :轮数:3
CC :: 15 :轮数:3
AA :: 1 :轮数:4
AA :: 2 :轮数:4
AA :: 3 :轮数:4
AA :: 4 :轮数:4
AA :: 5 :轮数:4
BB :: 1 :轮数:4
BB :: 2 :轮数:4
BB :: 3 :轮数:4
BB :: 4 :轮数:4
BB :: 5 :轮数:4
BB :: 6 :轮数:4
BB :: 7 :轮数:4
BB :: 8 :轮数:4
BB :: 9 :轮数:4
BB :: 10 :轮数:4
CC :: 1 :轮数:4
CC :: 2 :轮数:4
CC :: 3 :轮数:4
CC :: 4 :轮数:4
CC :: 5 :轮数:4
CC :: 6 :轮数:4
CC :: 7 :轮数:4
CC :: 8 :轮数:4
CC :: 9 :轮数:4
CC :: 10 :轮数:4
CC :: 11 :轮数:4
CC :: 12 :轮数:4
CC :: 13 :轮数:4
CC :: 14 :轮数:4
CC :: 15 :轮数:4
AA :: 1 :轮数:5
AA :: 2 :轮数:5
AA :: 3 :轮数:5
AA :: 4 :轮数:5
AA :: 5 :轮数:5
BB :: 1 :轮数:5
BB :: 2 :轮数:5
BB :: 3 :轮数:5
BB :: 4 :轮数:5
BB :: 5 :轮数:5
BB :: 6 :轮数:5
BB :: 7 :轮数:5
BB :: 8 :轮数:5
BB :: 9 :轮数:5
BB :: 10 :轮数:5
CC :: 1 :轮数:5
CC :: 2 :轮数:5
CC :: 3 :轮数:5
CC :: 4 :轮数:5
CC :: 5 :轮数:5
CC :: 6 :轮数:5
CC :: 7 :轮数:5
CC :: 8 :轮数:5
CC :: 9 :轮数:5
CC :: 10 :轮数:5
CC :: 11 :轮数:5
CC :: 12 :轮数:5
CC :: 13 :轮数:5
CC :: 14 :轮数:5
CC :: 15 :轮数:5
AA :: 1 :轮数:6
AA :: 2 :轮数:6
AA :: 3 :轮数:6
AA :: 4 :轮数:6
AA :: 5 :轮数:6
BB :: 1 :轮数:6
BB :: 2 :轮数:6
BB :: 3 :轮数:6
BB :: 4 :轮数:6
BB :: 5 :轮数:6
BB :: 6 :轮数:6
BB :: 7 :轮数:6
BB :: 8 :轮数:6
BB :: 9 :轮数:6
BB :: 10 :轮数:6
CC :: 1 :轮数:6
CC :: 2 :轮数:6
CC :: 3 :轮数:6
CC :: 4 :轮数:6
CC :: 5 :轮数:6
CC :: 6 :轮数:6
CC :: 7 :轮数:6
CC :: 8 :轮数:6
CC :: 9 :轮数:6
CC :: 10 :轮数:6
CC :: 11 :轮数:6
CC :: 12 :轮数:6
CC :: 13 :轮数:6
CC :: 14 :轮数:6
CC :: 15 :轮数:6
AA :: 1 :轮数:7
AA :: 2 :轮数:7
AA :: 3 :轮数:7
AA :: 4 :轮数:7
AA :: 5 :轮数:7
BB :: 1 :轮数:7
BB :: 2 :轮数:7
BB :: 3 :轮数:7
BB :: 4 :轮数:7
BB :: 5 :轮数:7
BB :: 6 :轮数:7
BB :: 7 :轮数:7
BB :: 8 :轮数:7
BB :: 9 :轮数:7
BB :: 10 :轮数:7
CC :: 1 :轮数:7
CC :: 2 :轮数:7
CC :: 3 :轮数:7
CC :: 4 :轮数:7
CC :: 5 :轮数:7
CC :: 6 :轮数:7
CC :: 7 :轮数:7
CC :: 8 :轮数:7
CC :: 9 :轮数:7
CC :: 10 :轮数:7
CC :: 11 :轮数:7
CC :: 12 :轮数:7
CC :: 13 :轮数:7
CC :: 14 :轮数:7
CC :: 15 :轮数:7
AA :: 1 :轮数:8
AA :: 2 :轮数:8
AA :: 3 :轮数:8
AA :: 4 :轮数:8
AA :: 5 :轮数:8
BB :: 1 :轮数:8
BB :: 2 :轮数:8
BB :: 3 :轮数:8
BB :: 4 :轮数:8
BB :: 5 :轮数:8
BB :: 6 :轮数:8
BB :: 7 :轮数:8
BB :: 8 :轮数:8
BB :: 9 :轮数:8
BB :: 10 :轮数:8
CC :: 1 :轮数:8
CC :: 2 :轮数:8
CC :: 3 :轮数:8
CC :: 4 :轮数:8
CC :: 5 :轮数:8
CC :: 6 :轮数:8
CC :: 7 :轮数:8
CC :: 8 :轮数:8
CC :: 9 :轮数:8
CC :: 10 :轮数:8
CC :: 11 :轮数:8
CC :: 12 :轮数:8
CC :: 13 :轮数:8
CC :: 14 :轮数:8
CC :: 15 :轮数:8
AA :: 1 :轮数:9
AA :: 2 :轮数:9
AA :: 3 :轮数:9
AA :: 4 :轮数:9
AA :: 5 :轮数:9
BB :: 1 :轮数:9
BB :: 2 :轮数:9
BB :: 3 :轮数:9
BB :: 4 :轮数:9
BB :: 5 :轮数:9
BB :: 6 :轮数:9
BB :: 7 :轮数:9
BB :: 8 :轮数:9
BB :: 9 :轮数:9
BB :: 10 :轮数:9
CC :: 1 :轮数:9
CC :: 2 :轮数:9
CC :: 3 :轮数:9
CC :: 4 :轮数:9
CC :: 5 :轮数:9
CC :: 6 :轮数:9
CC :: 7 :轮数:9
CC :: 8 :轮数:9
CC :: 9 :轮数:9
CC :: 10 :轮数:9
CC :: 11 :轮数:9
CC :: 12 :轮数:9
CC :: 13 :轮数:9
CC :: 14 :轮数:9
CC :: 15 :轮数:9
AA :: 1 :轮数:10
AA :: 2 :轮数:10
AA :: 3 :轮数:10
AA :: 4 :轮数:10
AA :: 5 :轮数:10
BB :: 1 :轮数:10
BB :: 2 :轮数:10
BB :: 3 :轮数:10
BB :: 4 :轮数:10
BB :: 5 :轮数:10
BB :: 6 :轮数:10
BB :: 7 :轮数:10
BB :: 8 :轮数:10
BB :: 9 :轮数:10
BB :: 10 :轮数:10
CC :: 1 :轮数:10
CC :: 2 :轮数:10
CC :: 3 :轮数:10
CC :: 4 :轮数:10
CC :: 5 :轮数:10
CC :: 6 :轮数:10
CC :: 7 :轮数:10
CC :: 8 :轮数:10
CC :: 9 :轮数:10
CC :: 10 :轮数:10
CC :: 11 :轮数:10
CC :: 12 :轮数:10
CC :: 13 :轮数:10
CC :: 14 :轮数:10
CC :: 15 :轮数:10

Process finished with exit code 0
 

 3.集合线程安全

总览:

ArrayList集合不安全为题演示:

需求:实现简单的list添加数据功能。

代码如下:

package com.atguigu.self;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class Test {

    public static void main(String[] args) {

        // 创建ArrayList集合
        List  list = new ArrayList<>();

        for (int i = 0; i <30; i++) {
            new Thread(()->{
                //向集合添加内容
                list.add(UUID.randomUUID().toString().substring(0,8));
                //从集合获取内容
                System.out.println(list);
            },String.valueOf(i)).start();
        }

    }
}

出现的问题:

        java.util.ConcurrentModificationException

解决方案:

代码如下1:vector方法

package com.atguigu.self;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.Vector;

public class Test {

    public static void main(String[] args) {

        // 创建vector
        List  list = new Vector<>();

        for (int i = 0; i <30; i++) {
            new Thread(()->{
                //向集合添加内容
                list.add(UUID.randomUUID().toString().substring(0,8));
                //从集合获取内容
                System.out.println(list);
            },String.valueOf(i)).start();
        }

    }
}

解释:ArrayList()源码中的add()方法没有加synchronized,Vector()源码中添加了。

代码如下2:Collections工具类解决

package com.atguigu.self;

import java.util.*;

public class Test {

    public static void main(String[] args) {

        // Collections工具类解决
        List  list = Collections.synchronizedList(new ArrayList<>());

        for (int i = 0; i <30; i++) {
            new Thread(()->{
                //向集合添加内容
                list.add(UUID.randomUUID().toString().substring(0,8));
                //从集合获取内容
                System.out.println(list);
            },String.valueOf(i)).start();
        }

    }
}

代码如下3:CopyOnWriteArrayList解决---JUC类中的(写时复制技术)

package com.atguigu.self;

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

public class Test {

    public static void main(String[] args) {

        // JUC中的CopyOnWriteArrayList解决
        List list = new CopyOnWriteArrayList<>();

        for (int i = 0; i <30; i++) {
            new Thread(()->{
                //向集合添加内容
                list.add(UUID.randomUUID().toString().substring(0,8));
                //从集合获取内容
                System.out.println(list);
            },String.valueOf(i)).start();
        }

    }
}

HaspSet和HashMap线程不安全问题:

HaspSet这种情况与ArrayList问题相同,解决方案也类似,使用CopyOnWriteArraySet<>();

HashMap这种情况也与AllayList问题相同,解决方案也类似,使用ConcurrentHashMap<>();

代码如下:

package com.atguigu.self;

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

public class Test {

    public static void main(String[] args) {

        //        演示Hashset
//        Set set = new HashSet<>();

        Set set = new CopyOnWriteArraySet<>();
        for (int i = 0; i <30; i++) {
            new Thread(()->{
                //向集合添加内容
                set.add(UUID.randomUUID().toString().substring(0,8));
                //从集合获取内容
                System.out.println(set);
            },String.valueOf(i)).start();
        }

//        演示HashMap
//        Map map = new HashMap<>();

        Map map = new ConcurrentHashMap<>();
        for (int i = 0; i <30; i++) {
            String key = String.valueOf(i);
            new Thread(()->{
                //向集合添加内容
                map.put(key,UUID.randomUUID().toString().substring(0,8));
                //从集合获取内容
                System.out.println(map);
            },String.valueOf(i)).start();
        }
    }
}

4.synchronized锁的八种情况

见代码路径:

C:Users惜听documents百度网盘JUC源juc_atguigusrccomatguigusyncLock_8.java

5.公平锁和非公平锁

公平锁不会使一个线程运行所有。非公平锁效率高

package com.atguigu.lock;

import java.util.concurrent.locks.ReentrantLock;

//第一步  创建资源类,定义属性和和操作方法
class LTicket {
    //票数量
    private int number = 30;

    //创建可重入锁    true为公平锁,false为非公平锁,默认为false
    private final ReentrantLock lock = new ReentrantLock(true);
    //卖票方法
    public void sale() {
        //上锁
        lock.lock();
        try {
            //判断是否有票
            if(number > 0) {
                System.out.println(Thread.currentThread().getName()+" :卖出"+(number--)+" 剩余:"+number);
            }
        } finally {
            //解锁
            lock.unlock();
        }
    }
}

public class LSaleTicket {
    //第二步 创建多个线程,调用资源类的操作方法
    //创建三个线程
    public static void main(String[] args) {

        LTicket ticket = new LTicket();

        new Thread(()-> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"AA").start();

        new Thread(()-> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"BB").start();

        new Thread(()-> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"CC").start();
    }
}
6.可重入锁(递归锁)

lock的可重入锁实现

package com.atguigu.self;


import com.atguigu.sync.SyncLockDemo;

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

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

        //Lock演示可重入锁
        Lock lock = new ReentrantLock();
        //创建线程
        new Thread(() -> {
            try {
                //上锁
                lock.lock();
                System.out.println(Thread.currentThread().getName() + " 外层");

                try {
                    //上锁
                    lock.lock();
                    System.out.println(Thread.currentThread().getName() + " 内层");
                } finally {
                    //释放锁
                    lock.unlock();
                }
            } finally {
                //释放锁
                lock.unlock();
            }
        }, "t1").start();

        //创建新线程
        new Thread(() -> {
            lock.lock();
            System.out.println("aaaa");
            lock.unlock();
        }, "aa").start();

    }
}
synchronized的可重入锁实现
package com.atguigu.self;

public class Test {

    public static void main(String[] args) {

        // synchronized
        Object o = new Object();
        new Thread(()->{
            synchronized(o) {
                System.out.println(Thread.currentThread().getName()+" 外层");

                synchronized (o) {
                    System.out.println(Thread.currentThread().getName()+" 中层");

                    synchronized (o) {
                        System.out.println(Thread.currentThread().getName()+" 内层");
                    }
                }
            }

        },"t1").start();
    }
}

7.死锁

 下面是一个死锁案例:

package com.atguigu.sync;

import java.util.concurrent.TimeUnit;


public class DeadLock {

    //创建两个对象
    static Object a = new Object();
    static Object b = new Object();

    public static void main(String[] args) {
        new Thread(()->{
            synchronized (a) {
                System.out.println(Thread.currentThread().getName()+" 持有锁a,试图获取锁b");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (b) {
                    System.out.println(Thread.currentThread().getName()+" 获取锁b");
                }
            }
        },"A").start();

        new Thread(()->{
            synchronized (b) {
                System.out.println(Thread.currentThread().getName()+" 持有锁b,试图获取锁a");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (a) {
                    System.out.println(Thread.currentThread().getName()+" 获取锁a");
                }
            }
        },"B").start();
    }
}

注意:程序运行但又没反应不一定是死锁,我们可以验证一个程序是否是死锁。

验证上方代码是否为死锁:使用jps -l和jstack命令

第一步:使程序处于运行中

第二步:按图示输入命令

之后程序会显示:

 

由此可知,上述为死锁。

8.Callable接口---没明白(使用了FutureTask)

 Callable有返回值,有异常。Runnable无返回值,无异常。

 9.JUC的辅助类

CountDownLatch---减少计数

需求:6个同学陆续离开教室之后班长锁门。

代码如下:下面是不使用CountDownLatch

package com.atguigu.self;

import java.util.concurrent.ExecutionException;

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        //6个同学陆续离开教室之后
        for (int i = 1; i <=6; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+" 号同学离开了教室");
            },String.valueOf(i)).start();
        }
        System.out.println(Thread.currentThread().getName()+" 班长锁门走人了");
    }
}

1 号同学离开了教室
4 号同学离开了教室
main 班长锁门走人了
3 号同学离开了教室
2 号同学离开了教室
6 号同学离开了教室
5 号同学离开了教室

Process finished with exit code 0

问题:出现了提前锁门

解决方案:

代码如下:

package com.atguigu.juc;

import java.util.concurrent.CountDownLatch;

//演示 CountDownLatch
public class CountDownLatchDemo {
    //6个同学陆续离开教室之后,班长锁门
    public static void main(String[] args) throws InterruptedException {

        //创建CountDownLatch对象,设置初始值
        CountDownLatch countDownLatch = new CountDownLatch(6);

        //6个同学陆续离开教室之后
        for (int i = 1; i <=6; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+" 号同学离开了教室");

                //计数  -1
                countDownLatch.countDown();

            },String.valueOf(i)).start();
        }

        //等待
        countDownLatch.await();

        System.out.println(Thread.currentThread().getName()+" 班长锁门走人了");
    }
}

CycleBarrier---循环栅栏

需求:集齐七颗龙珠召唤神龙

package com.atguigu.juc;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

//集齐7颗龙珠就可以召唤神龙
public class CyclicBarrierDemo {

    //创建固定值
    private static final int NUMBER = 7;

    public static void main(String[] args) {
        //创建CyclicBarrier
        CyclicBarrier cyclicBarrier =
                new CyclicBarrier(NUMBER,()->{
                    System.out.println("*****集齐7颗龙珠就可以召唤神龙");
                });

        //集齐七颗龙珠过程
        for (int i = 1; i <=7; i++) {
            new Thread(()->{
                try {
                    System.out.println(Thread.currentThread().getName()+" 星龙被收集到了");
                    //等待
                    cyclicBarrier.await();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            },String.valueOf(i)).start();
        }
    }
}

Semaphore---信号灯

需求:6辆汽车,停3个车位

package com.atguigu.juc;

import java.util.Random;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

//6辆汽车,停3个车位
public class SemaphoreDemo {
    public static void main(String[] args) {
        //创建Semaphore,设置许可数量
        Semaphore semaphore = new Semaphore(3);

        //模拟6辆汽车
        for (int i = 1; i <=6; i++) {
            new Thread(()->{
                try {
                    //抢占
                    semaphore.acquire();

                    System.out.println(Thread.currentThread().getName()+" 抢到了车位");

                    //设置随机停车时间
                    TimeUnit.SECONDS.sleep(new Random().nextInt(5));

                    System.out.println(Thread.currentThread().getName()+" ------离开了车位");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //释放
                    semaphore.release();
                }
            },String.valueOf(i)).start();
        }
    }
}

10.读写锁---对读写锁存在疑惑

 

 表锁为修改时整个表都锁柱,行锁为修改时那行数据被锁住。行锁会发生死锁。

读锁和写锁都会发生死锁。

package com.atguigu.readwrite;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

//资源类
class MyCache {
    //创建map集合
    private volatile Map map = new HashMap<>();

    //创建读写锁对象
    private ReadWriteLock rwLock = new ReentrantReadWriteLock();

    //放数据
    public void put(String key,Object value) {
        //添加写锁
        rwLock.writeLock().lock();

        try {
            System.out.println(Thread.currentThread().getName()+" 正在写操作"+key);
            //暂停一会
            TimeUnit.MICROSECONDS.sleep(300);
            //放数据
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+" 写完了"+key);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //释放写锁
            rwLock.writeLock().unlock();
        }
    }

    //取数据
    public Object get(String key) {
        //添加读锁
        rwLock.readLock().lock();
        Object result = null;
        try {
            System.out.println(Thread.currentThread().getName()+" 正在读取操作"+key);
            //暂停一会
            TimeUnit.MICROSECONDS.sleep(300);
            result = map.get(key);
            System.out.println(Thread.currentThread().getName()+" 取完了"+key);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //释放读锁
            rwLock.readLock().unlock();
        }
        return result;
    }
}

public class ReadWriteLockDemo {
    public static void main(String[] args) throws InterruptedException {
        MyCache myCache = new MyCache();
        //创建线程放数据
        for (int i = 1; i <=5; i++) {
            final int num = i;
            new Thread(()->{
                myCache.put(num+"",num+"");
            },String.valueOf(i)).start();
        }

        TimeUnit.MICROSECONDS.sleep(300);

        //创建线程取数据
        for (int i = 1; i <=5; i++) {
            final int num = i;
            new Thread(()->{
                myCache.get(num+"");
            },String.valueOf(i)).start();
        }
    }
}

11.读写锁的降级

写锁降级为读锁(读锁是不能升为写锁的),使写的同时可以读

 

package com.atguigu.readwrite;

import java.util.concurrent.locks.ReentrantReadWriteLock;

//演示读写锁降级
public class Demo1 {

    public static void main(String[] args) {
        //可重入读写锁对象
        ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
        ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();//读锁
        ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();//写锁

        //锁降级
        //1 获取写锁
        writeLock.lock();
        System.out.println("atguigu");

        //2 获取读锁
        readLock.lock();
        System.out.println("---read");

        //3 释放写锁
        writeLock.unlock();

        //4 释放读锁
        readLock.unlock();
    }
}

12.阻塞队列---BlockingQueue

ArrayBlockingQueue:由数组结构组成的有界阻塞队列

linkedBlockingQueue:由链表结构组成的有界阻塞队列

DelayQueue:使用优先级队列实现的延迟无界阻塞队列

PriorityBlockingQueue:支持优先级排序的队列

SynchronousQueue:不存储元素的阻塞队列,也即单个元素的队列

linkedTransferQueue:由链表组成的无界阻塞队列

linkedBlockingQueue:由链表组成的双向阻塞队列

核心方法演示:

        add():添加一个元素到队列中        返回boolean值        超过容量返回异常

        element():返回队列中第一个元素

        remove():删除第一个添加的元素        返回boolean值        超过容量返回异常

        offer():添加一个元素到队列中         返回boolean值        超过容量返回false

        poll():取出第一个添加的元素          

        put():添加一个元素到队列中        超过容量会阻塞

        take():取出第一个添加的元素           超过容量会阻塞

        offer("w",3L, TimeUnit.SECONDS):添加并且如果阻塞设置一定时间后关闭操作

        //创建阻塞队列
        BlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);

        //第一组
        System.out.println(blockingQueue.add("a"));
        System.out.println(blockingQueue.add("b"));
        System.out.println(blockingQueue.add("c"));
        System.out.println(blockingQueue.element());

12.ThreadPool线程池

 

package com.atguigu.pool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

//演示线程池三种常用分类
public class ThreadPoolDemo1 {
    public static void main(String[] args) {
        //一池五线程
        ExecutorService threadPool1 = Executors.newFixedThreadPool(5); //5个窗口

        //一池一线程
        ExecutorService threadPool2 = Executors.newSingleThreadExecutor(); //一个窗口

        //一池可扩容线程
        ExecutorService threadPool3 = Executors.newCachedThreadPool();
        //10个顾客请求
        try {
            for (int i = 1; i <=10; i++) {
                //执行
                threadPool3.execute(()->{
                    System.out.println(Thread.currentThread().getName()+" 办理业务");
                });
            }
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            //关闭
            threadPool3.shutdown();
        }

    }

}

代码中三种方式我们都不使用,一般使用自定义线程池方法

13.自定义线程池的方法

 

package com.atguigu.pool;

import java.util.concurrent.*;

//自定义线程池创建
public class ThreadPoolDemo2 {
    public static void main(String[] args) {
        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                5,
                2L,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );

        //10个顾客请求
        try {
            for (int i = 1; i <=10; i++) {
                //执行
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+" 办理业务");
                });
            }
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            //关闭
            threadPool.shutdown();
        }
    }
}
14.分支合并框架

拆分再合并解决问题

package com.atguigu.forkjoin;

import java.util.concurrent.*;

class MyTask extends RecursiveTask {

    //拆分差值不能超过10,计算10以内运算
    private static final Integer VALUE = 10;
    private int begin ;//拆分开始值
    private int end;//拆分结束值
    private int result ; //返回结果

    //创建有参数构造
    public MyTask(int begin,int end) {
        this.begin = begin;
        this.end = end;
    }

    //拆分和合并过程
    @Override
    protected Integer compute() {
        //判断相加两个数值是否大于10
        if((end-begin)<=VALUE) {
            //相加操作
            for (int i = begin; i <=end; i++) {
                result = result+i;
            }
        } else {//进一步拆分
            //获取中间值
            int middle = (begin+end)/2;
            //拆分左边
            MyTask task01 = new MyTask(begin,middle);
            //拆分右边
            MyTask task02 = new MyTask(middle+1,end);
            //调用方法拆分
            task01.fork();
            task02.fork();
            //合并结果
            result = task01.join()+task02.join();
        }
        return result;
    }
}

public class ForkJoinDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建MyTask对象
        MyTask myTask = new MyTask(0,100);
        //创建分支合并池对象
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask forkJoinTask = forkJoinPool.submit(myTask);
        //获取最终合并之后结果
        Integer result = forkJoinTask.get();
        System.out.println(result);
        //关闭池对象
        forkJoinPool.shutdown();
    }
}
15.异步回调

package com.atguigu.completable;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

//异步调用和同步调用
public class CompletableFutureDemo {
    public static void main(String[] args) throws Exception {
        //同步调用    无返回值
        CompletableFuture completableFuture1 = CompletableFuture.runAsync(()->{
            System.out.println(Thread.currentThread().getName()+" : CompletableFuture1");
        });
        completableFuture1.get();

        //mq消息队列
        //异步调用    有返回值
        CompletableFuture completableFuture2 = CompletableFuture.supplyAsync(()->{
            System.out.println(Thread.currentThread().getName()+" : CompletableFuture2");
            //模拟异常
            int i = 10/0;
            return 1024;
        });
        completableFuture2.whenComplete((t,u)->{
            System.out.println("------t="+t);   // 方法返回值
            System.out.println("------u="+u);   // 异常
        }).get();

    }
}

 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/285229.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号