JAVA多线程学习

1.线程简介

  1. 程序是指令和数据的有序集合,本身没有任何运行的含义,是一个静态的概念

  2. 进程是执行程序的一次执行过程,是一个动态的概念。是系统资源分配的单位

  3. 一个进程中可以包含多个线程,一个进程也至少有一个线程。线程是CPU调度和执行的单位。

  • 一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序不是人为可以干涉的
  • 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

在这里插入图片描述

2.线程实现

2.1线程创建

  1. 继承Thread类
    • 创建Thread类的子类
    • 重写run()方法
    • 创建子类对象,调用start()方法
/继承Thread
//创建Thread子类  , 重写run()方法 , 创建子类的对象 调用start方法
public class ThreadDemo1 extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("I'm start" + i);
        }
    }
    public static void main(String[] args) {
        ThreadDemo1 threadDemo1 = new ThreadDemo1();
        threadDemo1.start();
        
        for (int i = 0; i < 1000; i++) {
            System.out.println("我在学习多线程" + i);
        }
    }
}
//run()方法与主方法交替出现 两个线程!

代码运行结果

练习:利用多线程同步下载网络图片

package demo1;

import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;

public class ThreadDemo2 extends Thread{
    private String url; //网络图片地址
    private String name; //保存的文件名

    public ThreadDemo2(String url, String name) {
        this.url = url;
        this.name = name;
    }

    //线程体执行下载的任务
    @Override
    public void run() {
        WebDownLoader webDownLoader = new WebDownLoader();
        webDownLoader.downLoader(url,name);
        System.out.println(name + "文件已经下载好");
    }

    //创建三个线程对象,”同时“实现下载任务
    public static void main(String[] args) {
        ThreadDemo2 t1 = new ThreadDemo2("url");
        ThreadDemo2 t2 = new ThreadDemo2("url");
        ThreadDemo2 t3 = new ThreadDemo2("url");

        t1.start();
        t2.start();
        t3.start();
    }
}
//下载器
class WebDownLoader{
    public void downLoader(String url,String name){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常,downLoader方法出现问题");
        }
    }
}
//线程创建好了 并不一定立即实现

在这里插入图片描述
2. 实现Runnable接口

  • 实现runnable接口
  • 重写run()方法
  • 执行线程时需要丢入runnable接口实现类,调用start方法
package demo1;

//创建MyRunnable类实现Runnable接口
//重写run方法
//创建实现类丢入代理类中,调用start方法启动
public class ThreadDemo3 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("I'm start" + i);
        }
    }
    public static void main(String[] args) {
        ThreadDemo3 threadDemo3 = new ThreadDemo3();//实现类
        new Thread(threadDemo3).start();//代理类

        for (int i = 0; i < 1000; i++) {
            System.out.println("我在学习多线程" + i);
        }
    }
}

两种方法对比

继承Thread类:

  • 子类继承Thread类具备多线程能力

  • 启动线程:子类.start();

  • **不建议使用:**避免单继承局限性

实现Runnable类:

  • 实现接口Runnable具备多线程能力

  • 启动线程:传入目标对象+Thread对象.start()

  • **建议使用:**避免单继承局限性,灵活方便,方便同一对象被多个线程使用

  1. 实现callable接口
  • 好处:

    • 可以定义返回值
    • 可以抛出异常
  • package demo1;
    
    import java.util.concurrent.*;
    
    public class TestCallable implements Callable<Boolean> {
        private String url; //网络图片地址
        private String name; //保存的文件名
    
        public TestCallable(String url, String name) {
            this.url = url;
            this.name = name;
        }
    
        //线程体执行下载的任务
        @Override
        public Boolean call() {
            WebDownLoader webDownLoader = new WebDownLoader();
            webDownLoader.downLoader(url,name);
            System.out.println(name + "文件已经下载好");
            return true;
        }
    
        //创建三个线程对象,”同时“实现下载任务
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            TestCallable t1 = new TestCallable("url");
            TestCallable t2 = new TestCallable("url");
            TestCallable t3 = new TestCallable("url");
            //两种方式均可启动线程
            //参数可以丢callable的实现类,也可以丢入Runnable接口的实现类
            FutureTask<Boolean> futureTask = new FutureTask<Boolean>(t1);
            new Thread(futureTask).start();
            
            boolean flag = futureTask.get();//获得返回值
    
            /*//创建执行服务
            ExecutorService ser = Executors.newFixedThreadPool(3);
    
            //提交执行
            Future<Boolean> r1 = ser.submit(t1);
            Future<Boolean> r2 = ser.submit(t2);
            Future<Boolean> r3 = ser.submit(t3);
    
            //获取结果
            boolean rs1 = r1.get();
            boolean rs2 = r2.get();
            boolean rs3 = r3.get();
    
            //关闭服务
            ser.shutdownNow();*/
        }
    }
    
    

