多线程

多线程thread

重要概念

  • 程序,进程,线程

  • 程序在操作系统运行就是一个进程,一个进程可以有多个线程

  • 多线程指的是两个线程同时运行

    和之前的线性执行普通方法有区别

    image-20241104153822720

  • main方法为主线程,系统入口,没创建线程也存在的线程之一,还有gc,垃圾回收线程

创建进程

  • 继承(extends)Thread类
  • 实现(implements)Runnable类
  • 实现Callable类
  1. 继承(extends)Thread类(Thread类实现了Runnable)

    方法:继承Thread类,重写run方法,创建对象调用start方法开启线程

    调用start()方法是开启一个线程,调用run()方法只是根据顺序调用方法

    start()的结果是类似多线程,同时执行,两输出交叉出现,具体调用由cpu决定

    run方法为线程体,线程入口点

    package lesson01;
    
    public class Thread_Demo01 extends Thread{
    
        @Override
        public void run() {
            for (int i = 0; i < 500; i++) {
                System.out.println("0看代码"+i);
            }
        }
    
        public static void main(String[] args) {
            Thread_Demo01 threadDemo01 = new Thread_Demo01();
            threadDemo01.start();
            for (int i = 0; i < 1000; i++) {
                System.out.println("1学编程"+i);
            }
        }
    }
    
  • 多线程下载网图(用了工具类FileUtils.copyURLToFile)

  • 先百度下载commons-io-2.17.0.jar

  • 创建lib工具文件夹,导入commons-io-2.17.0.jar,右键添加到库

    package com.zhm.demo01;
    
    import org.apache.commons.io.FileUtils;
    
    import java.io.File;
    import java.io.IOException;
    import java.net.URL;
    
    public class Test_Thread02 extends Thread{
    
        private String url;//下载图片url
        private String name;//保存文件名
    
        public Test_Thread02(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) {
            Test_Thread02 p1 = new Test_Thread02("https://kuangstudy.oss-cn-beijing.aliyuncs.com/bbs/2023/10/16/kuangstudyb67cc598-9e1a-4e17-a7de-f4ecdd7f8d32.jpg", "1.jpg");
            Test_Thread02 p2 = new Test_Thread02("https://kuangstudy.oss-cn-beijing.aliyuncs.com/bbs/2021/07/21/kuangstudyb62b0ccb-55b5-4572-b067-347314beac15.jpg","2.jpg");
            Test_Thread02 p3 = new Test_Thread02("https://kuangstudy.oss-cn-beijing.aliyuncs.com/bbs/2021/07/21/kuangstudy69ec9992-bed7-4eec-a550-5fe2aeb44737.jpg", "3.jpg");
    
            p1.start();
            p2.start();
            p3.start();
        }
    }
    
    class WebDownloader{
        public void downloader(String url,String name){
            try {
                FileUtils.copyURLToFile(new URL(url),new File(name));
            } catch (IOException e) {
                System.out.println("downloader方法异常");
                throw new RuntimeException(e);
            }
        }
    }
    
    
  1. 实现(implements)Runnable接口(interface)

    方法:实现Runnable接口,实现run方法,创建实例对象,传入实例对象创建Thread对象.start()开启线程

    package com.zhm.demo01;
    
    public class Test_Thread03 implements Runnable{
    
        @Override
        public void run() {
            for (int i = 0; i < 1000; i++) {
                System.out.println("2这是2");
            }
        }
    
        public static void main(String[] args) {
    
            Test_Thread03 testThread03 = new Test_Thread03();
    //        Thread thread = new Thread(testThread03);
    //        thread.start();
            new Thread(testThread03).start();
            for (int i = 0; i < 1000; i++) {
                System.out.println("1这是1");
            }
        }
    }
    
    
    Thread thread = new Thread(testThread03);
    thread.start();
    

    等于

    new Thread(testThread03).start();
    

    extends Thread和implements Runnable区别

    image-20241116114430923

    例子

    一个对象被多个线程使用

    多线程抢票,并发问题

    package com.zhm.demo01;
    
    public class TestThread04 implements Runnable{
     private int ticketNums = 10;
     @Override
     public void run() {
         while (true){
             //无票退出循环
             if (ticketNums <=0){
                 break;
             }
             try {
                 //延时防止某一线程全部抢完
                 Thread.sleep(200);
             } catch (InterruptedException e) {
                 throw new RuntimeException(e);
             }
             //Thread.currentThread()返回当前线程本身,getname()方法是线程名字
             System.out.println(Thread.currentThread().getName()+"拿到了第"+(ticketNums--)+"张票");
         }
     }
    
     public static void main(String[] args) {
         TestThread04 ticket = new TestThread04();
         new Thread(ticket,"学生").start();
         new Thread(ticket,"老师").start();
         new Thread(ticket,"黄牛").start();
     }
    }
    

    龟兔赛跑案例

    一个race对象

    两个线程名,兔子和乌龟

    用距离,判断游戏是否结束,结束两者不再跑,输出胜利者名字

    run方法输出兔子乌龟跑的距离

    package com.zhm.demo01;
    
    //龟兔赛跑,两个线程比谁先到达100
    public class TestThread05 implements Runnable{
    //    private static int steps;
     private static String winnerName;
    
     @Override
     public void run() {
         for (int i = 1; i < 102; i++) {
             if (Thread.currentThread().getName().equals("兔子") && i%10==0){
                 try {
                     Thread.sleep(1);
                 } catch (InterruptedException e) {
                     throw new RuntimeException(e);
                 }
             }
             boolean flag = gameover(i);
             if (flag){
                 break;
             }
             System.out.println(Thread.currentThread().getName()+"-->到达第"+i+"m");
         }
     }
    
     //判断游戏是否结束,输出谁赢了,返回true代表结束
     public boolean gameover(int steps){
         if (winnerName!=null){
             return true;
         }
         if (steps>=101){
             winnerName = Thread.currentThread().getName();
             System.out.println("winner is "+winnerName);
             return true;
         }
         return false;
     }
    
     public static void main(String[] args) {
         TestThread05 race = new TestThread05();
         new Thread(race,"兔子").start();
         new Thread(race,"乌龟").start();
     }
    }
    
  2. 实现(implements)Callable接口(interface)

    和实现Runnable接口区别在多了两步,开启服务和关闭服务,重写call方法有返回值要抛出异常(对应run方法)

    image-20241116171617444

