Java学习总结:2020-9-8 多线程回顾

概述

进程

​ 在计算机中,把一个任务称为一个进程

线程

​ 在计算机中,把进程内部进行的子任务称为线程(线程就是独立的执行路径)

进程和线程的紧密联系

​ 一个进程可以包含多个线程

备注

一个Java程序实际上是一个JVM进程,JVM进程通过主线程(main())执行,在main内部,可以用来启动多个线程


线程创建的三种方式

1.1创建Thread的方法,继承Thread类

public class Demo01 extends Thread{

    @Override
    public void run() {
        for (int i  = 0; i < 20; i++){
            System.out.println("我在看代码");
        }
    }

    public static void main(String[] args) {
        //主线程(main)
        for (int i = 0; i < 20;i++){
            System.out.println("我在看线程");
        }
        //线程对象的创建
        Demo01 demo01 = new Demo01();
        //调用start()方法开启线程
        demo01.start();

    }
}

1.2实现Runnable接口(推荐)

public class Demo02 implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 20; i++ ){
            System.out.println(Demo01.currentThread().getName()+"实现Runnable接口");
        }
    }

    public static void main(String[] args) {
        //创建线程
        Demo02 demo02 = new Demo02();
        Thread thread = new Thread(demo02,"小明");
        thread.start();
        new Thread(demo02,"小黑").start();
    }
}

1.3实现Callable接口

public class Demo03 implements Callable<Boolean> {
    private String url;
    private String name;

    /**创建线程时,需要传入对象
     *
     * @param url
     * @param name
     */
    public Demo03(String url, String name) {
        this.url = url;
        this.name = name;
    }

    @Override
    public Boolean call() throws Exception {
       WebDownloader webDownloader = new WebDownloader();
       //调用方法,传入下载的参数
        webDownloader.Downloader(url,name);
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Demo03 demo03 = new Demo03("https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2062307482,1942308797&fm=26&gp=0.jpg","图片1.jpg");
       //创建执行服务
        ExecutorService ser = Executors.newFixedThreadPool(1);
        //提交执行服务,并发包
        Future<Boolean> r1 = ser.submit(demo03);
        //获取结果
        boolean rs1 = r1.get();
        System.out.println(rs1);
        //关闭服务
        ser.shutdown();
    }
}
/**下载其功能
 **/
