笔记(课14)(2)

一阶段:第14天:多线程同步(8.10)

一.多线程访问邻接资源

一个线程在访问临界资源的时候,如果给这个资源“上一把锁”,这个时候如果其他线程也要访问这个资源,就得在“锁”外面等待。
锁:任意的对象都可以被当做锁来使用
同步:Synchronized:有等待
异步:Asynchronized:没有等待,各执行各的

案例一:卖票(4个窗口共卖100张票)

public class Demo1 {
    public static void main(String[] args) {
        Ticket ticket=new Ticket();
        new Thread(ticket,"1 ").start();
        new Thread(ticket,"2 ").start();
        new Thread(ticket,"3 ").start();
        new Thread(ticket,"4 ").start();
    }
}
public class Ticket implements Runnable {
    private int ticket=100;
    private Object lock=new Object();
    @Override
    public void run() {
        while (true){
            /*synchronized (lock) {//不能直接new,eg:synchronized (new Object())不可以,但是this可以
                if (ticket<1) {
                    break;
                }
                System.out.println(Thread.currentThread().getName()+"卖了第"+ticket+"张票");
                ticket--;
            }*/
            if (!sellTicket()){
                break;
            }
        }
    }
    //同步方法
    public synchronized boolean sellTicket(){//非静态方法锁就是this 静态方法锁是当前类的类对象
        if (ticket<1) {
            return false;
        }
        System.out.println(Thread.currentThread().getName()+"卖了第"+ticket+"张票");
        ticket--;
        return true;
    }
}

案例二:存钱取钱

public class Demo2 {
    public static void main(String[] args) {
        BankCard bankCard=new BankCard();
        AddMoney addMoney=new AddMoney(bankCard);
        SubMoney subMoney=new SubMoney(bankCard);
        new Thread(addMoney,"少泊").start();
        new Thread(subMoney,"小海").start();
    }
}
该类必须创建,为了之后保证存钱和取钱锁的是同一个对象
public class BankCard {
    private double money;
    public BankCard() {
    }
    public BankCard(double money) {
        this.money = money;
    }
    public double getMoney() {
        return money;
    }
    public void setMoney(double money) {
        this.money = money;
    }
}
注意:创建了个card,为了保证存钱和取钱锁的是同一个对象
public class AddMoney implements Runnable {
    private BankCard card;
    public AddMoney(BankCard card) {
        this.card = card;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            synchronized (card) {
                card.setMoney(card.getMoney()+1000);
                System.out.println("存入1000元,余额为:"+card.getMoney());
            }
        }
    }
}
注意:创建了个card,为了保证存钱和取钱锁的是同一个对象
public class SubMoney implements Runnable {
    private BankCard card;
    public SubMoney(BankCard card) {
        this.card = card;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            synchronized (card) {
                if (card.getMoney()>1000){
                    card.setMoney(card.getMoney()-1000);
                    System.out.println("取了1000,余额为"+card.getMoney());
                }else {
                    System.out.println("余额不足");
                    i--;
                }
            }
        }
    }
}
从jdk1.5之后加入新的接口 Lock,ReentrantLock是Lock接口的实现类。
通过显式定义同步锁对象来实现同步,同步锁提供了比synchronized代码块更广泛的锁定操
注意:最好将 unlock的操作放到finally块中
通过使用ReentrantLock这个类来进行锁的操作,它实现了Lock接口,使用ReentrantLock可以显式地加锁、释放锁
 * 使用Lock接口来实现
public class Ticket implements Runnable {
    private int ticket=100;
    Lock lock=new ReentrantLock();
    public Ticket() {
    }
    public Ticket(int ticket, Lock lock) {
        this.ticket = ticket;
        this.lock = lock;
    }
    public int getTicket() {
        return ticket;
    }
    public void setTicket(int ticket) {
        this.ticket = ticket;
    }
    public Lock getLock() {
        return lock;
    }
    public void setLock(Lock lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
        while (true){
            lock.lock();
            try {
                if (ticket<1) {
                    break;
                }
                System.out.println(Thread.currentThread().getName()+"卖了第"+ticket+"张票");
                ticket--;
            } finally {//可以跨方法,可以在别的方法里解锁
                lock.unlock();
            }
        }
    }
}
public class Demo2 {
    public static void main(String[] args) {
        BankCard bankCard=new BankCard();
        Lock lock=new ReentrantLock();//*
        AddMoney addMoney=new AddMoney(bankCard,lock);
        //SubMoney subMoney=new SubMoney(bankCard,lock);
        new Thread(addMoney,"少泊").start();
        //new Thread(subMoney,"小海").start();
    }
}
死锁的条件:
	1两个以上的线程
	2至少两个锁以上
	3同步中嵌套同步

