Java(二十四)-- Java并发编程(二)

目录

Lock

用 synchronized 实现卖票

用 Lock 完成卖票

synchronized 和 lock 的区别 

公平锁

非公平锁

生产者消费者模式

synchronized

Lock

ConcurrentModificationException

并发访问异常

如何解决?

Set

Map


Lock

        JUC 提供的一种锁机制,功能和 synchronized 类似,是对 synchronized 的升级,它是一个接口。

        

        它的常用实现类是 ReentrantLock

        synchronized 是通过 JVM 实现锁机制,ReentrantLock 是通过 JDK 实现锁机制

        synchronized 是一个关键字,ReentrantLock 是一个类

        重入锁:可以给同一个资源添加多把锁。

        synchronized 是线程执行完毕之后自动释放锁,ReentrantLock 需要手动解锁

用 synchronized 实现卖票

import java.util.concurrent.TimeUnit;
​
public class Test {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"B").start();
    }
}
​
class Ticket{
    private Integer saleNum = 0;
    private Integer lastNum = 30;
​
    public synchronized void sale(){
        if(lastNum > 0){
            saleNum++;
            lastNum--;
            try {
                TimeUnit.MILLISECONDS.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"卖出了第"+saleNum+"张票,剩余"+lastNum+"张票");
        }
    }
​
}

用 Lock 完成卖票

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
​
public class Test2 {
    public static void main(String[] args) {
        Ticket2 ticket = new Ticket2();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"B").start();
    }
}
​
class Ticket2{
    private Integer saleNum = 0;
    private Integer lastNum = 30;
    private Lock lock = new ReentrantLock();
​
    public void sale(){
​
        lock.lock();
        lock.lock();
        if(lastNum > 0){
            saleNum++;
            lastNum--;
            try {
                TimeUnit.MILLISECONDS.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"卖出了第"+saleNum+"张票,剩余"+lastNum+"张票");
        }
        lock.unlock();
        lock.unlock();
    }
}

synchronized 和 lock 的区别 

        1、synchronized 自动上锁,自动释放锁,Lock 手动上锁,手动释放锁。

        2、synchronized 无法判断是否获取到了锁,Lock 可以判断是否拿到了锁。

        3、synchronized 拿不到锁就会一直等待,Lock 不一定会一直等待。

        4、synchronized 是 Java 关键字,Lock 是接口。

        5、synchronized 是非公平锁,Lock 可以设置是否为公平锁。

公平锁

        很公平,排队,当锁没有被占用时,当前线程需要判断队列中是否有其他等待线程。

非公平锁

        不公平,插队,当锁没有被占用时,当前线程可以直接占用,而不需要判断当前队列中是否有等待线程。

        实际开发中推荐使用 Lock 的方式。

        ReentrantLock 具备限时性的特点,可以判断某个线程在一定的时间段内能否获取到锁,使用 tryLock 方法,返回值是 boolean 类型,true 表示可以获取到锁,false 表示无法获取到锁

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
​
public class Test {
    public static void main(String[] args) {
        TimeLock timeLock = new TimeLock();
        new Thread(()->{
            timeLock.getLock();
        },"A").start();
        new Thread(()->{
            timeLock.getLock();
        },"B").start();
    }
}
​
class TimeLock{
    private ReentrantLock lock = new ReentrantLock();
​
    public void getLock(){
        try {
            if(lock.tryLock(3, TimeUnit.SECONDS)){
                System.out.println(Thread.currentThread().getName()+"拿到了锁");
                TimeUnit.SECONDS.sleep(5);
            }else{
                System.out.println(Thread.currentThread().getName()+"拿不到锁");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 判断是否是当前线程占用的锁
            if(lock.isHeldByCurrentThread()){
                lock.unlock();
            }
        }
    }
​
}

生产者消费者模式

synchronized

import java.util.concurrent.TimeUnit;
​
public class Test {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 30; i++) {
                data.increment();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 30; i++) {
                data.decrement();
            }
        },"B").start();
    }
}
​
class Data{
    private Integer num = 0;
​
    public synchronized void increment(){
        while(num!=0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        num++;
        this.notify();
        System.out.println(Thread.currentThread().getName()+"生产了汉堡"+num);
    }
​
    public synchronized void decrement(){
        while(num == 0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        num--;
        this.notify();
        System.out.println(Thread.currentThread().getName()+"消费了汉堡"+num);
    }
}

        必须使用 while 判断,不能用 if,因为 if 会存在线程虚假唤醒的问题,虚假唤醒就是一些 wait 方法会在除了 notify 的其他情况被唤醒,不是真正的唤醒,使用 while 完成多重检测,避免这一问题。

Lock

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
​
public class Test {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 30; i++) {
                data.increment();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 30; i++) {
                data.decrement();
            }
        },"B").start();
    }
}
​
class Data{
    private Integer num = 0;
​
    private Lock lock = new ReentrantLock();
​
    private Condition condition = lock.newCondition();
​
    public void increment(){
        lock.lock();
        while(num!=0){
            try {
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        num++;
        condition.signal();
        System.out.println(Thread.currentThread().getName()+"生产了汉堡"+num);
        lock.unlock();
    }
​
    public synchronized void decrement(){
        lock.lock();
        while(num == 0){
            try {
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        num--;
        condition.signal();
        System.out.println(Thread.currentThread().getName()+"消费了汉堡"+num);
        lock.unlock();
    }
}

        使用 Lock 锁,就不能通过 wait 和 notify 来暂停线程和唤醒线程,而应该使用 Condition 的 await() 和 signal() 来暂停和唤醒线程

ConcurrentModificationException

并发访问异常

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
​
public class Test {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                try {
                    TimeUnit.MILLISECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //写
                list.add("a");
                //读
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

如何解决?

        1、Vector(ArrayList 线程不安全,Vector 线程安全)

        

         

        2、Collections.synchronizedList

package com.icss.test2;

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

public class Test {
    public static void main(String[] args) {
        List<String> list = Collections.synchronizedList(new ArrayList<>());
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                try {
                    TimeUnit.MILLISECONDS.sleep(1);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                list.add("a");
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

        3、JUC:CopyOnWriteArrayList

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
​
public class Test2 {
    public static void main(String[] args) {
        List<String> list = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 10; i++) {
            new Thread((()->{
                try {
                    TimeUnit.MILLISECONDS.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                list.add("a");
                System.out.println(list);
            })).start();
        }
    }
}

        CopyOnWrite 写时复制,当我们往一个容器添加元素的时候,不是直接给容器添加,而是先将当前容器复制一份,向新的容器中添加数据,添加完成之后,再将原容器的引用指向新的容器。

 

Set

import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.TimeUnit;
​
public class Test2 {
    public static void main(String[] args) {
        Set<String> set = new CopyOnWriteArraySet<>();
        for (int i = 0; i < 10; i++) {
            final int temp = i;
            new Thread((()->{
                try {
                    TimeUnit.MILLISECONDS.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                set.add(String.valueOf(temp)+"a");
                System.out.println(set);
            })).start();
        }
    }
}

Map

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.TimeUnit;
​
public class Test2 {
    public static void main(String[] args) {
        Map<String,String> map = new ConcurrentHashMap<>();
        for (int i = 0; i < 10; i++) {
            final int temp = i;
            new Thread((()->{
                try {
                    TimeUnit.MILLISECONDS.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                map.put(UUID.randomUUID().toString().substring(0,3),UUID.randomUUID().toString().substring(0,2));
                System.out.println(map);
            })).start();
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bug 消灭师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值