class WebDownloader{
    /**    需要传入url和name
     *
     * @param url
     * @param name
     */
    public void Downloader(String url, String name){
        try {
            FileUtils.copyURLToFile( new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

静态代理

代理的概念

为某个对象提供一个代理,以控制对这个对象的访问。

静态代理的概念

由程序员创建或工具生成代理的源码,再编译代理类,所谓静态也就是子啊程序运行前就已经存在代理类的字节码文件。

注意点

​ 真实对象和代理对象都要实现同一个接口

​ 代理对象要代理真实角色

好处

​ 代理对象可以做真实对象做不了的事情

​ 真实对象可以专注做自己的事情

/**
静态代理例子:结婚 ,接口:结婚 实体;你、婚庆公司
 */
public class Demo {
    public static void main(String[] args) {
        
        /**       
        WeddingCompany weddingCompany = new WeddingCompany(new you());
         //调用方法
         weddingCompany.happyMarry();*/
        
        //简化操作
        new WeddingCompanys(new You()).happyMarry();
        
        /**线程操作(跟本例子没有关系)
         * new Thread(new Runnable() {
        @Override
        public void run() {
        System.out.println("我爱你");
        }
        }).start();*/
        //简化线程操作,Thread代理Runnable接口
        new Thread(()->{System.out.println("我爱你");}).start();
    }
}

/**结婚
 *
 */
interface Marrys{
    void happyend();
}

/** 实体:你 作用:结婚
 *
 */
class You implements Marry{

    @Override
    public void happyMarry() {
        System.out.println("结婚快乐");
    }
}

/** 实体:婚庆公司 作用:代理你(结婚前—>布置会场 结婚后—>收尾款 结婚时—>主持婚礼)
 *
 */
class WeddingCompanys implements Marry{
    //代理角色的名字
    private Marry targets;

    //传入参数
    public WeddingCompanys(Marry targets){
        this.targets = targets;
    }

    @Override
    public void happyMarry() {
        //结婚前
        before();
        //结婚时,公司代理的角色结婚
        this.targets.happyMarry();
        //结婚后
        after();

    }
    //结婚前
    private void before(){
        System.out.println("布置会场");
    }
    //结婚后
    private void after(){
        System.out.println("收取尾款");
    }
}
}

细节:lambda表达式

推导过程:lambda的出现进程历史(根据一步步的简化)

public class TestLambda1 {

    /**3、优化,静态内部类
     *
     */
    static class Like2 implements ILike{

        @Override
        public void lambda(int a) {
            System.out.println("i like lambda1");
        }
    }

    public static void main(String[] args) {
        ILike iLike = new Like();

        ILike iLike1 = new Like2();

/**      4、局部内部类
 *
 */
        class Like3 implements ILike{

            @Override
            public void lambda(int a) {
                System.out.println("i like lambda2");
            }
        }

        ILike iLike2 = new Like3();


        //5、匿名内部类
        ILike iLike3 = new ILike() {
            @Override
            public void lambda(int a) {
                System.out.println("i like lambda3");
            }
        };

        //6、用lambda表达式
        ILike iLike4 = (int a)->{System.out.println("i like lambda4");};
        //1、简化(省略类型),注意:要不全省,要不不省
        iLike4 = ( a)->{System.out.println("i like lambda4");};
        //2、简化括号
        iLike4 = a->{System.out.println("i like lambda4");};
        //3、简化中括号,注意:只能由一条语句存在时
        iLike4 = a-> System.out.println("i like lambda4");
        
    }
}

/**1、定义函数(只有一个方法)式接口(lambda必须要函数式)
 *
 */
interface ILike{
    void lambda(int a);
    }

/**   2、实现类
 *
 */
class Like implements ILike{

        @Override
        public void lambda(int a) {
            System.out.println("i like lambda");
        }
    }

线程状态

1.1线程状态五大状态

在这里插入图片描述
在这里插入图片描述

1.2线程方法

线程的停止(推荐标志位)

停止线程destroy()或是stop() [被JDK废弃了](不推荐)

===>推荐线程自己停止下来(使用标志位):

/**测试stop
 * 1、建议线程正常停止—>利用次数,不建议死循环
 * 2、建议使用标志位—>设置一个标志位
 * 3、不要使用stop或者destroy等过时或者jdk不建议的方法
 */
public class TestStop implements Runnable{

    /** 1、  设置标志位
     * 、
     */
    private boolean flag = true;

    @Override
    public void run() {
        int i = 0;
        while (flag){
            System.out.println("run....Thread"+ i++);
        }
    }

    /**2、设置一个公开的方法停止线程,转换标志位
     *
     */
    public void stop(){
        this.flag = false;
    }

    public static void main(String[] args) {
        TestStop testStop = new TestStop();
        new Thread(testStop).start();

        for (int i = 0; i < 1000; i++) {
            if (i == 900){
                //调用stop方法切换标志位,让flag切换程false,让线程停止
                testStop.stop();
                System.out.println("该线程停止");
            }
        }
    }

}
线程的休眠 sleep

指定当前线程阻塞的毫秒数;

存在异常InterruptedException;

时间达到后线程进入就绪状态;

可以模拟网络延时,倒计时等;

重点:每个对象都有一个锁,sleep不会释放锁;

//模拟倒计时
public class TestSleep2 {
    public static void main(String[] args) {
        try {
            tenDown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    public static void tenDown() throws InterruptedException {
        int num = 10;
        while (true){
            Thread.sleep(10);
            System.out.println( num--);
            if (num <= 0){
                break;
            }
        }
    }

}

线程礼让 Thread.yield()

礼让线程,让当前正在执行的线程暂停,但阻塞

将线程从运行状态转为就绪状态

注意:让CPU重新调度,礼让不一定成功!看CPU心情

public class TestYield {
    public static void main(String[] args) {
        MyYield myYield = new MyYield();

        new Thread(myYield,"a").start();
        new Thread(myYield,"b").start();
    }
}

class MyYield implements Runnable{

    @Override
    public void run(){
        System.out.println(Thread.currentThread().getName()+"线程开始执行");
        Thread.yield();
        System.out.println(Thread.currentThread().getName()+"线程停止执行");
    }
}
线程的强制执行 join()

join合并线程,待此行程执行完成后,再执行其他线程

可以想象成插队

应用:重要的线程需要先执行

public class TestJoin implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("线程vip来了"+i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        /**
        * 总结:
         *  thread线程通过join方法插队main线程,一般来说都时main线程先执行*/
        //启动我们的线程
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin);
        thread.start();
        //thread线程插队main线程(把启动的线程插队到main线程前)
        thread.join();

        for (int i = 0; i < 100; i++) {
            System.out.println("我是main线程"+i);
        }
    }
}

1.3线程状态观测

在这里插入图片描述

public class TestState {
    public static void main(String[] args) throws InterruptedException {
        //创建线程
        Thread thread = new Thread(()->{
            for (int i = 0; i < 1000; i++) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("线程死亡");
        });

//        观察状态,获取状态
        Thread.State state = thread.getState();
        //new
        System.out.println(state);
        //启动线程
        thread.start();
        //获取启动线程的状态:run
        state = thread.getState();
        System.out.println(state);
        //获取线程等待和死亡的状态,注意:线程死亡后是无法启动的
        while (state != Thread.State.TERMINATED){//只要线程不终止,就一直输出状态
            Thread.sleep(100);
            state = thread.getState();
            System.out.println(state);
        }

    }

}

1.4线程优先级

Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度哪个线程来执行

线程优先级用数字表示,范围从1~10(数字越高,优先级越高):

​ Thread.MIN_PRIORITY = 1;

​ Thread.MAX_PRIORITY = 10;

​ Thread.NORM_PRIORITY = 5;

使用以下方式改变或获取优先级:

​ getPriority().setPriority(int XXX)

注意:让CPU重新调度,不一定成功!看CPU心情 (可以看看线程礼让)

public class Demo {
    public static void main(String[] args) {
        //主线程
        System.out.println("嘻嘻,永远我最先,优先级:" + Thread.currentThread().getPriority());
        //创建对象
        MyPriority myPriority = new MyPriority();
        //创建线程->小明
        Thread thread = new Thread(myPriority,"小明");
        //设置优先级
        thread.setPriority(1);
        //创建线程->小黑
        Thread thread1 = new Thread(myPriority,"小黑");
        //设置优先级
        thread1.setPriority(10);
        //启动线程
        thread.start();
        thread1.start();
    }
}

class MyPriority implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "来设置优先级来啦!");
    }
}