(静态)代理模式(多线程底部实现原理)

  • 分为接口和两个实现类

  • 两个类分为真实类和代理类,都要实现同一个接口

  • 代理类需要定义真实类,重写实现方法中传入真实类对象

  • 真实类就是普通实现类,实现接口方法

  • 好处在于真实类可以专注自身,代理类可以灵活添加操作

  • 多线程底部实现原理

  • 定义MyRunnable类实现Runnable接口,为真实类,实现重写run方法

  • Thread类实现Runnable接口,为代理类,(传入真实类实例)实现重写run方法

  • //多线程接口例子
    MyRunnable myRunnable = new MyRunnable();
    Thread thread = new Thread(myRunnable);
    thread.start();
    
    //上=下
    new Thread(new TestThread03()).start();
    
    //对照
    
    //婚庆例子
    You you = new You();
    WeddingCompany weddingCompany = new WeddingCompany(you);
    weddingCompany.happyMarry();
    //上=下
    new WeddingCompany(new You()).happyMarry();
    
  • 婚庆例子

    package com.zhm.demo02;
    
    import com.zhm.demo01.TestThread01;
    import com.zhm.demo01.TestThread02;
    import com.zhm.demo01.TestThread03;
    
    public class StaticProxy {
        public static void main(String[] args) {
            You you = new You();
            WeddingCompany weddingCompany = new WeddingCompany(you);
            weddingCompany.happyMarry();
    
            new WeddingCompany(new You()).happyMarry();
    
    //        new Thread(new TestThread03()).start();
        }
    }
    
    class You implements Marry{
    
        @Override
        public void happyMarry() {
            System.out.println("我是you,要结婚了吗");
        }
    }
    
    class WeddingCompany implements Marry{
    
        private Marry marry;
    
        public WeddingCompany(Marry marry){
            this.marry = marry;
        }
    
        @Override
        public void happyMarry() {
            before();
            this.marry.happyMarry();
            System.out.println(marry.getClass());
            System.out.println(this.marry.getClass());
            after();
        }
    
        private void after() {
            System.out.println("结婚完了,那完蛋了");
        }
    
        private void before() {
            System.out.println("结婚之前,还能逆转");
        }
    }
    
    
    interface Marry{
        void happyMarry();
    }
    

