java多线程

一、线程简介

process进程:程序执行的过程,可以有多个线程。系统分配

thread线程:CPU调度和执行的单位。main函数:主线程,gc线程。独立的执行路径。

对同一份资源操作时,会存在资源抢夺问题,需要加入并发控制

线程会带来额外的开销。如cpu调度时间

二、线程的创建(重点)

1、继承Thread类(重点,实现Runnable接口),不建议使用:避免OOP单继承局限性

//继承Thread类,重写run() 方法,调用start() 方法开启线程
public class TestThread extends Thread{

    //重写run方法
    @Override
    public void run() {
        //线程体
        for(int i=0; i<20; i++) {
            System.out.println("我是子线程=====");
        }
    }

    public static void main(String[] args) {
        //调用子线程方法,由CPU调度
        TestThread thread = new TestThread();
        thread.start();

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

2、实现Runnable接口(重点),推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用

package com.yans.java.basic;

//实现Runnable接口,重写run方法,执行线程需要丢入runnable接口实现类,执行start方法
public class TestThread2 implements Runnable{

    @Override
    public void run() {
        //线程体
        for(int i=0; i<20; i++) {
            System.out.println("我是子线程=====");
        }
    }

    public static void main(String[] args) {

        //创建runnable接口实现类对象
        TestThread2 thread2 = new TestThread2();
        //创建线程对象,通过线程对象开启线程
//        Thread thread1 = new Thread(thread2);
//        thread1.start();

        //代理
        new Thread(thread2).start();

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

3、实现Callable接口(了解)

  • 实现Callable接口,需要返回值
  • 重写call方法,需要抛出异常
  • 创建目标对象
  • 创建执行服务:ExecuteService ser = Executors.newFixedThreadPool(1);
  • 提交执行:Future<Boolean> result1 = ser.submit(1);
  • 获取结果:boolean r1 = result1.get()
  • 关闭服务:ser.shutdownNow();
import java.util.concurrent.*;

//实现Callable接口
public class TestCallabe implements Callable<Boolean> {
    @Override
    public Boolean call() throws Exception {
        //线程体
        for(int i=0; i<20; i++) {
            System.out.println("我是子线程=====");
        }

        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //声明对象
        TestCallabe testCallabe =new TestCallabe();
        //开启执行服务
        ExecutorService ser = Executors.newFixedThreadPool(1);
        //提交执行
        Future<Boolean> result1 = ser.submit(testCallabe);
        //获取结果
        boolean r1 = result1.get();
        //关闭服务
        ser.shutdown();

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

三、静态代理

例子:

你:真实对象。实现结婚接口

婚庆公司:代理。实现结婚接口

结婚:结婚接口


//静态代理模式总结:
//真实对象和代理对象都需要实现同一个接口
//代理对象要代理真实角色

//好处:
//代理对象可以做很多真实对象做不了的事
//真实对象只需要专注做自己的事
public class StaticProxy {
    public static void main(String[] args) {
//        对比线程实现
//        new Thread(new Runnable() {
//            @Override
//            public void run() {
//                
//            }
//        }).start();
        WeddingCompany weddingCompany =new WeddingCompany(new You());
        weddingCompany.marry();
    }
}

//结婚
interface Marry {
    void marry();
}

//真实对象--》你
//真实结婚
class You implements Marry{

    @Override
    public void marry() {
        System.out.println("你要结婚了,超开心");
    }
}

//代理对象--》婚庆公司
//帮助你结婚
class WeddingCompany implements Marry{

    //获取代理真实对象
    private Marry target;

    //构造方法
    public WeddingCompany(Marry target) {
        this.target = target;
    }

    @Override
    public void marry() {
        before();
        this.target.marry(); //真实对象
        after();
    }

    private void after() {
        System.out.println("结婚后,收尾款");
    }

    private void before() {
        System.out.println("结婚前,布置场地");
    }

}

三、Lamda表达式

函数式编程

(params) -> expression[表达式]

(params) -> statement[语句]

(params) -> { statement }

new Thread(() -> System.out.println("多线程")).start();

函数式接口:任何接口,如果只包含唯一一个抽象类方法,那么它就是一个函数式接口,如

public interface Runnable {

        public abstract void run();

}

lamda表达式推导过程: 

package com.yans.java.basic;

//lamda表达式推导
public class TestLamda {

    //3、静态内部类
    static class Like2 implements ILike{

        @Override
        public void like() {
            System.out.println("I Like2");
        }
    }

    public static void main(String[] args) {
        //2、调用实现类
        ILike like = new Like();
        like.like();

        //3、静态内部类
        like = new Like2();
        like.like();

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

            @Override
            public void like() {
                System.out.println("I Like3");
            }
        }
        like = new Like3();
        like.like();

        //5、匿名内部类,没有名字,通过接口实现
        like = new ILike() {
            @Override
            public void like() {
                System.out.println("I Like4");
            }
        };
        like.like();

        //6、lamda简化
        like = () -> {
            System.out.println("I Like5");
        };
        like.like();
    }
}

//1.定义一个接口
interface ILike {
    void like();
}

//2、定义一个实现类
class Like implements ILike {

    @Override
    public void like() {
        System.out.println("I Like");
    }
}

lamda表达式简化过程:

package com.yans.java.basic;


//总结:
//lamda表达式只有一行代码的情况下可以省略{}
//前提是接口为函数式接口
//多个参数类型也可以去掉参数类型,去掉的话所有参数要都去掉,要加()
public class TestLamda2 {
    public static void main(String[] args) {
        ILove love;

        love = (int a) -> {
            System.out.println("I Love" + a);
        };
        love.love(1);

        //简化1,省略参数类型
        love = (a) -> {
            System.out.println("I Love" + a);
        };
        love.love(2);

        //简化2,省略括号
        love = a -> {
            System.out.println("I Love" + a);
        };
        love.love(3);

        //简化3,省略{}括号
        love = a -> System.out.println("I Love" + a);
        love.love(4);

    }

}

//定义一个接口
interface ILove {
    void love(int a);
}

四、线程的状态

创建、就绪、运行、阻塞、死亡

五、线程方法

1、停止线程:

  • 建议使用标志位
  • 正常停止,利用次数,不建议死循环
  • 不推荐使用JDK中提供的stop()、destroy()方法。

2、线程休眠:

  • sleep(时间),指定当前线程阻塞的毫秒数。
  • sleep存在异常InterruptedException
  • sleep到达时间后进入就绪状态
  • sleep可以模拟网络延时,倒计时
  • 每个对象都有一个锁,sleep不会释放锁

3、线程礼让yield

让当前线程暂停但不阻塞

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

让CPU重写调度,但不一定成功,看CPU心情

package com.yans.java.basic;

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());
    }
}

4、Join方法

合并线程,待此线程执行完后,其他线程才能执行

可以想象成插队

package com.yans.java.basic;

public class TestJoin implements Runnable{

    @Override
    public void run() {
        for (int i=0; i<1000; i++) {
            System.out.println("线程VIP来了" + i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        //启动线程
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin);
        thread.start();

        //主线程
        for (int i =0; i<500; i++) {
            if (i == 200){
                //当主线程执行到200时,子线程插队
                // 等子线程执行完后主线程才能继续执行
                thread.join();
            }
            System.out.println("main" + i);
        }
    }
}

5、线程状态观测

Thread.State

package com.yans.java.basic;

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

        //观察状态
        Thread.State state =thread.getState();
        System.out.println(state); //NEW

        //观察容器启动
        thread.start();
        state = thread.getState();
        System.out.println("运行中的状态" + state); //RUN

        while (state != Thread.State.TERMINATED) {
            Thread.sleep(100);
            //更新状态
            state = thread.getState();
            System.out.println(state);
        }
    }
}

6、线程优先级

setPriority(int newPriority) 更改线程优先级

getPriority() 获取线程优先级

优先级最小为1,最大为10,默认为5

package com.yans.java.basic;

public class TestPriority {