守护线程( Daemo() )

我认为的守护线程:

​ 当 JVM 中不存在任何一个正在运行的非守护线程(例如:死循环)时,则 JVM 进程即会退出。

​ 简单来说守护线程会随者用户线程(例如:main线程)的结束而结束

​ 应用:希望主体线程结束时,其他线程也可以自动关闭,即使时死循环

注意点:

线程分为:用户线程和守护线程

虚拟机必须确保用户线程执行完毕

虚拟机不用等待守护线程执行完毕

/**
 *保护线程(例子:上帝的守护)
 */
public class Demo {
    public static void main(String[] args) {
        //创建god对象
        God god = new God();
        //创建线程
        Thread thread = new Thread(god);
        //设置为守护线程,默认false为用户线程
        thread.setDaemon(true);
        //启动线程
        thread.start();
        //创建hum对象
        Hum hum = new Hum("小黑");
        //启动线程
        new Thread(hum).start();

    }
}

/**上帝
 * 
 */
class God implements Runnable{

    @Override
    public void run() {
        //死循环,突显虚拟机不用等待守护线程执行完毕
        while (true){
            System.out.println("上帝在守护你");
        }
    }
}

/** 人类个体
 * 
 */
class Hum implements Runnable{
    private String name;
    public Hum(String name){
        this.name = name;
    }
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println( name+ "活了"+ i+ "年");
        }
        System.out.println("====================再见世界");
    }
}

线程的同步机制

并发(多个线程操作同一个对象)