Lambda表达式

  • 主要是实现Runnable会用到

  • 要求:接口是函数式接口,接口里需要实现的方法只有一个image-20241126180444888

  • 一共有5种实现接口方法

    1. 外部类

    2. 静态内部类(加static的成员内部类)

    3. 方法内部类

    4. 匿名内部类

    5. lambda表达式 ()->{方法体}(匿名内部类简化)

      ILove love = new ILove() {
      @Overeide
      public void love(int a){
        System.out.println(a);
      }
      } 
      //-------------上 = 下----------------------------
      ILove love = (int a) -> {System.out.println(a);}
      
  • package com.zhm.lambda;
    
    public class LambdaTest01 {
    
        //2静态内部类
        static class Love2 implements ILove {
    
            @Override
            public void love(int a) {
                System.out.println("love"+a);
            }
        }
        public static void main(String[] args) {
            ILove love = new Love1();
            love.love(1);
    
            love = new Love2();
            love.love(2);
    
            //3方法内部类
            class Love3 implements ILove{
    
                @Override
                public void love(int a) {
                    System.out.println("love"+a);
                }
            }
            love = new Love3();
            love.love(3);
    
            //4匿名内部类
            love = new ILove() {
                @Override
                public void love(int a) {
                    System.out.println("love"+a);
                }
            };
            love.love(4);
    
            //5lambda表达式
            love = (int a) -> {
                System.out.println("love"+a);
            };
    
            love.love(5);
    
            //最简版本
            //去参数定义(int)要求是多个参数要去都去,去方法括号{}要求是方法只有一行代码
            love = a -> System.out.println("love"+a);
    
        }
    }
    
    interface ILove {
        void love(int a);
    }
    
    //1实现类
    class Love1 implements ILove {
    
        @Override
        public void love(int a) {
            System.out.println("love"+a);
        }
    }
    

    如果不是实现接口的类,想要把匿名内部类化简为lambda表达式,格式有不同

    Thread thread = new Thread(()->{});
    thread.start();
    //=
    new Thread(()->{}).start;
    //=
    Runnable runnable = new Runnable(){
    //重新run方法
    };//匿名内部类
    new Thread(runnbale).start;
    //=
    Runnable runnable = ()->{
    //run方法体
    };
    new Thread(runnbale).start;
    