2.2模拟龟兔赛跑

package demo1;

public class Race implements Runnable{
    private String winner;//胜利者

    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            //模拟兔子睡觉
            if(Thread.currentThread().getName().equals("兔子") && (i%10 == 0)){
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //判断比赛是否结束
            boolean flag = gameOver(i);
            if(flag){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"-->走了" + i + "步");
        }
    }
    public boolean gameOver(int steps){
        if(winner!=null) {//winner 已经存在
            return true;
        } else{
            //如果winner不存在 就判断步数
            if(steps>=100){
                winner = Thread.currentThread().getName();
                System.out.println("winner is " + winner);
                return true;
            }
        }return false;
    }

    public static void main(String[] args) {
        Race race = new Race();

        new Thread(race,"兔子").start();
        new Thread(race,"乌龟").start();
    }
}

在这里插入图片描述

2.3静态代理模式

package demo1;

//婚庆公司 模拟静态代理
//真实对象 代理对象 实现同一个接口
/*  代理对象可以做很多真实对象不做的事情
    真实对象可以专注做自己的事情
*/
public class StaticProxy {
    public static void main(String[] args) {
        You you = new You();
        new WeddingCompany(you).happyMarry();
    }
}
//代理对象
class WeddingCompany implements Marry{

    private Marry target;

    public WeddingCompany(Marry target) {
        this.target = target;
    }

    @Override
    public void happyMarry() {
        before();
        target.happyMarry();
        after();
    }

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

    private void before() {
        System.out.println("布置婚房");

    }
}
//真实对象
class You implements Marry{

    @Override
    public void happyMarry() {
        System.out.println("要结婚了");
    }
}
interface Marry{
    void happyMarry();
}

2.4lamda表达式

  1. 函数式接口:

    只有一个方法的接口称为函数式接口,Lambda表达式只能实现函数式接口

  2. Lambda表达式的推导

package demo1;
//lambda表达式简化过程
public class TestLambda01 {
    //3. 定义静态内部类
    static class Like2 implements ILike{
        @Override
        public void like() {
            System.out.println("I Like You2");
        }
    }
    public static void main(String[] args) {
        ILike like = new Like1();
        like.like();

        like = new Like2();
        like.like();
        //4. 定义局部内部类
        class Like3 implements ILike{
            @Override
            public void like() {
                System.out.println("I Like You3");
            }
        }
        like = new Like3();
        like.like();
        //5. 定义匿名内部类 没有名字的类, 必须借助接口名或者父类名定义
        like = new ILike() {
            @Override
            public void like() {
                System.out.println("I Like You4");
            }
        };
        like.like();
        //6. lambda表达式
        like = ()->{
            System.out.println("I Like You5");
        };
        like.like();
    }
}
//1. 定义函数式接口
interface ILike{
    void like();
}
//2. 函数式接口实现类
class Like1 implements ILike{
    @Override
    public void like() {
        System.out.println("I Like You1");
    }
}

总结

​ 只有函数式接口才能用lambda实现

  1. Lambda表达式的简化
    1. 参数类型可以省略
    2. 圆括号可以省略(没有参数或者多个参数时不能省略)
    3. 只有一句代码时可以简化成一行
like = ()->System.out.println("I Like You5");

3.线程状态

线程只能启动一次
在这里插入图片描述
在这里插入图片描述

3.1停止状态

/*线程停止的几种方法
* 1. 建议让线程自然停止--->利用次数
* 2. 建议使用标志位--->设置一个标志位
* 3. 不建议使用jdk不推荐的方法 stop() destroy()
* */
public class TestStop implements Runnable{
    //定义一个boolean变量 控制线程的停止
    private boolean flag = true;
    private int a = 0;
    @Override
    public void run() {
        while(flag){
            System.out.println("Thread...Run"+ a);
            a++;
        }
    }
    //自定义一个停止方法 转换标志
    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++) {
            System.out.println("main"+i);
            if(i==900){
                testStop.stop();
                System.out.println("Stop");
            }
        }
    }
}

3.2休眠状态

模拟网络延时:放大问题的发生性!!!