    public static void main(String[] args) {
        //主线程优先级
        System.out.println(Thread.currentThread().getName() + "===>" + Thread.currentThread().getPriority());

        MyPriorty myPriorty = new MyPriorty();
        Thread t1 = new Thread(myPriorty);
        Thread t2 = new Thread(myPriorty);
        Thread t3 = new Thread(myPriorty);
        Thread t4 = new Thread(myPriorty);
        Thread t5 = new Thread(myPriorty);
        Thread t6 = new Thread(myPriorty);

        //设置优先级,不一定优先级高的就会先跑
        t1.start();

        t2.setPriority(1);
        t2.start();

        t3.setPriority(2);
        t3.start();

        t4.setPriority(10);
        t4.start();

//        //t5会报错
//        t5.setPriority(-1);
//        t5.start();
//
//        //t6会报错
//        t6.setPriority(11);
//        t6.start();

        t5.setPriority(7);
        t5.start();

        //t6会报错
        t6.setPriority(8);
        t6.start();
    }
}

class MyPriorty implements Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "===>" + Thread.currentThread().getPriority());
    }
}

输出结果:

main===>5
Thread-5===>8
Thread-1===>1
Thread-3===>10
Thread-4===>7
Thread-0===>5
Thread-2===>2

六、守护线程

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

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