public class TestThread04 implements Runnable {
    //初始票数
    private int ticketNums = 10;
    @Override
    public void run() {
        while (true){
            if (ticketNums <= 0){
                break;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() +"我买了第" + ticketNums-- + "张");
        }

    }

    public static void main(String[] args){
        TestSleep testSleep = new TestSleep();
        new Thread(testSleep,"小明").start();
        new Thread(testSleep,"老师").start();
        new Thread(testSleep,"黄牛党").start();
    }
}

产生问题

多个线程操作同一个资源的情况下,线程不安全,数据紊乱。

解决问题

可以通过同步的同步块和通过同步方法解决

细节:处理多线程问题

多个线程访问同一个对象,并且某些线程还想修改这个对象,这个时候我们就需要线程同步。

线程同步的理解

一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成对列,等待前面线程完成后,下一个线程再使用。

举例:

​ 上厕所排队,一定要等上一个人结束后,才可以进行

线程同步的条件

​ 队列+锁(每个对象都有一个锁)

锁的作用

​ 同一进程的多个线程共享同一块存储空间,在带来便利的同时,也会带来访问冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制synchronized(当一个线程获得对象的排它锁,独占资源,其他线程必须等待)

锁的缺点

牺牲效率,但提高安全性

优先级倒置(高的等低的线程完成),引起性能问题

三大不安全案例:

1、不安全取钱

public class UnsafeBank {
    public static void main(String[] args) {
        //账户
        Account account = new Account(100,"结婚基金");
        //传入取的角色的线程名
        Drawing drawing = new Drawing(account,50,"你");
        Drawing girlFriend = new Drawing(account,100,"girlFriend");

        drawing.start();
        girlFriend.start();

    }
}
//账户
class Account{
    /**余额
     *
     */
    int money;
    /**卡名
     *
     */
    String name;

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}

/**银行:模拟取款*/
class Drawing extends Thread{
    /**账户
     *
     */
    Account account;
    /**取了多少钱
     *
     */
    int drawingMoney;
    /**现在手里有多少钱
     */
    int nowMoney;
    //指明账户,金额,名称
    public Drawing(Account account,int drawingMoney, String name){
        //设置父类的线程名
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }

    /**   取钱,通过继承Thread类重写run()
     *
     */
    @Override
    public void run(){
            //判断有没有钱
            if (account.money - drawingMoney<0){
                //没有钱
                System.out.println(Thread.currentThread().getName()+"钱不够,取不了钱");
                return;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //更新卡内余额,卡内余额 = 余额 - 取得金额
            account.money = account.money - drawingMoney;
            //判断现在的金额
            nowMoney = nowMoney + drawingMoney;
            //获取卡内余额
            System.out.println(account.name + "金额为:" + account.money);
            //Thread.currentThread().getName() = this.getName(),因为继承了Thread类
            System.out.println(this.getName() + "手里的钱"+ nowMoney);
        } 
}

2、不安全的买票

public class UnsafeBuyTicket {
    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();
        new Thread(buyTicket,"小明").start();
        new Thread(buyTicket,"小黑").start();
        new Thread(buyTicket,"黄牛党").start();
    }

}

class BuyTicket implements Runnable{
    //票
    private int ticketNums = 10;
    boolean flag = true;
    @Override
    public void run() {
        //让线程自动结束
        while (flag){
            //调用买票的方法
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }


    }

    /**    买ticketNums票
     */
    private void buy() throws InterruptedException {
        if (ticketNums <= 0){
            flag = false;
            //结束程序
            return;
        }
    //模拟延时,放大买票,更便于查出错误
        Thread.sleep(1000);
        //输出
        System.out.println(Thread.currentThread().getName() + "拿到" + ticketNums--);

    }
}

3、线程不安全的集合

public class UnsafeList {
    public static void main(String[] args) throws InterruptedException {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread( ()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        Thread.sleep(1000);
        System.out.println(list.size());
    }
}

同步方法(锁方法)

使用指南:

​ 比如买票的方法:一次只能进去一个人买,就只让一个线程进入方法

同步方法:

​ public synchronized void method( int args){}

控制对"对象"的访问,每个对象对应一把锁,每个同步方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,知道该方法返回才释放锁,后面被阻塞的线程才能这个锁,继续执行。

备注:同步方法的同步监控器是this

public class UnsafeBuyTicket {
    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();
        new Thread(buyTicket,"小明").start();
        new Thread(buyTicket,"小黑").start();
        new Thread(buyTicket,"黄牛党").start();
    }

}