package state;
//模拟网络延时:放大问题的发生性!!!
//做一个十秒钟的倒计时
public class TenDown implements Runnable{
    private int i = 10;
    @Override
    public void run() {
        while(true){
            try {
                System.out.println(i--);
                Thread.sleep(1000);//线程休眠
                if(i<=0){
                    break;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

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

3.3礼让状态

礼让不一定成功

package state;

public class TestYield implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"-->start");
        Thread.yield();//礼让
        System.out.println(Thread.currentThread().getName()+"-->end");
    }

    public static void main(String[] args) {
        TestYield testYield = new TestYield();

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

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

3.4强制执行(join)

不建议使用

package state;

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 join = new TestJoin();
        Thread thread = new Thread(join);
        thread.start();

        //主线程
        for (int i = 0; i < 500; i++) {
            System.out.println("main"+i);
            if(i==200) {
                thread.join();//线程插队 强制执行 其他线程进入阻塞状态
            }
        }
    }
}

3.5观测线程状态

package state;

public class TestState{
    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);
        //线程启动
        thread.start();
        state = thread.getState();
        System.out.println(state);
        
        while(state!=Thread.State.TERMINATED){
            Thread.sleep(200);//防止打印太快(主线程休眠)
            state = thread.getState();
            System.out.println(state);
        }
    }
}

3.6线程优先级

先设置优先级再执行

优先级高的不一定先执行!!!

public class TestPriority implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"--->"
                + Thread.currentThread().getPriority());
    }

    public static void main(String[] args) {
        TestPriority testPriority = new TestPriority();

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

        t1.start();

        t2.setPriority(6);
        t3.setPriority(8);
        t4.setPriority(3);
        t5.setPriority(9);
        t6.setPriority(2);

        t2.start();
        t3.start();
        t4.start();
        t5.start();
        t6.start();
    }
}

在这里插入图片描述

3.7守护线程

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

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

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

package state;
//测试守护线程
//虚拟机不用等待守护线程执行完毕再结束
public class TestDemon {
    public static void main(String[] args) {
        You you = new You();
        God god = new God();

        Thread thread = new Thread(god);
        thread.setDaemon(true);//设置为守护线程 默认值为false
        thread.start();//启动守护线程

        new Thread(you).start();//启动用户线程 一般线程都为用户线程
    }
}
class You implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 30000; i++) {
            System.out.println("开心的活着!");
        }
        System.out.println("=====you dead=====");
    }
}
class God implements Runnable{
    @Override
    public void run() {
        while(true){
            System.out.println("上帝守护着你");//上帝永生,但是再你去世之后一段时间 虚拟机会停止
        }
    }
}

4.线程同步

**线程同步:**即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态。多个线程操作统一资源时需要同步

线程的不安全:

​ 访问冲突问题

4.1三大不安全案例

package syn;
//不安全的买票问题
public class UnsafeBuyTicket {
    public static void main(String[] args) {
        Station station = new Station();

        new Thread(station,"小明").start();
        new Thread(station,"小红").start();
        new Thread(station,"黄牛党").start();
    }
}
class Station implements Runnable{

    private int ticketNums = 10;//车票
    private boolean flag = true;//外部标志 停止线程
    @Override
    public void run() {
        while(flag){
            buy();
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }//车票为零则退出
            if(ticketNums<=0){
                break;
            }
        }
    }
    public void buy(){
        //判断是否有车票
        if(ticketNums<=0){
            System.out.println("票卖完了,没有了");
            return;
        }
        System.out.println(Thread.currentThread().getName() +
                "--->拿到了第" + ticketNums-- +"张票");
    }
}

在这里插入图片描述

package syn;
//不安全的取钱问题
public class UnsafeDrawMoney {
    public static void main(String[] args) {
        Account account = new Account("结婚基金",100);
        Bank you = new Bank("you",0,50,account);
        Bank girlFriend = new Bank("girlFriend",0,100,account);
        you.start();
        girlFriend.start();

    }
}
//账户
class Account{
    private String name;
    private int accountMoney;

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

    public int getAccountMoney() {
        return accountMoney;
    }

    public String getName() {
        return name;
    }

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

    public void setAccountMoney(int accountMoney) {
        this.accountMoney = accountMoney;
    }
}
//银行
class Bank extends Thread{
    private Account account;
    private int nowMoney;
    private int drawMoney;

    public Bank(String name, int nowMoney, int drawMoney, Account account) {
        super(name);
        this.account = account;
        this.nowMoney = nowMoney;
        this.drawMoney = drawMoney;
    }