虚拟机不用等待守护线程执行完毕(gc线程),如:后台记录日志、监控日志等

package com.yans.java.basic;

//测试游湖线程
//上帝守护人类
public class TestDaemon {
    public static void main(String[] args) {
        God god = new God();
        People people = new People();

        Thread thread = new Thread(god);
        thread.setDaemon(true); //默认是false,true表示是守护线程,正常的线程都是用户线程
        thread.start();

        new Thread(people).start(); //用户线程启动,用户线程执行完之后,守护线程还会运行一段时间
    }
}

//守护线程 上帝
class God implements Runnable {

    @Override
    public void run() {
        while (true) {
            System.out.println("上帝守护着你");
        }
    }
}

//用户线程 人类
class People implements Runnable {

    @Override
    public void run() {
        for (int i=0; i<36500; i++) {
            System.out.println("人一生开心的活着");
        }
        System.out.println("goodbye world");
    }
}

七、线程同步(重点)

并发:同一对象被多个线程同时操作

线程同步就是一种等待机制,让多个对象线程进入线程等待池队列

队列和锁:

锁机制(synchronized)(安全),当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可,存在以下问题:

  • 一个线程持有锁会导致其他需要锁的线程挂起
  • 在多个线程竞争下,加锁、释放锁会导致较多的上下文切换和调度延迟,引起性能问题
  • 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级导致,引起性能问题

线程不安全例子:

package com.yans.java.basic;

//不安全买票
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();
            }
        }
    }

    private void buy() throws InterruptedException {
        //判断是否有票
        if (ticketNums <= 0) {
            flag = false;
            return;
        }

        //模拟延时,方法问题的发现性
        Thread.sleep(100);
        System.out.println(Thread.currentThread().getName() +"抢到了" + ticketNums-- +"张票");
    }


}
package com.yans.java.basic;

//不安全取钱
//两个人去银行取钱,账户
public class UnsafeDrawing {
    public static void main(String[] args) {
        Account account = new Account(100, "结婚基金");

        Drawing you = new Drawing(account,"你", 50);
        Drawing girlfried = new Drawing(account,"girlfriend", 100);

        you.start();
        girlfried.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, String name, int drawingMoney) {
        super(name); //继承父类name
        this.account = account;
        this.drawingMoney = drawingMoney;
    }

    //取钱
    @Override
    public void run() {
        //判断卡里是否有钱
        if (account.money - drawingMoney < 0) {
            System.out.println(this.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
        System.out.println(this.getName() + "手里有" + nowMoney + "钱");
    }
}
package com.yans.java.basic;

import java.util.ArrayList;

//不安全集合测试
public class TestArrayList {
    public static void main(String[] args) throws InterruptedException {
        ArrayList<String> list = new ArrayList<String>();
        for (int i=0; i<1000; i++) {
            new Thread(() -> {
                list.add(Thread.currentThread().getName());
            }).start();
        }

        Thread.sleep(1000);

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

同步方法synchronized关键字修饰

例子,解决不安全的买票问题:

package com.yans.java.basic;

//不安全买票
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();
            }
        }
    }

    //加synchronized关键字修饰,加锁 ==》解决不安全问题
    private synchronized void buy() throws InterruptedException {
        //判断是否有票
        if (ticketNums <= 0) {
            flag = false;
            return;
        }

        //模拟延时,方法问题的发现性
        Thread.sleep(100);
        System.out.println(Thread.currentThread().getName() +"抢到了" + ticketNums-- +"张票");
    }


}

同步块synchronized(Obj) { 代码块 }

 例子,解决不安全的取钱问题:

package com.yans.java.basic;

//不安全取钱
//两个人去银行取钱,账户
public class UnsafeDrawing {
    public static void main(String[] args) {
        Account account = new Account(100, "结婚基金");

        Drawing you = new Drawing(account,"你", 50);
        Drawing girlfried = new Drawing(account,"girlfriend", 100);

        you.start();
        girlfried.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, String name, int drawingMoney) {
        super(name); //继承父类name
        this.account = account;
        this.drawingMoney = drawingMoney;
    }

