java多线程和并发--java并发协作控制1

线程协作

  • Thread/Executor/Fork-Join
    – 线程启动,运行,结束
    – 线程之间缺少协作
  • synchronized 同步
    – 限定只有一个线程才能进入关键区
    – 简单粗暴,性能损失有点大
    如何做到线程之间的协作,比如1号线程与2号线程在执行到某一个地方的时候,要会合一下

Lock

  • Lock也可以实现同步的效果
    – 实现更复杂的临界区结构
    – tryLock方法可以预判是否空闲
    – 允许分离读写的操作,多个读,一个写
    – 性能更好
  • ReentrantLock类,可重入的互斥锁
  • ReentrantReadWriteLock类,可重入的读写锁
  • lock和unlock函数
    Lock是synchronized的升级版,synchronized能实现的功能,Lock也都能实现,它可以实现更复杂的协作

ReentrantLock可重入互斥锁示例:

package com.torey;
import java.util.concurrent.locks.ReentrantLock;
/**
 * 有家奶茶店,点单有时需要排队
 * 假设想买奶茶的人如果看到需要排队,就决定不买
 * @ClassName:BuyMilkTeaTest
 * @Description:
 * @author: Torey
 */
public class BuyMilkTeaTest {
    //可重入锁
    private static final ReentrantLock queueLock=new ReentrantLock();
    public static void main(String[] args) throws InterruptedException {
//可重入锁示例
        buyMilkTea();
    }
    /**
     * 学生买奶茶
     * @throws InterruptedException
     */
    public static void buyMilkTea() throws InterruptedException {
        BuyMilkTeaTest lockExample=new BuyMilkTeaTest();
        int STUDENTS_CNT=10;
        //有10个学生
        Thread[] students = new Thread[STUDENTS_CNT];
        for (int i = 0; i < STUDENTS_CNT; i++) {
            students[i]=new Thread(new Runnable() {
                @Override
                public void run() {
                    long walkingTime= (long)(Math.random()*1000);
                    try {
                        Thread.sleep(walkingTime);
                        lockExample.tryToBuyMilkTea();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        System.out.println(e.getMessage());
                    }
                }
            });
            students[i].start();
        }
        for (int i = 0; i < STUDENTS_CNT; i++) {
            students[i].join();
        }
    }
    /**
     * 排队买奶茶
     * @throws InterruptedException
     */
    public void tryToBuyMilkTea() throws InterruptedException {
        boolean flag=true;
        while (flag) {
            //可重入锁,排队买奶茶,如果里面有线程,则需要等等
            if (queueLock.tryLock()) {
                // queueLock.lock();//tryLock
                long thinkingTime = (long) (Math.random() * 500);
                Thread.sleep(thinkingTime);
                System.out.println(Thread.currentThread().getName() + ":来一杯奶茶,排队需要"+thinkingTime+"毫秒");
                flag=false;
                queueLock.unlock();
            }else {
                //System.out.println(Thread.currentThread().getName() + ": " + queueLock.getQueueLength() + "人");
                System.out.println(Thread.currentThread().getName() + ": 再等等");
            }
            if (flag) {
                Thread.sleep(2000);
            }
        }
    }
}

ReentrantReadWriteLock可重入互斥锁示例:

package com.torey;


import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @ClassName:LockExample
 * @Description:
 * @author: Torey
 */
public class LockExample {
    //可重入读写锁
    private static final ReentrantReadWriteLock orderLock=new ReentrantReadWriteLock();
    /**
     * 奶茶店有老板和多名员工,记单方式比较原始,只有一个订单本
     * 老板负责写新订单,员工不断地查看订单本得到信息来制作奶茶,在老板写新订单时员工不能看订单本
     * 多个员工可同时看订单本,在员工看时老板不能写新订单
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {
        //可重入读写锁
        handleOrder();//需手动关闭
    }
    public void addOrder() throws InterruptedException {
        //writeLock 写锁,排他的,只能一个线程拥有
        orderLock.writeLock().lock();
        long writingTime= (long) (Math.random()*1000);
        Thread.sleep(writingTime);
        System.out.println(Thread.currentThread().getName()+"老板:新加一笔订单");
        orderLock.writeLock().unlock();
    }
    /**
     * readLock 读锁,可以多个线程共享
     * writeLock 写锁,排他的,只能一个线程拥有
     * @throws InterruptedException
     */
    public void viewOrder() throws InterruptedException {
        //readLock 读锁,可以多个线程共享
        orderLock.readLock().lock();
        long readingTime= (long)(Math.random()*500);
        Thread.sleep(readingTime);
        System.out.println(Thread.currentThread().getName() + ": 查看订单本");
        orderLock.readLock().unlock();
    }
    public static void handleOrder(){
        LockExample lockExample=new LockExample();
        //老板写菜单
        Thread boss = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        lockExample.addOrder();
                        long watingTime= (long)(Math.random()*1000);
                        Thread.sleep(watingTime);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        boss.start();
        //三个员工
        int workerCnt=3;
        Thread[] workers = new Thread[workerCnt];
        //员工看菜单
        for (int i = 0; i < workerCnt; i++) {
           workers[i] = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        try {
                            lockExample.viewOrder();
                            long workingTime= (long)(Math.random()*5000);
                            Thread.sleep(workingTime);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
           workers[i].start();
        }
    }
}

Semaphore

  • 信号量,由1965年Dijkstra提出的
  • 信号量:本质上是一个计数器
  • 计数器大于0,可以使用,等于0不能使用
  • 可以设置多个并发量,例如限制10个访问
  • Semaphore有两个重要的方法
    – acquire 获取一个信号量,信号量减一
    – release 释放一个信号量,信号量加一
  • 比Lock更进一步,可以控制多个同时访问关键区

Semaphore示例:

package com.torey;

import java.util.concurrent.Semaphore;

/**
 * @ClassName:SemaphoreExample
 * @Description:
 * @author: Torey
 */
public class SemaphoreExample {
    //5个车位
    private final Semaphore placeSemaphore=new Semaphore(5);
    public boolean parking(){
        //每次执行到这里,都会减一,5个都减完后,线程就进不来了
        if (placeSemaphore.tryAcquire()) {
            System.out.println(Thread.currentThread().getName() + ": 停车成功");
            return true;
        }else {
            System.out.println(Thread.currentThread().getName() + ":没有空位");
            return false;
        }
    }
    public void leaving(){
        //release() 信号量加一
        placeSemaphore.release();
        System.out.println(Thread.currentThread().getName() + ": 走开");
    }

    /**
     * 现有一地下车库,共有车位5个,10辆车需要停放,每次停放时,去生气信号量
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {
        int tryToParkCnt=10;
        SemaphoreExample semaphoreExample = new SemaphoreExample();
        Thread[] parkers = new Thread[tryToParkCnt];
        for (int i = 0; i < tryToParkCnt; i++) {
           parkers[i]= new Thread(new Runnable() {
                @Override
                public void run() {
                    long randomTime= (long)(Math.random()*1000);
                    try {
                        Thread.sleep(randomTime);
                        if (semaphoreExample.parking()) {
                            //车停一段时间后,parkingTime长时间
                            long parkingTime= (long)(Math.random()*1200);
                            Thread.sleep(parkingTime);
                            //车开走
                            semaphoreExample.leaving();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
           parkers[i].start();
        }
        for (int i = 0; i < tryToParkCnt; i++) {
            parkers[i].join();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值