线程同步(线程安全问题)、等待和唤醒、线程池、Lambda、

目录

1、线程同步(线程安全问题)

(1)解决线程安全问题的第一种方案:使用同步代码块

a)定义一个Runnable接口的实现类,用于创建Runnable接口实现类对象

b)创建3个线程,同时开启,对共享的票进行出售

(2)解决线程安全问题的第二种方案:使用同步方法(使用synchronized修饰的方法)

a)定义一个Runnable接口的实现类,用于创建Runnable接口实现类对象

b)创建3个线程,同时开启,对共享的票进行出售

 (3)解决线程安全问题的第三种方案:使用静态同步方法(了解)

a)定义一个Runnable接口的实现类,用于创建Runnable接口实现类对象

b)创建3个线程,同时开启,对共享的票进行出售

(4)解决线程安全问题的第四种方案:使用Lock锁(推荐使用)

a)定义一个Runnable接口的实现类,用于创建Runnable接口实现类对象

b)创建3个线程,同时开启,对共享的票进行出售

2、等待和唤醒

(1)使用wait()方法使线程进入等待状态,使用notify()方法唤醒正在等待的线程

(2)使用带参数的wait(long m)方法使线程进入等待状态,使用notifyAll()方法唤醒所有正在等待的线程

3、线程池

a)先创建一个Runnable接口的实现类

b)实现线程池

4、Lambda

(1)使用Lambda表达式存储自定义类型的数据

a)定义一个Person类,用于创建Person类型的数据

b)创建数组存储Person类型的对象

(2)使用Lambda表达式调用接口中的方法实现指定的功能

a)首先定义一个接口,在接口中定义一个方法,用于实现一定的功能

b)使用Lambda表达式调用接口中的方法实现指定的功能


1、线程同步(线程安全问题)

(1)解决线程安全问题的第一种方案:使用同步代码块

好处:解决了线程安全问题

缺点:程序频繁的判断锁,获取锁,释放锁,程序的效率会降低

a)定义一个Runnable接口的实现类,用于创建Runnable接口实现类对象

package com.itheima.demo05Synchronized;
//解决线程安全问题的一种方案:使用同步代码块,把同步代码块锁住,只让一个线程在同步代码块中执行
public class RunnableImpl implements Runnable {
    //定义一个多个线程共享的票源
    private int ticket = 100;
    //创建一个锁对象
    Object obj = new Object();

    @Override
    public void run() {
        while (true) {
            synchronized (obj) {
                //先判断票是否存在
                if (ticket > 0) {
                    //提高安全问题出现的概率,让程序睡眠
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //票存在,卖票
                    System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票");
                    ticket--;
                }
            }
        }
    }
}

b)创建3个线程,同时开启,对共享的票进行出售

package com.itheima.demo05Synchronized;
//模拟卖票案例:创建3个线程,同时开启,对共享的票进行出售
public class Demo01Ticket {
    public static void main(String[] args) {
        //创建Runnable接口的实现类对象
        RunnableImpl run = new RunnableImpl();
        //创建Thread类对象,构造方法中传递Runnable接口的实现类对象
        Thread t0 = new Thread(run);
        Thread t1 = new Thread(run);
        Thread t2 = new Thread(run);
        //调用start方法开启多线程
        t0.start();
        t1.start();
        t2.start();
    }
}

(2)解决线程安全问题的第二种方案:使用同步方法(使用synchronized修饰的方法)

a)定义一个Runnable接口的实现类,用于创建Runnable接口实现类对象

package com.itheima.demo06SynchronizedMethod;
//解决线程安全问题的第二种方案:使用同步方法
public class RunnableImpl implements Runnable {
    //定义一个多个线程共享的票源
    private int ticket = 100;