二.多线程在单例中的应用

public class SingleTon {
    private SingleTon(){
        //禁止反射破解
        synchronized (SingleTon.class) {
            if (instance!=null){
                throw new RuntimeException("不能使用反射创建对象");
            }
        }
    }
    //volatile:不稳定的,易挥发的
    //为了保证线程可见性,禁止指令重排序,防止空指针异常
    //正常:1.开辟空间 2.初始化 3.返回地址
    //重排序:1.开辟空间 3.返回地址 2.初始化 --->容易发生空指针异常
    private static volatile SingleTon instance;
    public SingleTon getInstance(){
        if (instance==null) {//双重检查 double check,为了提高效率
            synchronized (SingleTon.class) {
                if (instance == null) {
                    instance = new SingleTon();
                }
            }
        }
        return SingleTon.instance;
    }
}
/*单例的其它写法
静态内部类写法:饿汉和懒汉结合
(1)节省空间
(2)不会出现线程安全问题
public class SingleTon2{
    private SingleTon2(){
        //禁止反射破解
        synchronized (SingleTon.class) {
            if (instance!=null){
                throw new RuntimeException("不能使用反射创建对象");
            }
        }
    }
    static class Holder{
        private static SingleTon2 INSTANCE=new SingleTon2();
    }
    public static SingleTon2 getInstance(){
        return Holder.INSTANCE;
    }
}
使用枚举
(1)没有线程安全问题(有点浪费空间)
(2)没有反射破解问题
public enum SingleTon3{
    INSTANCE;//静态常量
    public static SingleTon3 getInstance(){
        return INSTANCE;
    }
}
 */

三.线程的通信

wait(): 等待,线程执行这个方法进入等待队列(和锁有关,一个锁对应一个等待队列), 需要被唤醒
notify(): 通知唤醒,从等待队列中随机唤醒一个线程
notifyAll():全部唤醒,把等待队列中所有的线程都唤醒

