学习Java的第十天(2024.7.25)

1.线程中的函数式接口

   函数式接口将Runnable接口中的run()方法重写为method()方法,下列代码测试两个线程分别对同一个集合中10个元素,最后获取集合的长度,由于线程不安全,最后集合长度的输出结果会小于20。

    static List list = new ArrayList<>();
    public static void method(){
        for (int i=0;i<10;i++){
            list.add("A"+Thread.currentThread().getName());
        }
    }
    public static void main(String[] args) {
        Runnable run = EasyThreadA::method;//函数式接口
        //使用EasyThreadA中的method替换Runnable中的run()方法
        Thread a = new Thread(run);
        Thread b = new Thread(run);
        a.start();
        b.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(list.size());
    }

2.两个线程分别存入十个数据到数组中:

先创建一个继承Thread类的子类,在这个类中重写run()方法,使list中存入十个字符"a"。

class ThreadA extends Thread{
    public List list = new ArrayList();
    @Override
    public void run() {
        for (int i=0;i<10;i++){
            list.add("a");
        }
    }
}

主函数中定义两个线程,使他们分别执行类ThreadA中的run()方法,他们会在两个数组中分别存入十个字符"a"。

    public static void main(String[] args) {
        ThreadA a=new ThreadA();
        ThreadA b=new ThreadA();
        a.start();
        b.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(a.list);
        System.out.println(a.list);
    }

 

3.对两次线程对同一对象进行操作:

先创建一个类RunA,这个类实现了Runnable接口,重写run()方法

class RunA implements Runnable{
    public List list = new ArrayList();
    @Override
    public void run() {
        for (int i=0;i<10;i++){
            list.add("a");
        }
    }
}

在主函数中创建一个对象 run,创建两个线程,两个线程对象都指向run对象,会对run执行两次

    public static void main(String[] args) {
        RunA run = new RunA();
        Thread a = new Thread(run);
        Thread b = new Thread(run);
        a.start();
        b.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(run.list);
        System.out.println(run.list.size());
    }

 结果如下:

4.锁对象

 锁对象 Lock

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

可以在ReentrantLock()的括号中传入参数,传入参数true这个锁对象就是公平锁,不传入参数或传入false,这个锁就是非公平锁

    Lock lock1 = new ReentrantLock(true);//创建公平锁
    Lock lock2 = new ReentrantLock(false);//创建非公平锁(默认状态下是非公平锁)

有两个方法:

lock()             加锁

lock.trylock()  尝试加锁,是布尔类型,加锁成功返回true,失败返回false

    public void method(){
        if (lock.tryLock()) {
            System.out.println(Thread.currentThread().getName() + "进入方法");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName() + "结束方法");
            lock.unlock();
        }else {
            System.out.println("加锁未成功----去执行别的代码");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            method();
        }
    }
    public static void main(String[] args) {
        Runnable run = new EasyThreadB()::method;
        Thread a = new Thread(run);
        Thread b = new Thread(run);
        a.start();
        b.start();
    }

5.ReentrantLock中的两把锁

读锁和写锁是在锁盒子(ReentranReadWriteLock)中的两把锁

    public static ReentrantReadWriteLock rrwl = new ReentrantReadWriteLock();
    public static ReentrantLock rl = new ReentrantLock();

读锁:

ReentrantLock对象的readLock()方法

    public static void main(String[] args) {
        Runnable run = EasyThreadC::method;
        Runnable runWrite = EasyThreadC::methodWrite;
        Thread a = new Thread(run);
        a.start();
        Thread b = new Thread(run);
        b.start();
        Thread c = new Thread(run);
        c.start();
        Thread d = new Thread(run);
        d.start();
        Thread e = new Thread(run);
        e.start();
    }
    public static void method(){
        System.out.println(Thread.currentThread().getName()+"进入方法");
        Lock lock = rrwl.readLock();
        lock.lock();
        System.out.println(Thread.currentThread().getName()+"加锁成功----读锁");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        lock.unlock();
        System.out.println(Thread.currentThread().getName()+"方法结束");
    }

写锁:

ReentrantLock对象的writeLock()方法

    public static void methodWrite(){
        System.out.println(Thread.currentThread().getName()+"进入方法");
        Lock lock = rrwl.writeLock();
        lock.lock();
        System.out.println(Thread.currentThread().getName()+"加锁成功----写锁");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName()+"方法结束");
        lock.unlock();
    }
     public static void main(String[] args) {
        Thread f = new Thread(runWrite);
        f.start();
        Thread g = new Thread(runWrite);
        g.start();
        Thread h = new Thread(runWrite);
        h.start();
        Thread i = new Thread(runWrite);
        i.start();
        System.out.println("main线程结束--------");
    }