    @Override
    public void run() {
        while (true) {
            payTicket();
        }
    }
    //定义一个同步方法
    //第一种定义方法的方式
    public synchronized void payTicket(){
        //先判断票是否存在
        if (ticket > 0) {
            //提高安全问题出现的概率,让程序睡眠
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //票存在,卖票
            System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票");
            ticket--;
        }
    }
   //第二种定义方法的方式
    /*public void payTicket(){
        synchronized (this){
            //先判断票是否存在
            if (ticket > 0) {
                //提高安全问题出现的概率,让程序睡眠
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //票存在,卖票
                System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票");
                ticket--;
            }
        }
    }*/
}

b)创建3个线程,同时开启,对共享的票进行出售

package com.itheima.demo05Synchronized;
//模拟卖票案例:创建3个线程,同时开启,对共享的票进行出售
public class Demo01Ticket {
    public static void main(String[] args) {
        //创建Runnable接口的实现类对象
        RunnableImpl run = new RunnableImpl();
        //创建Thread类对象,构造方法中传递Runnable接口的实现类对象
        Thread t0 = new Thread(run);
        Thread t1 = new Thread(run);
        Thread t2 = new Thread(run);
        //调用start方法开启多线程
        t0.start();
        t1.start();
        t2.start();
    }
}

 (3)解决线程安全问题的第三种方案:使用静态同步方法(了解)

a)定义一个Runnable接口的实现类,用于创建Runnable接口实现类对象

package com.itheima.demo07SynchronizedStaticMethod;

/*
问题:卖票案例出现了线程安全问题,卖出了不存在的票和重复的票

解决线程安全问题的第三种方案:使用静态同步方法
* */
public class RunnableImpl implements Runnable {
    //定义一个多个线程共享的票源
    private static int ticket = 100;

    @Override
    public void run() {
        while (true) {
            payTicketStatic();
        }
    }
    //定义一个静态同步方法
    //第一种定义静态同步方法
    public static synchronized void payTicketStatic(){
        //先判断票是否存在
        if (ticket > 0) {
            //提高安全问题出现的概率,让程序睡眠
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //票存在,卖票
            System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票");
            ticket--;
        }
    }
    //第二种定义静态同步方法
    /*public static void payTicketStatic(){
        synchronized (RunnableImpl.class){
            //先判断票是否存在
            if (ticket > 0) {
                //提高安全问题出现的概率,让程序睡眠
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //票存在,卖票
                System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票");
                ticket--;
            }
        }
    }*/
}

b)创建3个线程,同时开启,对共享的票进行出售

package com.itheima.demo05Synchronized;
//模拟卖票案例:创建3个线程,同时开启,对共享的票进行出售
public class Demo01Ticket {
    public static void main(String[] args) {
        //创建Runnable接口的实现类对象
        RunnableImpl run = new RunnableImpl();
        //创建Thread类对象,构造方法中传递Runnable接口的实现类对象
        Thread t0 = new Thread(run);
        Thread t1 = new Thread(run);
        Thread t2 = new Thread(run);
        //调用start方法开启多线程
        t0.start();
        t1.start();
        t2.start();
    }
}

(4)解决线程安全问题的第四种方案:使用Lock锁(推荐使用)

a)定义一个Runnable接口的实现类,用于创建Runnable接口实现类对象

package com.itheima.demo08Lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//解决线程安全问题的第四种方案:使用Lock锁
public class RunnableImpl implements Runnable {
    //定义一个多个线程共享的票源
    private int ticket = 100;

    //1.在成员位置创建一个ReentrantLock对象
    Lock lock = new ReentrantLock();

    //更好的写法
    @Override
    public void run() {
        while (true) {
            //2.在可能出现安全问题的代码前调用Lock接口中的方法Lock获取锁
            lock.lock();
            //先判断票是否存在
            if (ticket > 0) {
                //提高安全问题出现的概率,让程序睡眠
                try {
                    Thread.sleep(10);
                    //票存在,卖票
                    System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票");
                    ticket--;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //3.在可能出现安全问题的代码后调用Lock接口中的方法unLock释放锁
                    lock.unlock();//无论程序是否异常,都会把锁释放
                }
            }
        }
    }
    /*@Override
    public void run() {
        while (true){
            //2.在可能出现安全问题的代码前调用Lock接口中的方法Lock获取锁
            lock.lock();
            //先判断票是否存在
            if (ticket>0){
                //提高安全问题出现的概率,让程序睡眠
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //票存在,卖票
                System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票");
                ticket--;
            }
            //3.在可能出现安全问题的代码后调用Lock接口中的方法unLock释放锁
            lock.unlock();
        }
    }*/
}