public class BankCard {
    private double money;
    private boolean flag;//true:有钱,可以取 false:没钱,可以存
    //存钱
    public synchronized void save(){//this
        while /*if*/ (flag==true){//有钱
            try {
                this.wait();//锁的wait;等待,释放cpu和锁
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //存
        money=money+1000;
        System.out.println(Thread.currentThread().getName()+"存了1000,余额是:"+money);
        flag=true;//修改标记
        //this.notify();//唤醒一个,取钱线程,当人多时,容易发生死锁,都睡了
        this.notifyAll();//唤醒所有
    }
    //取钱
    public synchronized void take(){//this
        if (flag==false){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        money=money-1000;
        System.out.println(Thread.currentThread().getName()+"取了1000,余额是:"+money);
        flag=false;//修改标记
        //this.notify();//唤醒一个,通知存钱线程去存钱
        this.notifyAll();//唤醒所有
    }
}

四.读写锁

ReadWriteLock接口:可以实现多个读线程同时读取数据,写线程需要互斥执行。
特点:
读|写 、写|写 需要互斥
读|读 不需要互斥

 * 读写锁
public class Demo1 {
    public static void main(String[] args) {
        //共享资源
        Res res=new Res();
        //创建锁
        ReentrantReadWriteLock lock=new ReentrantReadWriteLock();
        //写入(互斥)
        //匿名内部类
        Runnable runnable=new Runnable() {
            @Override
            public void run() {
                lock.writeLock().lock();
                try {
                    int  n;
                    n = new Random().nextInt(1000);
                    res.setNum(n);
                    System.out.println(Thread.currentThread().getName()+"写入"+n);
                } finally {
                    lock.writeLock().unlock();
                }
            }
        };
        //读取(共享锁)
        Runnable runnable1=new Runnable() {
            @Override
            public void run() {
                lock.readLock().lock();;
                try {
                    int num=res.getNum();
                    System.out.println(Thread.currentThread().getName()+"读取"+num);
                } finally {
                    lock.readLock().unlock();
                }
            }
        };
        //创建线程
        for (int i = 0; i < 10; i++) {
            new Thread(runnable).start();
        }
        for (int i = 0; i < 100; i++) {
            new Thread(runnable1).start();
        }
    }
}

五.线程池

线程池维护着一个队列,队列中保存着处于等待(空闲)状态的线程。不用每次都创建新的线程。
和线程池相关的接口和类存在java.util.concurrent并发包中。
1 Executor:线程池的核心接口,负责线程的创建使用和调度的根接口
2 ExecutorService: Executor的子接口,线程池的主要接口, 提供基本功能。
3 ScheduledExecutorService: ExecutorService的子接口,负责线程调度的子接口。
1 ThreadPoolExecutor:ExecutorService的实现类,负责线程池的创建使用。
2 ScheduledThreadPoolExecutor:继承 ThreadPoolExecutor,并实现 ScheduledExecutorService接口,既有线程池的功能,又具有线程调度功能。
3 Executors:线程池的工具类,负责线程池的创建。
​	newFixedThreadPool();创建固定大小的线程池。
​	newCachedThreadPool();创建缓存线程池,线程池大小没有限制。根据需求自动调整线程数量。
​	newSingleThreadExecutor();创建单个线程的线程池,只有一个线程。
​	newScheduledThreadPool();创建固定大小的线程池,可以延迟或定时执行任务。
public class Demo2 {
    public static void main(String[] args) {
        //创建票
        Ticket ticket=new Ticket();
        //创建线程池(4个线程)
        ExecutorService executorService= Executors.newFixedThreadPool(4);
        //分配任务
        for (int i = 0; i < 4; i++) {
            executorService.submit(ticket);
        }
        //关闭
        executorService.shutdown();
    }
}
 * 计算1-100的和,使用线程池
public class Demo3 {
    public static void main(String[] args) throws Exception{
        ExecutorService executorService= Executors.newCachedThreadPool();
        List<Future<Integer>> list=new ArrayList<>();
        //给线程池分配任务
        for (int i = 0; i < 10; i++) {
            Future<Integer> future=executorService.submit(new Callable<Integer>(){
                @Override
                public Integer call() throws Exception {
                    int sum=0;
                    for (int i1 = 0; i1 < 100; i1++) {
                        sum=sum+i1;
                    }
                    System.out.println(Thread.currentThread().getName());
                    return sum;
                }
            });
            list.add(future);
        }
        //获取结果
        for (Future<Integer> future : list) {
            System.out.println(future.get());
        }
        executorService.shutdown();
    }
}
 * 线程池的使用
public class Demo4 {
    public static void main(String[] args) {
        //创建线程池
        ScheduledExecutorService scheduledExecutorService= Executors.newScheduledThreadPool(5);
        //分配任务
        for (int i = 0; i < 5; i++) {
            int temp = i;
           /* scheduledExecutorService.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"==="+temp);
                }
            });*/
           /*scheduledExecutorService.schedule(new Runnable(){
               @Override
               public void run() {
                   System.out.println(Thread.currentThread().getName()+"==="+temp);
               }
           }, 5000, TimeUnit.SECONDS);
        }*/
            //周期执行
            /*scheduledExecutorService.scheduleAtFixedRate(new Runnable(){//固定的
                @Override
                public void run() {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"==="+temp);
                }
            }, 5, 1, TimeUnit.SECONDS);*///period(间隔时间):运行时间+每次运行间隔时间(运行<间隔),若运行时间>间隔时间;就是运行时间,间隔时间就没用了

            scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "===" + temp);
                }
            }, 5, 1, TimeUnit.SECONDS);//delay(间隔时间):无论运行时间是多久,间隔时间就=每次运行间隔时间,与运行时间无关
            //关闭
            //scheduledExecutorService.shutdown();
        }
    }
}

六.定时器Timer

public class Demo6 {
    public static void main(String[] args) {
        //创建Timer对象,调度用这个比较多
        Timer timer=new Timer();
        //创建人物
        TimerTask timerTask=new TimerTask() {
            int index=0;
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getName()+"=="+i);
                }
                index++;
                if (index==10){
                    timer.cancel();
                }
                //取消
                //timer.cancel();
            }
        };
        //分配任务
        /*timer.schedule(timerTask,3000);
        //执行完可以取消
        //timer.cancel();//不能在这取消,这样可能没执行就取消了*/
        //delay---运行前间隔;period---运行中间隔
        timer.schedule(timerTask, 1000,2000);//无论在哪都不能取消,除非创建计数变量
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值