6.锁对象的方法

先创建一个锁对象:

    public static final Object OBJ=new Object();//作为锁对象

OBJ.wait();          让执行到该行代码的线程进入等待状态(等待池中等待)

OBJ.notify();        唤醒一条被该锁对象wait的线程

OBJ.notifyAll();    唤醒全部被该锁对象wait的线程

注意:只有锁对象才能调用这三种方法

public static void method(){
        System.out.println(Thread.currentThread().getName()+"进入方法");
        synchronized (OBJ){
            OBJ.notify();//唤醒一条被该锁对象wait的线程
            //OBJ.notifyAll();//唤醒全部被该锁对象wait的线程
            System.out.println(Thread.currentThread().getName()+"进入同步代码块");
            try {
                try {
                    System.out.println(Thread.currentThread().getName()+"进入等待状态");
                    OBJ.wait();//只有锁对象才能调用wait方法
                    //让执行到该行代码的线程进入等待状态(等待池中等待)
                    System.out.println(Thread.currentThread().getName()+"重新运行");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName()+"结束同步代码块");
            OBJ.notify();
        }
    }

    public static void main(String[] args) {
        Runnable run = EasyThreadD::method;
        Thread a = new Thread(run);
        a.start();
        Thread b = new Thread(run);
        b.start();
        Thread c = new Thread(run);
        c.start();
        Thread d = new Thread(run);
        d.start();
    }

wait方法和sleep方法的区别:

wait是Object中定义的方法,可以用锁对象调用,让执行到该代码的线程进入到等待状态

sleep是Thread类中定义的静态方法,也可以让执行到该行的线程进入等待状态

区别:

1.sleep需要传入一个时间(毫秒数),到达时间后会自动唤醒

wait不能自动唤醒,必须调用notify方法或notifyAll方法唤醒

2.sleep方法保持锁状态进入等待状态,其他线程不可运行

wait方法被锁对象解除锁状态,其他线程可以介入运行

7.线程池

池的作用:重用

线程池的作用:对线程进行创建和管理,以及销毁线程

创建一个线程池对象:

ThreadPoolExecutor tpe = new ThreadPoolExecutor(,,,,,,);
//括号中要传入7个参数

要传入的参数:

 这七个参数的含义分别为:

 开启线程池:

tpe.execute(run);

关闭线程池:

注意:线程池像输入输出流一样,开启后需要关闭!!!

tpe.shutdown();//结束线程池

8.线程任务:

Runnable和Callable

public class EasyExecuters {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ArrayBlockingQueue qu = new ArrayBlockingQueue(12);//队列里只能放12个任务
        ThreadPoolExecutor tpe = new ThreadPoolExecutor(5,//核心线程数
                10,//最大线程数
                10,//存活时间
                TimeUnit.SECONDS,//时间类型
                qu,//工作队列
                Executors.defaultThreadFactory(),//工厂
                new ThreadPoolExecutor.AbortPolicy());//回绝策略
        Runnable run = EasyExecuters::method;
        tpe.execute(run);

        Callable<String> call = EasyExecuters::methodCall;
        Future<String> f = tpe.submit(call);
        //tpe.submit(run);
        System.out.println(f.get());//会等待线程执行完毕
        tpe.shutdown();//结束线程池
    }
    public static void method(){
        System.out.println(Thread.currentThread().getName()+"执行代码");
    }
    public static String methodCall() throws InterruptedException {
        System.out.println(Thread.currentThread().getName()+"执行代码");
        Thread.sleep(2000);
        return "callResult";
    }
}

9.回绝策略:

回绝策略分为四个 :

1.AbortPolicy(默认的回绝策略)放弃该任务并且会抛出一个异常RejectdExecutionException,只有AbortPolicy回绝策略会抛出异常

2.CallerRunsPolicy 调用者执行,让传递任务的线程执行此任务,不抛出异常

3.DiscardOldestPolicy 放弃队列中时间最长的任务,将新任务加到队列中,不会抛出异常

4.DiscardPolicy 直接放弃新任务,不会抛出异常

10.线程池的工作原理

<1>池中是否有空闲的线程,如果有就让线程执行任务

<2>当工作队列填满后,如果没有空闲的线程,判断池中的线程数量有没有达到核心线程数

<3>如果没有达到核心线程数,会创建新的线程执行新的任务,直到填满核心线程数。如果已经达到,优先在队列中存储,直到队列填满