    @Override
    public void run() {
        try {
            drawMoney();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public void drawMoney() throws InterruptedException {
        if(account.getAccountMoney()-drawMoney<=0){
            System.out.println(Thread.currentThread().getName() + "--->取不到钱了,卡里的余额不足");
        }
        Thread.sleep(100);
        account.setAccountMoney(account.getAccountMoney()-drawMoney);//更新账户余额
        nowMoney = nowMoney + drawMoney;//更新现有余额
        System.out.println(Thread.currentThread().getName()+"--->取了"+drawMoney +
                ",手中还有" + nowMoney);
        System.out.println("卡里余额--->"+account.getAccountMoney());
    }
}

在这里插入图片描述

package syn;

import java.util.ArrayList;
import java.util.List;
//线程不安全的集合
public class UnsafeList {
    public static void main(String[] args) throws InterruptedException {
        List<String> list = new ArrayList<String>();
        for (int i = 0; i < 100000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        Thread.sleep(3000);
        System.out.println(list.size());
    }
}

在这里插入图片描述

4.2解决方法

​ 为了保证数据在方法中被访问时的正确性,再访问时加入**锁机制synchronized**,当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可。(队列+锁 —> 可以想象为多人上一个厕所)

  • 也会存在以下问题**(安全和性能不可兼得)**:
    • 一个线程持有锁会导致其他所有需要此锁的线程挂起(阻塞)
    • 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题
    • 如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能问题(性能导致)

解决方法

  1. synchronized方法

    //同步方法:
    public synchronized void function(){}
    
    //修改不安全的买票问题
    package syn;
    //不安全的买票问题
    public class UnsafeBuyTicket {
        public static void main(String[] args) {
            Station station = new Station();
    
            new Thread(station,"小明").start();
            new Thread(station,"小红").start();
            new Thread(station,"黄牛党").start();
        }
    }
    class Station implements Runnable{
    
        private int ticketNums = 10;//车票
        private boolean flag = true;//外部标志 停止线程
        @Override
        public void run() {
            while(flag){
                buy();
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }//车票为零则退出
                if(ticketNums<=0){
                    break;
                }
            }
        }
        //修改为同步方法,修改run()方法为同步方法也可。默认同步监视器均为这个对象即class station
        public synchronized void buy(){
            //判断是否有车票
            if(ticketNums<=0){
                System.out.println("票卖完了,没有了");
                return;
            }
            System.out.println(Thread.currentThread().getName() +
                    "--->拿到了第" + ticketNums-- +"张票");
        }
    }
    

    若将一个大的方法申明为同步方法,则会大大影响效率

    方法里面需要修改的内容才需要加锁,锁的太多浪费资源

  2. synchronized块

    synchronized(Obj){}
    /*
        Obj: 称为同步监视器;推荐使用共享资源作为同步监视器。
        同步方法中不需要指定同步监视器,默认为this这个对象
    */
    
    //修改不安全的集合
    public class UnsafeList {
        public static void main(String[] args) throws InterruptedException {
            List<String> list = new ArrayList<String>();
            for (int i = 0; i < 100000; i++) {
                new Thread(()->{
                    //加入同步块 将贡献资源list作为同步监视器
                    synchronized (list){
                        list.add(Thread.currentThread().getName());
                    }
                }).start();
            }
            Thread.sleep(3000);
            System.out.println(list.size());
        }
    }
    
    //修改不安全的取钱问题
    package syn;
    //不安全的取钱问题
    public class UnsafeDrawMoney {
        public static void main(String[] args) {
            Account account = new Account("结婚基金", 100);
            Bank you = new Bank("you", 0, 50, account);
            Bank girlFriend = new Bank("girlFriend", 0, 100, account);
            you.start();
            girlFriend.start();
        }
    }
    //账户
    class Account{
        private String name;
        private int accountMoney;
    
        public Account(String name, int accountMoney) {
            this.name = name;
            this.accountMoney = accountMoney;
        }
    
        public int getAccountMoney() {
            return accountMoney;
        }
    
        public void setAccountMoney(int accountMoney) {
            this.accountMoney = accountMoney;
        }
    }
    //银行
    class Bank extends Thread{
        private Account account;
        private int nowMoney;
        private int drawMoney;
    
        public Bank(String name, int nowMoney, int drawMoney, Account account) {
            super(name);
            this.account = account;
            this.nowMoney = nowMoney;
            this.drawMoney = drawMoney;
        }
    
        @Override
        public void run() {
            try {
                drawMoney();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        public void drawMoney() throws InterruptedException {
            //加入同步块,因为账户为共享资源 则同步监视器为account
            synchronized (account){
                if(account.getAccountMoney()-drawMoney<=0){
                    System.out.println(Thread.currentThread().getName() +
                            "--->想取" + drawMoney + "卡里的余额不足");
                    return;
                }
                Thread.sleep(100);
                account.setAccountMoney(account.getAccountMoney()-drawMoney);//更新账户余额
                nowMoney = nowMoney + drawMoney;//更新现有余额
                System.out.println(Thread.currentThread().getName()+"--->取了"+drawMoney +
                        ",手中有" + nowMoney);
                System.out.println("卡里余额--->"+account.getAccountMoney());
            }
        }
    }
    

4.3死锁

package syn;

//测试死锁
//模拟化妆
public class DeadLock {
    public static void main(String[] args) {
        Makeup g1 = new Makeup(0, "小红");
        Makeup g2 = new Makeup(1, "小芳");

        g1.start();
        g2.start();
    }
}
//镜子
class Mirror{ }
//口红
class Lipstick{ }
//化妆
class Makeup extends Thread{
    //需要的资源
    //且需要用static 保证资源的唯一性
    private static Mirror mirror = new Mirror();
    private static Lipstick lipstick = new Lipstick();
    private int choice;//做出选择 先拿什么
    private String name;

    public Makeup(int choice,String name) {
        super(name);
        this.choice = choice;
    }

    @Override
    public void run() {
        try {
            makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    private void makeup() throws InterruptedException {
        if(choice == 0){
            synchronized (mirror){
                System.out.println(Thread.currentThread().getName()+
                        "拿到了镜子");
                Thread.sleep(1000);//一秒钟之后想要拿到口红
                synchronized (lipstick) {
                    System.out.println(Thread.currentThread().getName() +
                            "拿到了口红");
                }
            }
        }else{
            synchronized (lipstick){
                System.out.println(Thread.currentThread().getName()+
                        "拿到了口红");
                Thread.sleep(2000);//一秒钟之后想要拿到镜子
                synchronized (mirror) {
                    System.out.println(Thread.currentThread().getName() +
                            "拿到了镜子");
                }
            }
        }
    }
}

4.4lock锁

package gaoji;

import java.util.concurrent.locks.ReentrantLock;

public class TestLock {
    public static void main(String[] args) {
        Station station = new Station();

        new Thread(station).start();
        new Thread(station).start();
        new Thread(station).start();
    }
}
class Station implements  Runnable{

    ReentrantLock lock = new ReentrantLock();
    int ticketNums = 10;
    @Override
    public void run() {
        try {
            lock.lock();//开锁
            while(true){
                if(ticketNums<=0){
                    System.out.println("卖完了");
                    return;
                }
                System.out.println("拿到了第" + ticketNums-- + "张票");
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();//解锁
        }
    }
}

4.5lock 与synchronized 对比

  1. Lock是显式锁(手动开启和关闭锁) synchronized是隐式锁,出了代码块之后自动释放
  2. Lock只有代码块锁,而synchronized可以锁方法和代码块
  3. Lock锁,JVM将花费较少的时间来调度,性能更好。并且具有更好的拓展性(提供更多的子类)
  4. 使用顺序一般为:Lock—>同步代码块—>同步方法

5.线程通信问题

并发协作模型 “生产者、消费者 ”

5.1管程法

package gaoji;


/*  线程之间的通信
    并发协作模型 ”生产者 消费者模式“-->解决方法1 -->缓冲区:管程法
    生产者:负责生产数据的模块
    消费者:负责处理数据的模块
    缓冲区:生产者将数据放入缓冲区 消费者从缓冲区中拿走数据
 */

//生产者 消费者 产品 缓冲区
public class TestPC {
    public static void main(String[] args) {
        Container container = new Container();

        Productor productor = new Productor(container);
        Consumer consumer = new Consumer(container);

        productor.start();
        consumer.start();
    }
}
//生产者
class Productor extends Thread{
    Container container;

    public Productor(Container container){
        this.container = container;
    }

    //生产者专注生产鸡
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            container.push(new Chicken(i));
            System.out.println("生产了-->" + i + "鸡");
        }
    }
}
//消费者
class Consumer extends Thread{
    Container container;

    public Consumer(Container 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 Container{
    //缓冲区大小
    Chicken[] chickens = new Chicken[10];
    //缓冲区计数器
    static int count = 0;
    //生产者生产产品
    public synchronized void push(Chicken chicken){
        //判断如果缓冲区中的产品是满的就停止生产,通知消费者消费
        if(count == chickens.length){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果没有将缓冲区填满 就继续生产
        chickens[count++] = chicken;
        //只要缓冲区中有产品就可以通知消费者消费
        this.notifyAll();
    }

    //消费者消费产品
    public synchronized Chicken pop(){
        //判断 缓冲区中是否有产品
        if(count == 0){
            //没有产品 等待生产者生产
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果有产品直接消费,同时通知生产者生产
        Chicken chicken = chickens[--count];
        this.notifyAll();
        return chicken;
    }
}

5.2信号灯法(标志位)

package gaoji;

//解决生产者 消费者2--->信号灯法(标志位)

public class TestPC2 {
    public static void main(String[] args) {
        TV tv = new TV();

        new Performer(tv).start();
        new Audience(tv).start();
    }
}
//生产者--->演员
class Performer extends Thread{
    TV tv = new TV();
    public Performer(TV tv){
        this.tv = tv;
    }
    //演员表演节目
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if(i%2==0){
                tv.show("快乐大本营");
            }else{
                tv.show("抖音:记录美好生活");
            }
        }
    }
}

//消费者--->观众
class Audience extends Thread{
    TV tv = new TV();
    public Audience(TV tv){
        this.tv = tv;
    }

    @Override
    public void run() {
        //观众专注观看
        for (int i = 0; i < 20; i++) {
            tv.watch();
        }
    }
}
//产品--->TV
//演员表演,观众等待 T
//观众观看,演员等待 F
class TV {
    //表演的节目
    String program;//节目传递在演员和观众之间
    //标志位
    boolean flag = true;

    //表演
    public synchronized void show(String program) {
        // 演员等待 flag = false
        if (!flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //演员表演节目
        System.out.println("演员表演了--->" + program);
        this.program = program;//传给观众
        //表演结束 通知观众观看
        this.flag = !this.flag;//更新标志位
        this.notifyAll();

    }

    //观看
    public synchronized void watch() {
        // 观众等待 flag = true
        if (flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //否则观众观看
        System.out.println("观众观看了--->" + this.program);
        this.flag = !this.flag;//更新标志位
        //观看结束 通知演员表演
        this.notifyAll();
    }
}

6. 线程池

package gaoji;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// 两个类:  ExecutorService--->创建服务 (callable也使用了)
//         Executors---> 工具类 用于创造并返回不同类型的线程池
public class Pool {
    public static void main(String[] args) {
        //newFixedThreadPool 参数为 :线程池大小
        //1. 创建服务
        ExecutorService service = Executors.newFixedThreadPool(10);
        //2. 执行
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        //3. 关闭服务
        service.shutdown();
    }
}
class MyThread implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
2020/5月/15好上传最新版 JavaGuide 目前已经 70k+ Star ,目前已经是所有 Java 类别项目中 Star 数量第二的开源项目了。Star虽然很多,但是价值远远比不上 Dubbo 这些开源项目,希望以后可以多出现一些这样的国产开源项目。国产开源项目!加油!奥利给! 随着越来越多的人参与完善这个项目,这个专注 “Java知识总结+面试指南 ” 项目的知识体系和内容的不断完善。JavaGuide 目前包括下面这两部分内容: Java 核心知识总结; 面试方向:面试题、面试经验、备战面试系列文章以及面试真实体验系列文章 内容的庞大让JavaGuide 显的有一点臃肿。所以,我决定将专门为 Java 面试所写的文章以及来自读者投稿的文章整理成 《JavaGuide面试突击版》 系列,同时也为了更加方便大家阅读查阅。起这个名字也犹豫了很久,大家如果有更好的名字的话也可以向我建议。暂时的定位是将其作为 PDF 电子书,并不会像 JavaGuide 提供在线阅读版本。我之前也免费分享过PDF 版本的《Java面试突击》,期间一共更新了 3 个版本,但是由于后面难以同步和订正所以就没有再更新。《JavaGuide面试突击版》 pdf 版由于我工作流程的转变可以有效避免这个问题。 另外,这段时间,向我提这个建议的读者也不是一个两个,我自己当然也有这个感觉。只是自己一直没有抽出时间去做罢了!毕竟这算是一个比较耗费时间的工程。加油!奥利给! 这件事情具体耗费时间的地方是内容的排版优化(为了方便导出PDF生成目录),导出 PDF 我是通过 Typora 来做的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值