class BuyTicket implements Runnable{
    //票
    private int ticketNums = 10;
    boolean flag = true;
    @Override
    public void run() {
        //让线程自动结束
        while (flag){
            //调用买票的方法
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }


    }

    /**    买ticketNums票
     *  注意:synchronized锁(同步方法)的是默认this,因为只有一个对象,所以锁的是BuyTicket
     */
    private synchronized void buy() throws InterruptedException {
        if (ticketNums <= 0){
            flag = false;
            //结束程序
            return;
        }
        //模拟延时,放大买票,更便于查出错误
        Thread.sleep(1000);
        //输出
        System.out.println(Thread.currentThread().getName() + "拿到" + ticketNums--);

    }
}

同步块(锁对象)

使用指南:多个线程操作同一个对象,比如:存钱和取钱时两个方法操作的都是同一个账户

同步块:

​ synchronized(Obj){}

Obj称为同步监控器:

​ Obj可以是任何对象,但是推荐使用共享资源作为同步监控器

同步监视器的执行过程:

​ 1、第一个线程访问,锁定同步监视器,执行其中代码

​ 2、第二个线程访问,发现同步监视器被锁定,无法访问

​ 3、第一个线程访问完毕,解锁同步监视器

​ 4、第二个线程访问,发现同步监视器没有锁,然后锁定并访问

public class UnsafeBank {
    public static void main(String[] args) {
        //账户
        Account account = new Account(100,"结婚基金");
        //传入取的角色的线程名
        Drawing drawing = new Drawing(account,50,"你");
        Drawing girlFriend = new Drawing(account,100,"girlFriend");

        drawing.start();
        girlFriend.start();

    }
}
//账户
class Account{
    /**余额
     *
     */
    int money;
    /**卡名
     *
     */
    String name;

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}

/**银行:模拟取款*/
class Drawing extends Thread{
    /**账户
     *
     */
    Account account;
    /**取了多少钱
     *
     */
    int drawingMoney;
    /**现在手里有多少钱
     */
    int nowMoney;
    //指明账户,金额,名称
    public Drawing(Account account,int drawingMoney, String name){
        //设置父类的线程名
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }

    /**   取钱,通过继承Thread类重写run()
     *
     */
    @Override
    public void run(){
        //同步块,因为有多个对象,锁的是对象为变化的量,即增删改查的对象
        synchronized (account){
            //判断有没有钱
            if (account.money - drawingMoney<0){
                //没有钱
                System.out.println(Thread.currentThread().getName()+"钱不够,取不了钱");
                return;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //更新卡内余额,卡内余额 = 余额 - 取得金额
            account.money = account.money - drawingMoney;
            //判断现在的金额
            nowMoney = nowMoney + drawingMoney;
            //获取卡内余额
            System.out.println(account.name + "金额为:" + account.money);
            //Thread.currentThread().getName() = this.getName(),因为继承了Thread类
            System.out.println(this.getName() + "手里的钱"+ nowMoney);
        }

    }
}

CopyOnWriteArrayList(了解)

public class Demo {
    public static void main(String[] args){
        //已经写好,本身就是安全的
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 1000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }

        try{
            Thread.sleep(3000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }

        System.out.println(list.size());
    }

}

死锁

定义:多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形

我的理解

死锁:多个线程互相抱着对方需要的资源,然后形成僵持
资源:口红、镜子
实现:化装问题

产生死锁的四个必要条件

互斥条件:一个资源每次只能被一个进程使用
请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系

解决方法

破坏其中的任意一个或多个条件就可以避免死锁发生

public class DeadThread {
    public static void main(String[] args) {
        Makeup g1 = new Makeup(0,"灰姑娘");
        Makeup g2 = new Makeup(1,"白雪公主");
        g1.start();
        g2.start();
    }
}
/**
 * 口红
 */
class Lipstick{

}
/**
 * 镜子
 */
class Mirror{

}
/**
 * 化装
 */
class Makeup extends Thread{
    /**
     * 需要的资源只有一份,用static来保证只有一份
     */
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();