线程状态

  • 状态有5种

  • 创建状态,就绪状态,运行状态,阻塞状态,死亡状态

    image-20241127123041038

  • 线程停止

  • 推荐定义标志位,学方法让线程自己停下,不要用已经jdk废弃的方法(@Deprecated标识代表废弃)

  • package com.zhm.state;
    
    public class TestStop implements Runnable{
    
        //用标志位停止
        private boolean flag = true;
        @Override
        public void run() {
            int i = 1;
            while (flag){
                System.out.println("Thrend is running..."+(i++));
            }
        }
        //线程停止方法,线程自己调用方法停止最安全
        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("mainThread is running..."+i);
                if (i==900) {
                    testStop.stop();
                    System.out.println("线程停止");
                }
            }
            
        }
    }
    
  • 线程休眠

  • sleep(1000)代表线程阻塞1000ms=1s,1s后线程进入就绪状态等待cpu调度

  • 主要应用于网络延时,放大问题发送;倒计时;时钟

    //倒计时
    package com.zhm.state;
    
    public class TestSleep {
        public static void main(String[] args) {
            try {
                countDown();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    
        public static void countDown() throws InterruptedException {
            int num = 10;
            while (num>=0){
                System.out.println(num--);
                Thread.sleep(1000);
            }
        }
    }
    
    //1秒1次输出当前时间
    package com.zhm.state;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    public class TestSleep2 {
        public static void main(String[] args) throws InterruptedException {
            Date startTime = new Date(System.currentTimeMillis());//获取系统当前时间
    
            //1秒1次输出时间
            while (true){
                System.out.println(new SimpleDateFormat("yy:HH:mm:ss").format(startTime));
                Thread.sleep(1000);
                startTime = new Date(System.currentTimeMillis());
            }
    
        }
    }
    
  • 礼让yield方法

  • 让当前线程由运行变为就绪状态,再由cpu重新调度

    package com.zhm.state;
    
    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()+"->start");
            Thread.yield();//礼让,让当前线程由运行->就绪
            System.out.println(Thread.currentThread().getName()+"->end");
        }
    }
    
  • 插队join方法

  • 类似现实生活插队,强制让插队线程最先运行到结束,再让cpu调度

  • 用的不多

    package com.zhm.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 testJoin = new TestJoin();
            Thread thread = new Thread(testJoin);
            thread.start();
    
            for (int i = 0; i < 500; i++) {
                if (i==50){
                    thread.join();
                }
                System.out.println("主线程"+i);
            }
        }
    }
    
  • 线程状态在代码中的表现

  • Thread.State 枚举类型

  • 线程状态。 线程可以处于以下状态之一: 
    NEW 
    尚未启动的线程处于此状态。 
    RUNNABLE 
    在Java虚拟机中执行的线程处于此状态。 
    BLOCKED 
    被阻塞等待监视器锁定的线程处于此状态。 
    WAITING 
    正在等待另一个线程执行特定动作的线程处于此状态。 
    TIMED_WAITING 
    正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。 
    TERMINATED 
    已退出的线程处于此状态。 
    一个线程可以在给定时间点处于一个状态。 这些状态是不反映任何操作系统线程状态的虚拟机状态。 
    
    package com.zhm.state;
    
    public class TestState {
        //观察线程状态
        public static void main(String[] args) throws InterruptedException {
            Thread thread = new Thread(()->{
                //一共运行5s
                for (int i = 0; i < 5; i++) {
                    try {
                        Thread.sleep(1000);
                        if (i == 4){
                            System.out.println("/////");
                        }
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
    
            });
    
            Thread.State state = thread.getState();//得到当前状态
            System.out.println(state);//New
    
            thread.start();
            state = thread.getState();//得到当前状态
            System.out.println(state);//Runnable
    
            while (state != Thread.State.TERMINATED){
                state = thread.getState();
                //sleep后状态变为TIMED_WAITING
                //TIMED_WAITING
                System.out.println(state);
                //1s监测10次状态
                Thread.sleep(100);
            }
        }
    }
    
  • 线程优先级

  • 范围1-10,超出会抛出异常,越高cpu调度可能性越大(只是可能性)

  • 默认是5

    package com.zhm.state;
    
    public class TestPriority {
        public static void main(String[] args) {
            System.out.println(Thread.currentThread().getName() + Thread.currentThread().getPriority());
    
            MyPriority p = new MyPriority();
            Thread t1 = new Thread(p);
            Thread t2 = new Thread(p);
            Thread t3 = new Thread(p);
            Thread t4 = new Thread(p);
            Thread t5 = new Thread(p);
            Thread t6 = new Thread(p);
    
            //设置优先级要在线程运行之前(1~10)
    
            t1.start();
    
            t2.setPriority(Thread.MAX_PRIORITY);//10
            t2.start();
    
            t3.setPriority(Thread.MIN_PRIORITY);//1
            t3.start();
    
            t4.setPriority(Thread.NORM_PRIORITY);//5
            t4.start();
    
            t5.setPriority(8);
            t5.start();
    
            t6.setPriority(4);
            t6.start();
    
        }
    }
    
    class MyPriority implements Runnable{
    
        @Override
        public void run() {
            //获得当前线程名字和优先级
            System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
        }
    }
    
  • 守护线程

  • 普通的线程都是用户线程,setdaemon定义为true为守护线程

  • 虚拟机会执行用户直到结束,守护线程不会管结不结束

  • 应用于后台记录操作日志,监控内存,垃圾回收

    package com.zhm.state;
    
    public class TestDaemon {
        public static void main(String[] args) {
            You you = new You();
            God god = new God();
    
            Thread thread = new Thread(god);
            thread.setDaemon(true);//设置为守护线程
    
            thread.start();
            new Thread(you).start();
        }
    }
    
    class You implements Runnable{
    
        @Override
        public void run() {
            for (int i = 0; i < 36500; i++) {
                System.out.println("活着");
            }
            System.out.println("goodbye");
        }
    }
    
    class God implements Runnable{
    
        @Override
        public void run() {
            while (true){
                System.out.println("永生");
            }
        }
    }
    

线程同步

  • 不同线程操作同一资源可能会出现并发问题,不安全

  • 要排队+锁机制解决

  • 好处:安全 坏处:效率降低

  • 线程不安全案例

  • 多个线程买票

    package com.zhm.syn;
    
    //多个人同时买票
    //可能会出现0,-1这种不安全情况,原因是多个线程同时操作票数为1时的内存
    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 ticketsNum = 10;
        private boolean flag = true;//线程停止符,true代表运行
    
        @Override
        public void run() {
            while (flag){
                buyTickets();
            }
        }
    
        //买票
        public void buyTickets(){
            if (ticketsNum<=0){
                flag = false;
                return;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
    
            System.out.println(Thread.currentThread().getName()+"买到的票序号为:"+(ticketsNum--));
    
        }
    
    }
    
  • 2个人操作1个银行账户

    package com.zhm.syn;
    
    public class UnsafeBank {
        public static void main(String[] args) {
            Account account = new Account(1000,"共同财产");
    
            getMoney p1 = new getMoney(account,500,"you");
            getMoney p2 = new getMoney(account,1000,"she");
    
            p1.start();
            p2.start();
    
        }
    }
    
    //账户
    class Account {
        public int accountMoney;//账户金额
        public String name;//账户人
    
        public Account(int accountMoney, String name) {
            this.accountMoney = accountMoney;
            this.name = name;
        }
    }
    
    //取钱
    class getMoney extends Thread{
        private Account account;//取钱账户
        private int outMoney;//取钱金额
        private int nowMoney;//现在有多少钱
    
        public getMoney(Account account,int outMoney,String outName){
            super(outName);
            this.account = account;
            this.outMoney = outMoney;
        }
    
        @Override
        public void run() {
            getMoney();
        }
    
        //取钱
        public void getMoney(){
            if (outMoney>account.accountMoney){
                System.out.println(this.getName()+"-->账户钱不够");
                return;
            };
    
            try {
                //加延时,代表两边都能取到内存的1000账户金额数据
                //相当于放大安全问题,某一线程取到数据后,等待其他线程取到数据,问题更容易发生
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
    
            account.accountMoney = account.accountMoney - outMoney;
            nowMoney = nowMoney + outMoney;
            //Thread.currentThread().getName()=相当于=this.getName()
            System.out.println(this.getName()+"取钱后,"+account.name+"余额为-->"+account.accountMoney);
            System.out.println(this.getName()+"手里有-->"+nowMoney);
        }
    }
    
  • list数组

  • 存储10000个线程名字到数列,数列大小小于10000,有几个线程重复存储到同一位置

    package com.zhm.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 < 10000; i++) {
                Thread thread = new Thread(()->{
                    list.add(Thread.currentThread().getName());
                });
                thread.start();
            }
            Thread.sleep(3000);
            System.out.println(list.size());
        }
    }
    