<4>工作队列填满之后再添加新的任务,判断是否达到最大线程数,如果没有,创建新的线程执行任务直到填满最大的线程数

<5>已经填满最大线程数,队列也已经填满,没有空闲的线程,就执行回绝策略

11.核心线程数注意点:

1.线程池中的线程达到/超过核心线程数,超出的数量会根据存活时间,进行销毁,直到数量达到核心线程数

2.如果线程的数量少于核心线程数,不会消亡

12.Java中内置的线程池对象:

可以根据工作任务来创建线程,如果没有空闲的线程,就创建新的线程

注意:线程存活时间:60s

Executors.newCachedThreadPool();

Executors.newFixedThreadPool(10);设定最大线程数量的线程池

Executors.newScheduledThreadPool(10);提供定时运行的处理方案(可以延期运行)

Executors.newSingleThreadExecutor();创建一个单个线程的线程池 保障任务队列完全按照顺序执行

public class EasyExecutersA {
    public static void main(String[] args) {
        BlockingQueue queue = new ArrayBlockingQueue(12);
        ThreadPoolExecutor tpe = new ThreadPoolExecutor(
                5,8,10, TimeUnit.SECONDS,queue,
                Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy()
        );
        Runnable run = ()->{
            System.out.println(Thread.currentThread().getName()+"执行代码run");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName()+"代码执行完毕");
        };
        for (int i=0;i<12;i++){
            tpe.execute(run);
        }
        tpe.shutdown();
    }
}

13.synchronized和Lock 的实例

Lock:

public class EasyThreadE {
    public static void main(String[] args) {
        EasyList list=new EasyList();
        Runnable runSize=()->{list.size();};
        Runnable runGet=()->{list.get(0);};
        Runnable runAdd=()->{list.add(12);};
        list.add(12);
        Thread a=new Thread(runSize);
        Thread b=new Thread(runGet);
        Thread c=new Thread(runAdd);
        a.start();b.start();c.start();
    }
}
class EasyList{
    private int[] values=new int[20];
    private int size=0;

    ReentrantReadWriteLock rwLock=new ReentrantReadWriteLock();

    public int size(){
        Lock readLock=rwLock.readLock();
        readLock.lock();
        System.out.println(Thread.currentThread().getName()+"Size开始");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName()+"Size结束");
        readLock.unlock();
        return size;
    }

    public int get(int index){
        Lock readLock=rwLock.readLock();
        readLock.lock();
        System.out.println(Thread.currentThread().getName()+"Get开始");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        if(index>=size){
            throw new IndexOutOfBoundsException("index is "+index);
        }
        System.out.println(Thread.currentThread().getName()+"Get结束");
        readLock.unlock();
        return values[index];
    }

    public  boolean add(int item){
        Lock writeLock=rwLock.writeLock();
        writeLock.lock();
        System.out.println(Thread.currentThread().getName()+"Add开始");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        if(size>=values.length){
            return false;
        }
        values[size++]=item;
        System.out.println(Thread.currentThread().getName()+"Add结束");
        writeLock.unlock();
        return true;
    }
}

synchronized锁:

public class EasyThreadE {

    public static void main(String[] args) {
        EasyList list=new EasyList();
        Runnable runSize=()->{list.size();};
        Runnable runGet=()->{list.get(0);};
        Runnable runAdd=()->{list.add(12);};
        list.add(12);
        Thread a=new Thread(runSize);
        Thread b=new Thread(runGet);
        Thread c=new Thread(runAdd);
        a.start();b.start();c.start();
    }
}
class EasyList{
    private int[] values=new int[20];
    private int size=0;

    ReentrantReadWriteLock rwLock=new ReentrantReadWriteLock();

    public synchronized int size(){
        System.out.println(Thread.currentThread().getName()+"Size开始");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName()+"Size结束");
        return size;
    }

    public synchronized int get(int index){
        System.out.println(Thread.currentThread().getName()+"Get开始");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        if(index>=size){
            throw new IndexOutOfBoundsException("index is "+index);
        }
        System.out.println(Thread.currentThread().getName()+"Get结束");
        return values[index];
    }

    public synchronized boolean add(int item){
        System.out.println(Thread.currentThread().getName()+"Add开始");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        if(size>=values.length){
            return false;
        }
        values[size++]=item;
        System.out.println(Thread.currentThread().getName()+"Add结束");
        return true;
    }
}

14.死锁

死锁:是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去
(1) 因为系统资源不足。
(2) 进程运行推进顺序不合适。
(3) 资源分配不当等。
  • 35
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值