    /**
     * 实体:选择、化装的人的姓名
     */
    int choice;
    String girlName;

    /**
     * 输入参数,初始化
     */
    public Makeup(int choice, String girlName) {
        this.choice = choice;
        this.girlName = girlName;
    }

    @Override
    public void run() {
        //化妆
        try {
            makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
    /**
     * 死锁,互相持有对方的锁,就是需要拿到对方的资源
     * 本例子的产生的条件:一人在持有口红未结束的同时去持有镜子,另一个人相反
     */
    public void makeup() throws InterruptedException{
        //先选口红,后选镜子
        if (choice == 0){
            //获取口红的锁
            synchronized (lipstick){
                System.out.println( girlName + "获取口红");
                synchronized (mirror){
                    System.out.println( girlName + "获取镜子");
                }
            }
        }else {
            synchronized (mirror){
                System.out.println( girlName + "获取镜子");
                synchronized (lipstick) {
                    System.out.println( girlName + "获取口红");
                }
            }
        }
    }
}

Lock锁

Lock锁

定义:显示定义同步锁
详细定义:
ReentrantLock(重复入锁)类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是 ReentrantLock,可以显式加锁,释放锁
注意:Lock只能锁块

public class TestLock {

    public static void main(String[] args) {
        TestLock02 testLock02 = new TestLock02();
        new Thread(testLock02).start();
        new Thread(testLock02).start();
        new Thread(testLock02).start();
    }

}

class TestLock02 implements Runnable{

    int ticketNume = 10;

    /** 定义Lock锁
     *
     */
    private final ReentrantLock lock = new ReentrantLock();


    @Override
    public void run() {
        while(true){
            //加锁
            lock.lock();
            //加锁的代码
            try {
                if (ticketNume > 0){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(ticketNume--);
                }else {
                    break;
                }
            }finally {
                //解锁
                lock.unlock();
            }

        }
    }
}

synchronized与Lock的对比

  • Lock是显性锁(手动开启和关闭锁,别忘记关闭锁)synchronized是隐形锁,出了作用域自动释放
  • Lock只有代码块锁,synchronized有代码锁和方法锁
  • 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且有更好的扩展性
  • 优先使用顺序
    • Lock > 同步代码块 > 同步方法

线程协程(生产者消费者模式=》问题)

描述:生产者和消费这个共享一个资源,并且生产者和消费者之间相互依赖,互为条件。

  • 对于生产者,没有生产产品之前,要通知消费者等待,而生产了产品之后,又需要马上通知消费者消费
  • 对于消费者,在消费之后,要通知生产者已经结束消费,需要生产新的产品以供消费
  • 在生产者消费者问题中,仅有synchronized是不够的
    • synchronized可阻止并发更新同一个共享资源,实现了同步
    • synchronized不能用来实现不同线程之间的消息传递(通信)

解决方法

在这里插入图片描述

/**
 * 测试:生产者消费者模型-->利用缓冲区解决:管程法
 * 实体:生产者,消费者,产品,缓冲区
 */
public class TestPC {
    public static void main(String[] args) {
        SynContainer container = new SynContainer();
        //创建线程
        new Productor(container).start();
        new Consumer(container).start();
    }

}

/**
 * 产品(只管生产)
 */
class Productor extends Thread{
    /**
     * 创建缓冲区对象
     */
    SynContainer container = new SynContainer();

    /**
     * 传入初始参数
     * @param container
     */
    public Productor(SynContainer container){
        this.container = container;
    }

    /**    生产
     *
     */
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            //添加i值
            container.push(new Chicken(i));
            System.out.println("生产了" + i + "只鸡");
        }

    }
}

/**
 * 消费者
 */
class Consumer extends Thread{

    SynContainer container = new SynContainer();

    public Consumer(SynContainer container){
        this.container = container;
    }

    /**
     * 消费
     */
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("消费了--->"+ container.pop().id+ "只鸡");
        }

    }
}

/**产品
 *
 */
class Chicken{
    /**
     * 产品编号
     */
    int id;
    public Chicken(int id){
        this.id = id;
    }

}