同步方法和同步块

  • 同步方法 synchronized修饰符

  • public synchronized void run(){}

  • 同步块 synchronized修饰符方法体内

  • synchronized(对象){},对象推荐使用共享资源

  • 对谁增删改查对象就写谁

  • 同步方法不用写对象,对象就是this,所以synchronized(this){}=方法加修饰符synchronized

    List<String> list = new ArrayList<String>();
    for (int i = 0; i < 10000; i++) {
        Thread thread = new Thread(()->{
            synchronized (list){
                list.add(Thread.currentThread().getName());
            }
        });
        thread.start();
    }
    
    public synchronized void buyTickets(){
        if (ticketsNum<=0){
            flag = false;
            return;
        }
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    
        System.out.println(Thread.currentThread().getName()+"买到的票序号为:"+(ticketsNum--));
    
    }
    
    public void getMoney(){
        synchronized (account){
            if (outMoney>account.accountMoney){
                System.out.println(this.getName()+"-->账户钱不够");
                return;
            };
    
            try {
                //加延时,代表两边都能取到内存的1000账户金额数据
                //相当于放大安全问题,某一线程取到数据后,等待其他线程取到数据,问题更容易发生
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
    
            account.accountMoney = account.accountMoney - outMoney;
            nowMoney = nowMoney + outMoney;
            //Thread.currentThread().getName()=相当于=this.getName()
            System.out.println(this.getName()+"取钱后,"+account.name+"余额为-->"+account.accountMoney);
            System.out.println(this.getName()+"手里有-->"+nowMoney);
        }
    
    }
    

    最后一个两人取一个账户钱的例子里,不能用同步方法,因为同步方法代表监视同步this,即GetMoney取钱这个对象,而实际增删改查的数据是account账户,所以要使用同步块

    Thread thread = new Thread(()->{});
    thread.start();
    //=
    new Thread(()->{}).start;
    //=
    Runnable runnable = new Runnable(){
    //重新run方法
    };//匿名内部类
    new Thread(runnbale).start;
    //=
    Runnable runnable = ()->{
    //run方法体
    };
    new Thread(runnbale).start;
    
    

死锁情况

  • 多个线程互相抱着对方需要的资源,然后形成僵持

  • 类似两个锁分别锁着对方的钥匙

    //死锁情况
    synchronized (lipStick){
        System.out.println(herName+"拿到镜子");
        Thread.sleep(2000);
        synchronized (mirror){
            System.out.println(herName+"拿到镜子");
        }
    }
    
    package com.zhm.syn;
    
    public class DeadLock {
        public static void main(String[] args) {
            Makeup girl1 = new Makeup(0,"灰姑娘");
            Makeup girl2 = new Makeup(1,"白雪公主");
    
            girl1.start();
            girl2.start();
        }
    }
    
    class LipStick{}
    
    class Mirror{}
    
    class Makeup extends Thread{
        //加static保证唯一,要改都改
        static LipStick lipStick = new LipStick();
        static Mirror mirror = new Mirror();
    
        int choic;//标识符
        String herName;//使用人的名字
    
        public Makeup(int choic, String herName) {
            this.choic = choic;
            this.herName = herName;
        }
    
        @Override
        public void run() {
            try {
                makeup();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    
        //化妆
        private void makeup() throws InterruptedException {
            if (choic == 0){
                synchronized (lipStick){
                    System.out.println(herName+"拿到口红");
                    Thread.sleep(2000);
                }
                synchronized (mirror){
                    System.out.println(herName+"拿到镜子");
                }
            }else {
                synchronized (mirror){
                    System.out.println(herName+"拿到镜子");
                    Thread.sleep(3000);
    
                }
                synchronized (lipStick){
                    System.out.println(herName+"拿到口红");
                }
            }
        }
    
    }
    

lock锁

  • 类似同步块,区别是lock是显式定义锁,范围都是显式,synorized同步块是隐式锁

  • 用lock.lock()和lock.unlock()方法包围监测内容

  • 一般try块写lock,finally写unlock

    package com.zhm.syn;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class TestLock {
        public static void main(String[] args) {
            LockBuyTicket thread = new LockBuyTicket();
    
            new Thread(thread).start();
            new Thread(thread).start();
        }
    }
    
    class LockBuyTicket implements Runnable{
    
        private final ReentrantLock lock = new ReentrantLock();
        int ticketNums = 10;
        @Override
        public void run() {
            try {
                lock.lock();
                while (ticketNums>0){
                    System.out.println(ticketNums--);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            } finally {
                lock.unlock();
            }
        }
    }
    

生产者消费者问题

  • 管程法

  • 用一个缓冲区存商品

  • 生产者满了就不存,等待;不是满就存,通知停止等待

  • 消费者空了就不取,等待;不是空就取,通知停止等待

  • 记得使用syngorized同步方法或者同步块

    package com.zhm.gaoji;
    
    //商品,生产者,消费者,缓冲区
    public class TestProdCum {
        public static void main(String[] args) {
            SynContainer synContainer = new SynContainer();
            Producer producer = new Producer(synContainer);
            Consumer consumer = new Consumer(synContainer);
    
            producer.start();
            consumer.start();
        }
    }
    //鸡
    class Chicken{
        int id;//鸡编号
    
        public Chicken(int id) {
            this.id = id;
        }
    }
    
    //生产者
    class Producer extends Thread{
        SynContainer synContainer;
    
        public Producer(SynContainer synContainer) {
            this.synContainer = synContainer;
        }
    
        @Override
        public void run() {
            //生产100次,最后一只是99
            for (int i = 0; i < 100; i++) {
                synContainer.push(new Chicken(i));
                System.out.println("生产了第"+i+"只鸡");
            }
        }
    }
    
    //消费者
    class Consumer extends Thread{
        SynContainer synContainer;
    
        public Consumer(SynContainer synContainer) {
            this.synContainer = synContainer;
        }
    
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                Chicken chicken = synContainer.pop();
                System.out.println("消费了第"+chicken.id+"只鸡");
            }
        }
    }
    
    //缓冲区,存鸡,取鸡
    class SynContainer {
        Chicken[] chickens = new Chicken[10];
        int count;//当前鸡的数量
    
        //存鸡
        public synchronized void push(Chicken chicken){
            //如果鸡满了,就不存,等待
            if (count == chickens.length){
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            //没满就存
            chickens[count++] = chicken;
    
            //存完通知
            this.notifyAll();
        }
    
        //取鸡
        public synchronized Chicken pop(){
            //如果没鸡,就不取,等待
            if (count == 0){
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
    
            //有鸡就取
            count--;
            Chicken chicken = chickens[count];
    
            //取完通知
            this.notifyAll();
            return chicken;
        }
    
    }
    
  • 信号灯法

  • 用一个标志位flag分开

  • 例如flag=t,消费者等待,生产者执行,执行完通知,flag取反

    package com.zhm.gaoji;
    
    
    public class TestProdCun02 {
        public static void main(String[] args) {
            TV tv = new TV();
            new Player(tv).start();
            new Watcher(tv).start();
    
    
        }
    }
    
    //信号灯法,
    //标志位T代表演员演出,观众等待
    //标志位F代表演员等待,观众演出
    
    //演员
    class Player extends Thread{
        TV tv;
    
        public Player(TV tv) {
            this.tv = tv;
        }
    
        @Override
        public void run() {
            for (int i = 0; i < 10; 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 < 10; i++) {
                tv.watch();
            }
        }
    }
    
    //节目
    class TV{
        boolean flag = true;
        String tvName = "begin";
    
        public synchronized void play(String tvName){
            //flag为F,演员等待
            if (!flag){
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            //否则演员开始演
            System.out.println(tvName+"演出开始");
            //通知等待的开始
            this.notifyAll();
            this.tvName = tvName;
            flag = !flag;
        }
    
        public synchronized void watch(){
            //flag为T,观众等待
            if (flag){
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            //否则观众开始看
            System.out.println("观众开始看"+this.tvName);
            //通知
            this.notifyAll();
            flag = !flag;
        }
    }
    

线程池

  • 创建有大小的线程池子,放入线程,需要就取出,优化效率

    package com.zhm.gaoji;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class TestPool {
        public static void main(String[] args) {
            //创建服务,创建线程池子
            ExecutorService service = Executors.newFixedThreadPool(10);
    
            //执行
            service.execute(new MyThread());
            service.execute(new MyThread());
            service.execute(new MyThread());
            service.execute(new MyThread());
    
            //关闭服务
            service.shutdown();
    
        }
    }
    
    class MyThread implements Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
        }
    }
    

image-20241209160751463

posted @ 2025-04-22 22:07  学习java的白菜  阅读(20)  评论(0)    收藏  举报