b)创建3个线程,同时开启,对共享的票进行出售

package com.itheima.demo05Synchronized;
//模拟卖票案例:创建3个线程,同时开启,对共享的票进行出售
public class Demo01Ticket {
    public static void main(String[] args) {
        //创建Runnable接口的实现类对象
        RunnableImpl run = new RunnableImpl();
        //创建Thread类对象,构造方法中传递Runnable接口的实现类对象
        Thread t0 = new Thread(run);
        Thread t1 = new Thread(run);
        Thread t2 = new Thread(run);
        //调用start方法开启多线程
        t0.start();
        t1.start();
        t2.start();
    }
}

2、等待和唤醒

(1)使用wait()方法使线程进入等待状态,使用notify()方法唤醒正在等待的线程

package com.itheima.demo09WaitAndNotify;

public class Demo01WaitAndNotify {
    public static void main(String[] args) {
        //创建锁对象,保证唯一
        Object obj = new Object();
        //创建一个顾客线程
        new Thread(){
            @Override
            public void run() {
                //保证等待和唤醒的线程只能有一个执行,需要使用同步技术
                synchronized (obj){
                    System.out.println("告知老板要的包子的种类和数量");
                    //调用wait方法,放弃CPU的执行,进入到WAITING状态(无限等待)
                    try {
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //唤醒之后执行的代码
                    System.out.println("包子已经做好了,开吃!");
                }
            }
        }.start();
        //创建一个老板线程
        new Thread(){
            @Override
            public void run() {
                //花5秒钟做包子
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //保证等待和唤醒的线程只能有一个执行,需要使用同步技术
                synchronized (obj){
                    System.out.println("老板5秒钟之后做好包子,告知顾客,可以吃包子了");
                    obj.notify();
                }
            }
        }.start();
    }
}

(2)使用带参数的wait(long m)方法使线程进入等待状态,使用notifyAll()方法唤醒所有正在等待的线程

①使用sleep(long m)方法,在毫秒值结束之后,线程自动睡醒进入运行状态或者阻塞状态(同wait(long m)方法效果相同)

②使用wait(long m)方法,如果在毫秒值结束之后,还没有被notify唤醒,就会自动醒来,进入运行状态或者阻塞状态(同sleep(long m)方法效果相同)

③使用notifyAll()方法唤醒监视器上等待的所有线程

3、线程池

a)先创建一个Runnable接口的实现类

package com.itheima.demo02ThreadPool;
//2.创建一个类,实现Runnable接口,重写run方法,设置线程任务
public class RunnableImpl implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"创建了一个新的线程执行");
    }
}

b)实现线程池

package com.itheima.demo02ThreadPool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/*
    线程池:JDK1.5之后提供的
    java.util.concurrent.Executors:线程池的工厂类,用来生成线程池
    Executors类中的静态方法:
        static ExecutorService newFixedThreadPool(int nThreads) 创建一个可重用固定线程数的线程池
        参数:
            int nThreads:创建线程池中包含的线程数量
        返回值:
            ExecutorService接口,返回的是ExecutorService接口的实现类对象,我们可以使用ExecutorService接口接收(面向接口编程)
    java.util.concurrent.ExecutorService:线程池接口
        用来从线程池中获取线程,调用start方法,执行线程任务
            submit(Runnable task) 提交一个 Runnable 任务用于执行
        关闭/销毁线程池的方法
            void shutdown()
    线程池的使用步骤:
        1.使用线程池的工厂类Executors里边提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
        2.创建一个类,实现Runnable接口,重写run方法,设置线程任务
        3.调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法
        4.调用ExecutorService中的方法shutdown销毁线程池(不建议执行)
 */
