同步技术学习(volatile,CountDownLatch,LockSupport,Semaphore,Wait()和notify())

Q:
实现一个容器,提供两个方法add、size,写两个线程:
线程1,添加10个元素到容器中
线程2,实时监控元素个数,当个数到达5个时,线程2给出提示并结束

初步想法:

程序1

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @BelongsProject: test22
 * @BelongsPackage: PACKAGE_NAME
 * @Author: 高传威
 * @CreateTime: 2020-07-19 16:21
 * @Description:
 */
public class T01_withoutVolatile {
    List list = new ArrayList();
    
    public void add(Object o){
        list.add(0);
    }
    
    public int size(){
        return list.size();
    }

    public static void main(String[] args) {
        T01_withoutVolatile c = new T01_withoutVolatile();
        new Thread(()->{
            for (int i=0;i<10;i++){
                c.add(new Object());
                System.out.println("add "+i);
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"t1").start();
        new Thread(()->{
            while (true){
                if(c.size()==5){
                    break;
                }
            }
            System.out.println("t2 结束");
        },"t2").start();
    }
}

运行结果:
在这里插入图片描述
实现与题目不符,原因:

  • 方法没有加同步
  • while(true)中的c.size()方法永远没有检测到,因为线程与线程之间是不可见的。

于是添加volatile解决上述问题

程序2

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @BelongsProject: test22
 * @BelongsPackage: PACKAGE_NAME
 * @Author: 高传威
 * @CreateTime: 2020-07-19 16:36
 * @Description:
 */
public class T02_withVolatile {
    volatile List list = new ArrayList();

    public void add(Object o){
        list.add(0);
    }

    public int size(){
        return list.size();
    }

    public static void main(String[] args) {
        T02_withVolatile c = new T02_withVolatile();
        new Thread(()->{
            for (int i=0;i<10;i++){
                c.add(new Object());
                System.out.println("add "+i);
                /*try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }*/
            }
        },"t1").start();
        new Thread(()->{
            while (true){
                if(c.size()==5){
                    break;
                }
            }
            System.out.println("t2 结束");
        },"t2").start();
    }
}

使用volatile修饰List集合,实现线程间信息的传递。
运行结果:
在这里插入图片描述
仍然与题目不符,原因:

  • volatile修饰引用类型,这个引用对象指向另外一个new出来的对象,如果这个对象的成员变量值改变了,是无法观察到的。

程序3

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @BelongsProject: test22
 * @BelongsPackage: PACKAGE_NAME
 * @Author: 高传威
 * @CreateTime: 2020-07-19 16:43
 * @Description:
 */
public class T03_NotifyHoldingLock {
    //添加volatile,使得t2能够得到通知
    volatile List list = new ArrayList();

    public void add(Object o){
        list.add(o);
    }

    public int size(){
        return list.size();
    }

    public static void main(String[] args) {
        T03_NotifyHoldingLock c = new T03_NotifyHoldingLock();
        final Object lock = new Object();
        //需要注意先启动t2再启动t1
        new Thread(()->{
            synchronized (lock){
                System.out.println("t2 启动");
                if(c.size()!=5){
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("t2 结束");
            }
        },"t2").start();
        new Thread(()->{
            System.out.println("t1 启动");
            synchronized (lock){
                for (int i=0;i<10;i++){
                    c.add(new Object());
                    System.out.println("add "+i);
                    
                    if(c.size() == 5){
                        lock.notify();
                    }
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        },"t1").start();
    }
}

利用synchronized锁的方式(利用wait()、notify()),通过给object对象加锁,然后调用wait()方法和notify()方法。
运行结果:
在这里插入图片描述
仍然不符合题意,因为:

  • notify()方法不释放锁,当t1线程调用了notify()方法后,并没有释放当前的锁,所以t1还是会执行下去,等到t1执行完毕,t2线程才能被唤醒。

程序4:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @BelongsProject: test22
 * @BelongsPackage: PACKAGE_NAME
 * @Author: 高传威
 * @CreateTime: 2020-07-19 16:57
 * @Description:
 */
public class T04_NotifyHoldingLock {
    //添加volatile,使得t2能够得到通知
    volatile List list = new ArrayList();

    public void add(Object o){
        list.add(o);
    }

    public int size(){
        return list.size();
    }

    public static void main(String[] args) {
        T04_NotifyHoldingLock c = new T04_NotifyHoldingLock();
        final Object lock = new Object();
        //需要注意先启动t2再启动t1
        new Thread(()->{
            synchronized (lock){
                System.out.println("t2 启动");
                if(c.size()!=5){
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("t2 结束");
            }
            //通知t1继续执行
            lock.notify();
        },"t2").start();
        new Thread(()->{
            System.out.println("t1 启动");
            synchronized (lock){
                for (int i=0;i<10;i++){
                    c.add(new Object());
                    System.out.println("add "+i);
                    if(c.size() == 5){
                        lock.notify();
                        //释放锁,让t2得以执行
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        },"t1").start();
    }
}

程序4在程序3的基础上进行了一些小改动,调用wait()方法阻塞t1线程,释放锁,让t2得以执行,实现t2的实时监控。
运行结果:
在这里插入图片描述
list集合中的对象为5个时结束,符合题意。

我们考虑使用其他同步技术来实现该问题:
程序5:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * @BelongsProject: test22
 * @BelongsPackage: PACKAGE_NAME
 * @Author: 高传威
 * @CreateTime: 2020-07-19 17:06
 * @Description:
 */
public class T05_CountDownLatch {
    //添加volatile,使t2能够得到通知
    volatile List list = new ArrayList();

    public void add(Object o){
        list.add(0);
    }

    public int size(){
        return list.size();
    }

    public static void main(String[] args) {
        T05_CountDownLatch c = new T05_CountDownLatch();
        //需要注意先启动t2再启动t1
        CountDownLatch latch = new CountDownLatch(1);
        new Thread(()->{
            System.out.println("t2 启动");
            if(c.size()!=5){
                try {
                    latch.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("t2 结束");
        },"t2").start();

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

        new Thread(()->{
            System.out.println("t1 启动");
            for (int i = 0; i < 10; i++) {
                c.add(new Object());
                System.out.println("add "+i);
                if(c.size()==5){
                    //暂停t1线程
                    latch.countDown();
                }
            }
        },"t1").start();
    }

}

使用CountDownLatch(门闩)来实现,与程序4不同,没有了锁,采用await()方法替换t2线程和t1线程的wait()方法。
运行结果:
在这里插入图片描述
与题意不符,t2没有来得及去实时监控,对其进行改进。
程序6:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * @BelongsProject: test22
 * @BelongsPackage: PACKAGE_NAME
 * @Author: 高传威
 * @CreateTime: 2020-07-19 17:17
 * @Description:
 */
public class T06_CountDownLatch {
    //添加volatile,使t2能够得到通知
    volatile List list = new ArrayList();

    public void add(Object o){
        list.add(0);
    }

    public int size(){
        return list.size();
    }

    public static void main(String[] args) {
        T06_CountDownLatch c = new T06_CountDownLatch();
        //需要注意先启动t2再启动t1
        CountDownLatch latch = new CountDownLatch(1);
        new Thread(()->{
            System.out.println("t2 启动");
            if(c.size()!=5){
                try {
                    latch.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("t2 结束");
        },"t2").start();

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

        new Thread(()->{
            System.out.println("t1 启动");
            for (int i = 0; i < 10; i++) {
                c.add(new Object());
                System.out.println("add "+i);
                if(c.size()==5){
                    //暂停t1线程
                    latch.countDown();
                    try {
                        latch.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"t1").start();
    }

}

程序6在程序5的基础上,实现在t1线程打开t2线程门闩的时候,给t1自己加一个门闩。

程序7:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

/**
 * @BelongsProject: test22
 * @BelongsPackage: PACKAGE_NAME
 * @Author: 高传威
 * @CreateTime: 2020-07-19 17:22
 * @Description:
 */
public class T07_LockSupport {
    //添加volatile,使t2能够得到通知
    volatile List list = new ArrayList();

    public void add(Object o){
        list.add(0);
    }

    public int size(){
        return list.size();
    }

    static Thread t1=null,t2=null;
    public static void main(String[] args) {
        T07_LockSupport c = new T07_LockSupport();
        CountDownLatch latch = new CountDownLatch(1);
        
        t2=new Thread(()->{
            System.out.println("t2 启动");
            if(c.size()!=5){
                LockSupport.park();
            }
            System.out.println("t2 结束");
            LockSupport.unpark(t1);
        },"t2");
        

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

        t1=new Thread(()->{
            System.out.println("t1 启动");
            for (int i = 0; i < 10; i++) {
                c.add(new Object());
                System.out.println("add "+i);
                if(c.size() ==5){
                    LockSupport.unpark(t2);
                    LockSupport.park();
                }
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"t1");
        //需要注意先启动t2再启动t1
        t2.start();
        t1.start();
    }
}

实现原理与程序5相同,只是利用LockSupport的park()方法和unpark()方法来实现。

程序8:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

/**
 * @BelongsProject: test22
 * @BelongsPackage: PACKAGE_NAME
 * @Author: 高传威
 * @CreateTime: 2020-07-19 17:36
 * @Description:
 */
public class T08_Semaphore {
    //添加volatile,使t2能够得到通知
    volatile List list = new ArrayList();

    public void add(Object o){
        list.add(0);
    }

    public int size(){
        return list.size();
    }

    static Thread t1=null,t2=null;
    public static void main(String[] args) {
        T08_Semaphore c = new T08_Semaphore();
        Semaphore s = new Semaphore(1);
        t1=new Thread(()->{
            try {
                s.acquire();
                for (int i = 0; i < 5; i++) {
                    c.add(new Object());
                    System.out.println("add "+i);
                }
                s.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                t2.start();
                t2.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                s.acquire();
                for (int i = 5; i < 10; i++) {
                    c.add(new Object());
                    System.out.println("add "+i);
                }
                s.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"t1");

        t2=new Thread(()->{
            try {
                s.acquire();
                System.out.println("t2 结束");
                s.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"t2");
        t1.start();
    }
}

围绕这一问题,共采用5种技术来实现:

  • volatile
  • wait()和notify()
  • CountDownLatch
  • LockSupport
  • Semaphore
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您好!对于您的问题,我可以为您提供一些信息。 首先,让我们来了解一下volatileCountDownLatch在全局上的作用。 1. volatile关键字: - volatileJava中的一个关键字,用于标识变量的可见性和禁止指令重排序。 - 当一个变量被声明为volatile时,每次访问该变量时都会从主内存中读取最新的值,而不是使用线程的本地缓存。 - 这可以确保不同线程之间对该变量的修改是可见的,并避免了由于指令重排序导致的并发问题。 2. CountDownLatch: - CountDownLatchJava提供的一个并发工具类,用于控制多个线程之间的同步。 - 它基于一个计数器的方式工作,初始值可以设定为任意整数。 - 当一个线程完成了自己的任务后,可以调用CountDownLatchcountDown()方法将计数器减1。 - 其他等待的线程可以通过调用await()方法等待计数器变为0。 - 一旦计数器变为0,所有等待的线程将被唤醒继续执行。 在全局上使用volatileCountDownLatch有以下几点考虑: - volatile关键字主要用于保证变量在多线程环境下的可见性和一致性,适合用于标识共享变量。 - CountDownLatch主要用于控制多个线程之间的同步,适合用于多个线程协同完成某个任务的场景。 综上所述,volatileCountDownLatch可以在全局上使用,但是主要的应用场景和作用是不同的。请根据具体需求选择合适的使用方式。希望这些信息对您有帮助!如果您还有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值