    //取钱
    //synchronized默认锁的是this,加关键字锁的是Drawing类,但实际修改的是Account类
    //使用synchronized块,将进行增删改的对象锁住
    @Override
    public synchronized void run() {
        //锁的对象是进行增删改的对象
        synchronized (account) {
            //判断卡里是否有钱
            if (account.money - drawingMoney < 0) {
                System.out.println(this.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
            System.out.println(this.getName() + "手里有" + nowMoney + "钱");

        }
    }
}
JUC并发线程安全:CopyOnWriteArrayList集合
package com.yans.java.basic;

import java.util.concurrent.CopyOnWriteArrayList;

public class TestJUC {
    public static void main(String[] args) throws InterruptedException {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
        for (int i=0; i<1000; i++) {
            new Thread(() -> {
                list.add(Thread.currentThread().getName());
            }).start();
        }

        Thread.sleep(1000);

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

八、死锁

多线程在共享同一资源时,互相持有对方的锁,导致一致在等待锁。

四个必要条件:

1、互斥条件:一个资源每次只能被一个进程使用

2、请求与保持:一个进程因请求资源二阻塞时,对方已获得的资源保持不放

3、不剥夺:进程已获得的资源,在未使用完之前,不能被强行剥夺

4、循环等待:若干进程之间形成一种头尾相接的循环等待资源关系

只要破坏任一条件,死锁就不会产生。

列子,产生死锁:

package com.yans.java.basic;

public class TestDeathLock {
    public static void main(String[] args) {
        Makeup g1 = new Makeup(0, "灰姑娘");
        Makeup g2 = new Makeup(1, "白雪公主");

        new Thread(g1).start();
        new Thread(g2).start();
    }
}

//口红
class Lipstic {

}

//镜子
class Mirror {

}

class Makeup implements Runnable {
    //static确保资源只有一份
    static Lipstic lipstic = new Lipstic();
    static Mirror mirror = new Mirror();

    String name; //化妆的人
    int choice; //选择 口红or镜子

    //构造函数
    public Makeup(int choice, String name) {
        this.choice = choice;
        this.name = name;
    }

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

    //互相持有对方的锁,又需要对方的资源,形成僵持
    private void makeup() throws InterruptedException {
        if (choice == 0) {
            //同时拥有两把锁
            synchronized (lipstic) {
                System.out.println(Thread.currentThread().getName() + "获得口红");
                Thread.sleep(1000);
                synchronized (mirror) {
                    System.out.println(Thread.currentThread().getName() + "获得镜子");
                }
            }
        } else {
            synchronized (mirror) {
                System.out.println(Thread.currentThread().getName() + "获得镜子");
                Thread.sleep(2000);
                synchronized (lipstic) {
                    System.out.println(Thread.currentThread().getName() + "获得口红");
                }
            }
        }

    }
}

破坏死锁条件,不要同时拥有两把锁:

package com.yans.java.basic;

public class TestDeathLock {
    public static void main(String[] args) {
        Makeup g1 = new Makeup(0, "灰姑娘");
        Makeup g2 = new Makeup(1, "白雪公主");

        new Thread(g1).start();
        new Thread(g2).start();
    }
}

//口红
class Lipstic {

}

//镜子
class Mirror {

}

class Makeup implements Runnable {
    //static确保资源只有一份
    static Lipstic lipstic = new Lipstic();
    static Mirror mirror = new Mirror();

    String name; //化妆的人
    int choice; //选择 口红or镜子

    //构造函数
    public Makeup(int choice, String name) {
        this.choice = choice;
        this.name = name;
    }

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

    //互相持有对方的锁,又需要对方的资源,形成僵持
    private void makeup() throws InterruptedException {
        if (choice == 0) {
            //只拥有一把锁
            synchronized (lipstic) {
                System.out.println(Thread.currentThread().getName() + "获得口红");
                Thread.sleep(1000);

            }
            synchronized (mirror) {
                System.out.println(Thread.currentThread().getName() + "获得镜子");
            }
        } else {
            synchronized (mirror) {
                System.out.println(Thread.currentThread().getName() + "获得镜子");
                Thread.sleep(2000);

            }
            synchronized (lipstic) {
                System.out.println(Thread.currentThread().getName() + "获得口红");
            }
        }

    }
}

九、Lock锁

java.util.concurrent.Lock 接口

ReentrantLock 可重入锁,JVM花费调度时间更少,性能更快。

例子,解决不安全买票问题

package com.yans.java.basic;

import java.util.concurrent.locks.ReentrantLock;

//不安全买票
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; //停止标志

    //定义锁
    ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        //买票
        while (flag) {
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    //加synchronized关键字修饰,加锁 ==》解决不安全问题
    private void buy() throws InterruptedException {

        lock.lock(); //加锁
        try {
            //判断是否有票
            if (ticketNums <= 0) {
                flag = false;
                return;
            }

            //模拟延时,方法问题的发现性
            Thread.sleep(100);
            System.out.println(Thread.currentThread().getName() +"抢到了" + ticketNums-- +"张票");
        } finally {
            lock.unlock(); //解锁
        }
    }


}

十、线程协作(生产者消费者模式)

一个线程同步问题,生产者和消费者共享一个资源,互相依赖,互为条件

线程通信

wait() 等待

wait(long timeout) 指定等待毫秒数

notify() 唤醒等待的资源

notifyAll() 

解决方法:

1、管程法

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAcXFfMzc3MTg1ODU=,size_20,color_FFFFFF,t_70,g_se,x_16

package com.yans.java.basic;

//测试生产者消费者模式--》管程法
//生产者、消费者、产品(鸡)、数据缓冲区
public class TestPC {
    public static void main(String[] args) {
        synContainer synContainer = new synContainer();

        Producer producer = new Producer(synContainer);
        Customer customer = new Customer(synContainer);

        producer.start();
        customer.start();
    }
}

//生产者
class Producer extends Thread {
    synContainer synContainer;

    public Producer(synContainer synContainer) {
        this.synContainer = synContainer;
    }

    @Override
    public void run() {
        for (int i=0; i<100; i++) {
            synContainer.push(new Chicken(i));
        }
    }
}

//消费者
class Customer extends Thread {
    synContainer synContainer;

    public Customer(synContainer synContainer) {
        this.synContainer = synContainer;
    }

    @Override
    public void run() {
        for (int i=0; i<100; i++) {
            synContainer.pop();
        }
    }
}

//产品
class Chicken {
    int id;

    public Chicken(int id) {
        this.id = id;
    }
}

//数据缓冲区
class synContainer {
    Chicken[] chickens = new Chicken[100];
    int count = 0; //记录鸡的数量

    //生产鸡
    public synchronized Chicken push(Chicken chicken) {
        //判断容器是否满了
        if (count == chickens.length) {
            try {
                this.wait(); //等待消费者消费
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } else {
            chickens[count] = chicken; //把鸡放到容器里
            count ++;
            System.out.println("生产者生产了第" + count + "只鸡");
            this.notifyAll(); //通知消费者消费
        }

        return chicken;
    }

    //消费者消费鸡
    public synchronized Chicken pop() {
        //判断是否有鸡
        if (count == 0) {
            try {
                this.wait(); //没有鸡,需要等待生产者生产
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //如果还有,继续消费
        System.out.println("消费者消费了第" + count + "只鸡");
        count --;
        Chicken chicken = chickens[count];

        //消费完,通知生产者生产
        this.notifyAll();

        return chicken;
    }

}

2、信号灯法

package com.yans.java.basic;

//测试生产者消费者模式==》信号灯法,标志位解决
public class TestPC2 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Player(tv).start();
        new Watcher(tv).start();
    }
}

//生产者 ==》演员
class Player extends Thread {
    TV tv;
    public  Player(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 Watcher extends Thread {
    TV tv;
    public  Watcher(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.notifyAll();
        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;
    }
}

十一、线程池

经常创建和销毁、使用量特别大的资源,如并发情况下的线程,对性能影响很大。

思路:提前创建好多个线程,放到线程池中,使用时直接取,用完放回池中。

JDK5.0后提供了线程池相关API:ExecutorServer和Executors。

ExecutorServer:真正的线程池接口,常见子类ThreadPoolExecutor:

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

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

package com.yans.java.basic;

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestPool {

    public static void main(String[] args) {
        //创建服务,创建线程池参数为线程池大小
        ExecutorService ser = Executors.newFixedThreadPool(10);

        //执行
        ser.execute(new MyThread());
        ser.execute(new MyThread());
        ser.execute(new MyThread());
        ser.execute(new MyThread());
        ser.execute(new MyThread());

        //关闭连接
        ser.shutdown();
    }
}

class MyThread implements Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

十二、总结

1、线程创建

  • 继承Thread类
  • 实现Runnable接口
  • 实现Callable接口

2、同步

synchronized

Lock锁

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值