class SynContainer{
    /**
     *需要一个容器大小
     */
    Chicken[] chickens = new Chicken[10];
    /**
     * 容器计数器
     */

    int count = 0;
    /**
     * 生产者(鸡)放入产品
     */
    public synchronized void push(Chicken chicken){
        //判断容器(满)
        if (count == chickens.length){
            //通知消费者消费,生产等待
            try {
                //线程等待,直到其他线程通知
                this.wait();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
        //判断容器未满.丢入产品
        chickens[count] = chicken;
        count++;

        //通知消费者消费
        this.notifyAll();
    }
    /**
     * 消费者消费产品
     */
    public synchronized Chicken pop(){
        //判断容器(满)
        if (count == 0){
            //通知生产者生产,消费者等待
            try {
                //线程等待,直到其他线程通知
                this.wait();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
        //判断容器未满.丢入产品
       count --;
        Chicken chicken = chickens[count];

        //吃完了,通知生产者生产
        this.notifyAll();
        return chicken;
    }

}

在这里插入图片描述

/**
 * 测试生产者消费者问题2:信号灯,标志位解决
 */
public class TestPC02 {
    public static void main(String[] args) {
            Tv tv = new Tv();
            new Play(tv).start();
            new Watch(tv).start();

    }
}

/**生产者--->演员
 *
 */
class Play extends Thread{
    Tv tv = new Tv();
    public Play(Tv tv){
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if (i%2 == 0){
                this.tv.play("快乐大本营");
            }else {
                //广告
                this.tv.play("抖音记录美好生活");
            }
        }
    }
}

/**
 * 消费者---->观众
 */
class Watch extends Thread{
    Tv tv;
    public Watch(Tv tv){
        this.tv = tv;
    }

    @Override
    public void run() {
        //保证对立
        for (int i = 0; i < 20; i++) {
           this.tv.watch();
        }
    }
}

/**
 * 产品--->节目
 */
class Tv{
    //演员表演,观众等待
    //观众观看,演员等待
    /**
     * 表演节目(声音)
     */
    String voice;
    /**
     * 标志
     */
    boolean flag = true;

    /**
     * 表演
     */
    public synchronized void play(String voice){
        //为假,方法等待
        if (!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("演员表演了:"+ voice);
        //通知观众观看(通知唤醒)
        this.notify();
        this.voice = voice;
        this.flag = !this.flag;
    }
    /**
     * 观看
     */
    public synchronized void watch(){
        //为真,方法等待
        if (flag){
            try {
                this.wait();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
        System.out.println("观看了:"+ voice);
        //通知演员观看(通知唤醒)
        this.notifyAll();
        this.flag = this.flag;
    }

}

使用线程池

相关API:ExecutorService和Executors

ExecutorService:真正的线程池接口。常用类ThreadPoolExecutor

  • ​ void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable
  • Future submit(Callable task):执行任务,有返回值,一般又来执行Callable
  • void shutdown():关闭连接池

Executors:工具类,线程池的工厂类,用于创建并返回不同类型的线程池

public class TestPool {
    public static void main(String[] args) {
        //1、创建服务,创建线程池
        //构建线程池
        ExecutorService service = Executors.newFixedThreadPool(10);
        //执行
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        //结束连接
        service.shutdown();
    }
}
class MyThread implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName());
        }
    }
}

扩展:

/**     public ThreadPoolExecutor(
            int corePoolSize, - 线程池核心池的大小。
                            int maximumPoolSize, - 线程池的最大线程数。
                            long keepAliveTime, - 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
                            TimeUnit unit, - keepAliveTime 的时间单位。
                            BlockingQueue<Runnable> workQueue, - 用来储存等待执行任务的队列。
                            ThreadFactory threadFactory, - 线程工厂。
                            RejectedExecutionHandler handler)  - 拒绝策略。
 **/


public class TestPool {
    public static void main(String[] args) {
        //1、创建服务,创建线程池
        //构建线程池
        ThreadPoolExecutor service = new ThreadPoolExecutor(10,10,10, TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(1),new ThreadPoolExecutor.DiscardOldestPolicy());
        //执行
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        //2、关闭链接
        service.shutdown();
    }
}
class MyThread implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName());
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值