public class Demo01ThreadPool {
    public static void main(String[] args) {
        //1.使用线程池的工厂类Executors里边提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
        ExecutorService es = Executors.newFixedThreadPool(2);
        //3.调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法
        es.submit(new RunnableImpl());//pool-1-thread-1创建了一个新的线程执行
        //线程池会一直开启,使用完了线程,会自动把线程归还给线程池,线程可以继续使用
        es.submit(new RunnableImpl());//pool-1-thread-1创建了一个新的线程执行
        es.submit(new RunnableImpl());//pool-1-thread-2创建了一个新的线程执行

        //4.调用ExecutorService中的方法shutdown销毁线程池(不建议执行)
        es.shutdown();

        es.submit(new RunnableImpl());//抛异常,线程池都没有了,就不能获取线程了
    }
}

4、Lambda

Lambda的使用前提:

①使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。

无论是JDK内置的Runnable、Comparator接口还是自定义的接口,只有当接口中的方法存在且唯一时,才可以使用Lambda

②使用Lambda必须具有上下文推断。

也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例

备注:有且仅有一个抽象方法的接口,称为“函数式接口”。

(1)使用Lambda表达式存储自定义类型的数据

a)定义一个Person类,用于创建Person类型的数据

package com.itheima.demo04Lambda;

public class Person {
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

b)创建数组存储Person类型的对象

package com.itheima.demo04Lambda;

import java.util.Arrays;
/*
    Lambda表达式有参数有返回值的练习
    需求:
        使用数组存储多个Person对象
        对数组中的Person对象使用Arrays的sort方法通过年龄进行升序排序
 */
public class Demo01Arrays {
    public static void main(String[] args) {
        //使用数组存储多个Person对象
        Person[] arr = {
                new Person("迪丽热巴",38),
                new Person("古力娜扎",18),
                new Person("马尔扎哈",19)
        };

        //对数组中的Person对象使用Arrays的sort方法通过年龄进行升序(前边-后边)排序
        /*Arrays.sort(arr, new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.getAge()-o2.getAge();
            }
        });*/

        //使用Lambda表达式,简化匿名内部类
        Arrays.sort(arr,(Person o1, Person o2)->{
            return o1.getAge()-o2.getAge();
        });

        //优化省略Lambda
        Arrays.sort(arr,(o1, o2)->o1.getAge()-o2.getAge());

        //遍历数组
        for (Person p : arr) {
            System.out.println(p);
        }
    }
}

(2)使用Lambda表达式调用接口中的方法实现指定的功能

a)首先定义一个接口,在接口中定义一个方法,用于实现一定的功能

package com.itheima.demo05Lamdba;
//给定一个计算器Calculator接口,内含抽象方法calc可以将两个int数字相加得到和值
public interface Calculator {
    //定义一个计算两个int整数和的方法并返回结果
    public abstract int calc(int a, int b);
}

b)使用Lambda表达式调用接口中的方法实现指定的功能

package com.itheima.demo05Lamdba;
/*
    Lambda表达式有参数有返回值的练习
    需求:
        给定一个计算器Calculator接口,内含抽象方法calc可以将两个int数字相加得到和值
        使用Lambda的标准格式调用invokeCalc方法,完成120和130的相加计算
 */
public class Demo01Calculator {
    public static void main(String[] args) {
        //调用invokeCalc方法,方法的参数是一个接口,可以使用匿名内部类
        invokeCalc(10, 20, new Calculator() {
            @Override
            public int calc(int a, int b) {
                return a + b;
            }
        });

        //使用Lambda表达式简化匿名内部类的书写
        invokeCalc(120, 130, (int a, int b) -> {
            return a + b;
        });

        //优化省略Lambda
        invokeCalc(120, 130, (a, b) -> a + b);
    }

    /*
        定义一个方法
        参数传递两个int类型的整数
        参数传递Calculator接口
        方法内部调用Calculator中的方法calc计算两个整数的和
     */
    public static void invokeCalc(int a, int b, Calculator c) {
        int sum = c.calc(a, b);
        System.out.println(sum);
    }
}

打印结果:
30
250
250

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值