zoukankan      html  css  js  c++  java
  • 狂神说Java个人笔记-多线程

    多线程

    1.0本章核心概念

    • 线程就是独立的执行路径;

    • 在线程运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程;

    • main()称之为主线程,为系统的入口,用于执行整个程序;

    • 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为的干预的。

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

    • 线程会带来额外的开销,如cpu调度时间,并发控制开销。

    • 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

      创建线程对象方法

      1.Thread

      • 自定义线程类继承Thread类

      • 重写run()方法,编写线程执行体

      • 创建线程对象,调用start()方法启动线程

    //创建线程方式一:继承thread类,重写run()方法,调用start开启线程
       //总结:注意,线程开启不一定立即执行,由CPU调度执行
    public class ThreadDemo01 extends Thread {
       @Override
       public void run() {//run方法线程体
           for (int i = 0; i <100; i++) {
               System.out.println("多线程被执行了");
          }
      }

       public static void main(String[] args) {
           //main线程,主线程
           //创建一个线程对象
           ThreadDemo01 td1 = new ThreadDemo01();
           td1.start();//调用start方法开启线程
           for (int i = 0; i <1000 ; i++) {
               System.out.println("每天都在学习java");
          }
      }
    }

    练习

     

    package com.dong.thread;

    import org.apache.commons.io.FileUtils;

    import java.io.File;
    import java.io.IOException;
    import java.net.URL;

    /**
    * @author dong
    * @date 2020/5/24 - 8:39
    */
    //
    public class ThreadDemo02 extends Thread{
      private String url;//保存网络图片地址
      private String name;//保存的文件名

       public ThreadDemo02(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) {
           ThreadDemo02 t1 = new ThreadDemo02("https://dss0.baidu.com/73t1bjeh1BF3odCf/it/u=1561546013,1259770086&fm=85&s=1A21EC02EE337FAF0854119903001062","1.jpg");
           ThreadDemo02 t2 = new ThreadDemo02("https://dss0.baidu.com/73t1bjeh1BF3odCf/it/u=3506652242,2368086075&fm=85&s=8C9F875066675AAE078DE4D6030050F1","2.jpg");
           ThreadDemo02 t3 = new ThreadDemo02("https://dss0.baidu.com/73t1bjeh1BF3odCf/it/u=2623032014,2137091052&fm=85&s=F4C2BE56F74162EE0E5EEC7C03004071","3.jpg");
           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有问题");//文件下载失败
          }
      }
    }

    2.Runnable接口

    public class ThreadDemo03 implements Runnable{
       @Override
       public void run() {
           for (int i = 0; i <200 ; i++) {
               System.out.println("多线程被执行了");
          }
      }

       public static void main(String[] args) {
           new Thread(new ThreadDemo03()).start();
           for (int i = 0; i <1000 ; i++) {
               System.out.println("每天都在学习java");
          }
      }
    }

    小结

    • 继承Thread类

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

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

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

    • 实现Runnable接口

    实现接口Runnable具有多线程能力

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

    -推荐使用:避免单继承局限性,灵活方便,方便同一对象被多个线程使用

    实例

    public class ThreadDemo04 implements Runnable{
       private static String winner;//胜利者
       @Override
       public void run() {
           for (int i = 0; i <=100 ; i++) {
               //模拟兔子休息
               if(Thread.currentThread().getName().equals("兔子")&& i%20==0){
                   try {
                       Thread.sleep(10);
                  } catch (InterruptedException e) {
                       e.printStackTrace();
                  }
              }

               //判断是否结束比赛
                   boolean flag=gameOver(i);
               //如果比赛结束了,停止程序
               if(flag){
                   break;
              }
               System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"步");
          }
      }
       //判断是否完成比赛
       private boolean gameOver(int steps){
         //判断是否有胜利者
         if(winner!=null){
             return true;
        } {
             if (steps>=100){
                 winner=Thread.currentThread().getName();
                 System.out.println("winner is:"+winner);
                 return true;
            }
          }
         return false;
      }
       public static void main(String[] args) {
           new Thread(new ThreadDemo04(),"兔子").start();
           new Thread(new ThreadDemo04(),"乌龟").start();
      }
    }

    3.实现Callable接口

    1. 实现Callable接口,需要返回值类型

    2. 重写call方法,需要抛出异常

    3. 创建目标对象

    4. 创建执行服务:ExecutorService=Executor.newFixedThreadPool(1);

    5. 提交执行:Future<Boolean>result1=ser.submit(t1);

    6. 获取结果:boolean r1=result.get()

    7. 关闭服务:ser.shutdownNow();

      静态代理模式总结

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

      代理对象要代理真实角色

      好处:

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

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

      Lambda表达式

      避免匿名内部类定义过多

      其实质属于函数式编程的概念

      可以让代码看起来很简洁

      去掉了一堆没有意义的代码,只留下核心的逻辑

    函数式接口

    Functional Interface(函数式接口)

    定义:

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

    对于函数式接口,可以通过lambda表达式来创建该接口的对象。

    public class TestLambda1 {
       //2.静态内部类
       static class Love implements ILove {

           public void ILove(int a) {
               System.out.println("i like lambda" + a);
          }
      }
       public static void main(String[] args) {
         Love love=new Love();
         love.ILove(1);
         Love love1=new Love();
         love1.ILove(2);
           class Love implements ILove {
           //3.局部内部类
               public void ILove(int a) {
                   System.out.println("i like lambda" + a);
              }
          }
           Love love2 = new Love();
           love2.ILove(3);
           //4.匿名内部类
           ILove iLove=new ILove() {
               @Override
               public void ILove(int a) {
                   System.out.println("i like lambda" + a);
              }
          };
         iLove.ILove(4);
         //5.lambda表达式
           ILove iLove1=(int a)->{
               System.out.println("i like lambda" + a);
          };
           iLove1.ILove(5);
      }
    }
    //定义一个接口,只有一个方法,函数式接口
    interface ILove{
       void ILove(int a);
    }
       //1.普通实现
    class Love implements ILove {
           @Override
       public void ILove(int a) {
           System.out.println("i like lambda" + a);
      }
    }
    public class TestLambda2 {
       public static void main(String[] args) {
           YouLove youLove=(a,b)->{
               System.out.println("一句话你说:"+a+b);
          };
           youLove.youLove(10,20);
      }
    }

    interface YouLove{
       void youLove(int a,int b);
    }

    总结:

     

    1. lambda表达式只能有一行代码的情况下才能简化为一行,如果有多行,那么就用代码块包裹

    2. 前提是接口为函数式接口

    3. 多个参数也可以去掉参数类型,要去掉就都去掉,必须加上括号。

    线程停止

    public class TestStop implements Runnable {
       private boolean flag=true;
       @Override
       public void run() {
           int i=0;
           while (flag){
               System.out.println("run.....Thread"+(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("main"+i);
               if (i==900){
                   testStop.stop();
                   System.out.println("线程停止了!");
              }
          }
      }
    }

    线程休眠

    sleep(时间)指定当前线程阻塞的毫秒数;

    sleep存在异常InterruptedException

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

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

    每一个对象都有一个锁,sleep不会释放锁

    public class TestSleep1 {
       public static void main(String[] args) {
       tenDown();
       //打印当前系统时间
           Date startTime=new Date(System.currentTimeMillis());//获取当前系统时间
           while (true){
               try {
                   Thread.sleep(1000);
                   System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
                   startTime=new Date(System.currentTimeMillis());//更新时间
              } catch (InterruptedException e) {
                   e.printStackTrace();
              }
          }
      }
       //模拟倒计时
       public static void tenDown(){
           int num=10;
           while (true){
               try {
                   Thread.sleep(1000);
                   if (num<=0){
                       break;
                  }else{
                       System.out.println("倒计时!!!"+num--+"秒");
                  }
              } catch (InterruptedException e) {
                   e.printStackTrace();
              }
          }
      }
    }

    线程礼让

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

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

    让cpu重新调度,礼让不一定成功!看CPU心情。

    public class TestYield implements Runnable {
        public static void main(String[] args) {
            TestYield yield=new TestYield();
            new Thread(yield,"a").start();
            new Thread(yield,"b").start();
        }
        @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 thread= new Thread(new TestJoin());
    
                for (int i = 0; i <400 ; i++) {
                    System.out.println("主线程在排队!!!"+i);
                    if (i==100){
                        thread.start();
                        thread.join();
    
                }
            }
        }
    }

    线程状态观测

    Thread.State

    线程状态,线程可以处于一下状态之一:

    • new 尚未启动的线程处于此状态

    • Runnable 在java虚拟机中执行的线程处于此状态

    • Blocked 被阻塞等待监视器锁定的线程处于此状态。

    • Waiting 正在等待另一个线程执行特定动作的线程处于此状态。

    • Timed Waiting 正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。

    • Terminated 已退出的线程处于此状态。

    一个线程可以给定时间点处于一个状态。这些状态是不反映任何操作系统线程状态的虚拟机状态

    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);//new
            thread.start();//启动线程
            state=thread.getState();//runnable
            System.out.println(state);
            while (state!= Thread.State.TERMINATED){//只要线程不终止就输入线程状态
                Thread.sleep(100);
                state=thread.getState();
                System.out.println(state);
            }
        }
    }

     

    image-20200525151720415

    线程优先级

    java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行。

    线程的优先级用数字表示,范围从1~10。

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

    getPriority().setPriority(int xxx)

    public class TestPriority {
        public static void main(String[] args) {
            MyPriority myPriority = new MyPriority();
            Thread t1 = new Thread(myPriority);
            Thread t2 = new Thread(myPriority);
            Thread t3 = new Thread(myPriority);
            Thread t4 = new Thread(myPriority);
            Thread t5 = new Thread(myPriority);
            Thread t6 = new Thread(myPriority);
            //先设置线程优先级
            t1.setPriority(1);
            t1.start();
            t2.setPriority(3);
            t2.start();
            t3.setPriority(6);
            t3.start();
            t4.setPriority(Thread.MAX_PRIORITY);//  优先级=10
            t4.start();
            t5.setPriority(Thread.MIN_PRIORITY);// 优先级=1
            t6.setPriority(9);
            t6.start();
    
            System.out.println("main");
        }
    }
    class MyPriority implements Runnable{
    
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"---线程被执行了!---"+Thread.currentThread().getPriority());
        }
    }

    注意:先设置优先级,再start线程!!!

    守护(daemon)线程

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

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

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

    • 如,后台记录操作日志,监控内存,垃圾回收等待。。。

    • public class TestDaemon {
          public static void main(String[] args) {
              God god = new God();
              You you=new You();
              Thread thread = new Thread(god);
              thread.setDaemon(true);//默认为flase 为用户线程,  true为守护线程
              thread.start();
              new Thread(you).start();
          }
      }
      class God implements Runnable{
      
          @Override
          public void run() {
              while (true){
                  System.out.println("上帝守护着你-------");
              }
          }
      }
      class You implements Runnable{
      
          @Override
          public void run() {
              for (int i = 0; i <36500 ; i++) {
                  System.out.println("开心着活着每一天------");
              }
              System.out.println("----goodbye!Beautiful World!!!------");
      
          }
      }

      线程同步机制

      线程同步

      • 由于同一进城的多个线程共享同一块存储空间,在带来方便的同事,也带来了访问冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制synchronized,当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可,存在以下问题:

    • 一个线程持有锁会导致其它所有需要此锁的线程挂起;

    • 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题;

    • 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题。

    线程锁

    //不安全的买票
    public class UnsafeButTicket {
        public static void main(String[] args) {
            BuyTicket bt=new BuyTicket();
            new Thread(bt,"我").start();
            new Thread(bt,"你").start();
            new Thread(bt,"黄牛党").start();
        }
    }
    
    class BuyTicket implements Runnable{
        //票
        private int ticketNums=10;
         boolean flag=true;//外部停止方式
        @Override
        public void run() {
            //买票
            while (flag){
                buy();
            }
        }
    
    
        public synchronized void buy(){//锁了方法,相当于this 把类给锁住
    
            //判断是否有票
            if(ticketNums<=0){
                System.out.println("票没了");
                flag=false;
                return ;
            }
            //模拟延时
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+ticketNums--);
        }
    }
    //不安全取钱
        //两个人去银行取钱,账户
    public class UnsafeBank {
        public static void main(String[] args) {
            //账户
            Account account=new Account(100,"结婚基金");
            Drawing you=new Drawing(account,50,"你");
            Drawing girlFriend=new Drawing(account,100,"女朋友");
            you.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;
    
        }
        //取钱
    
        @Override
        public void run() {
            synchronized (account) {//锁的对象是变化的量,锁需要增删改的对象
                //判断有没有钱
                if (account.money - drawingMoney <= 0) {
                    System.out.println(Thread.currentThread().getName() + "钱不够");
                    return;
                }
                //卡内余额
                account.money -= drawingMoney;
                //手里的钱
                nowMoney += drawingMoney;
                System.out.println(account.name + "余额为:" + account.money);
                System.out.println(this.getName() + "手里的钱:" + nowMoney);
            }
        }
    }
    public class UnsafeList {
        public static void main(String[] args) {
            ArrayList<String> list=new ArrayList<String>();
            for (int i = 0; i <1000 ; i++) {
                new Thread(()->{
                    synchronized (list){
                        list.add(Thread.currentThread().getName());
                    }
                }).start();
                try {
                    Thread.sleep(30);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(list.size());
        }
    }
    

    同步块

    Synchronized(Obj){}

    Obj称之为同步监视器

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

    • 同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身,或者是class【反射中讲解】

    同步监视器的执行过程:

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

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

    3. 第一个线程访问完毕,皆出同步监视器

    4. 第二个线程访问,发现同步监视器没有锁

      死锁避免方法

      产生死锁的四个必要条件:

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

      2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不妨。

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

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

      Lock锁

      • JDK5.0开始,java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当

      • java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象

      • ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁。

        synchronized与Lock的对比

        • Lock是显式锁(手动开启和关闭锁,别忘记关闭锁)synchronized是隐式锁,出了作用域自动释放

        • Lock只有代码块加锁,synchronized有代码块锁和方法锁

        • 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)

        • 优先使用顺序:

        • Lock》同步代码块(已经进入了方法体,分配了相应资源)》同步方法(在方法体之外)

    public class TestLock {
        public static void main(String[] args) {
            Ticket ticket = new Ticket();
            new Thread(ticket).start();
            new Thread(ticket).start();
            new Thread(ticket).start();
        }
    
    }
    class Ticket extends Thread{
        private int ticketNums=10;
        //定义lock锁
        private final ReentrantLock lock=new ReentrantLock();
    
        @Override
        public void run() {
            while (true){
                try {
                    lock.lock();//加锁
                    if (ticketNums > 0) {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(ticketNums--);
                    } else {
                        break;
                    }
                }finally {
                    lock.unlock();//减锁
                }
            }
        }
    }

    线程通信

    应用场景:生产者和消费者问题

    • 假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费。

    • 如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止。

    • 如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止。

    image-20200526104229072

    //信号灯法!!!
    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++) {
                tv.watch();
            }
        }
    }
    //产品-->节目
    class TV{
        //演员表演,观众等待  T
        //观众观看,演员等待  F
        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;
        }
    }

    管程法

    //测试:生产者消费者模型--》
        //生产者,消费者,产品
    public class Tes
        public stati
            SynConta
            new Prod
            new Cons
        }
    }
    //生产者
    class Productor 
        SynContainer
        public Produ
            this.con
        }
        //生产
    
        @Override
        public void 
            for (int
                cont
                Syst
            }
        }
    }
    //消费者
    class Consumer e
        SynContainer
        public Consu
            this.con
        }
    //消费
        @Override
        public void 
            for (int
                Syst
            }
        }
    }
    //产品
    class Chicken{
        int id;//产品编
        public Chick
            this.id=
        }
    
    }
    //容器
    class SynContain
        //需要一个容器大小
        Chicken[] ch
        //容器计数器
        int count =1
        //生产者放入产品
        public synch
            //如果容器满了
            if (coun
                //同知
    
                    
                    
                    
                    
                    
    
            }
            //如果没有满,
            chickens
            count++;
            //可以同知消费
            this.not
        }
        //消费者消费产品
        public synch
            //判断能否消费
            if (coun
                //等待
                try 
                    
                } ca
                    
                }
            }
    
            //如果可以消费
            count--;
            Chicken 
            //吃完了,同知
            this.not
            return c
        }
    }

    线程池

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

    • 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。

    • 好处:

      • 提高响应速度(减少了创建新线程的时间)

      • 降低资源消耗(重复利用线程池中线程,不需要每次都创建)

      • 便于线程管理(。。。)

        • corePoolSize:核心池的大小

        • maximumPoolSize:最大线程数

        • keepAliveTime:线程没有任务时最多保持多长时间后会终止

           

          public class TestPool {
              public static void main(String[] args) {
                  //1.创建服务,创建线程池
                  ExecutorService service= Executors.newFixedThreadPool(10);
                  //newFixedThreadPool 参数为:线程池大小
                  //执行
                  service.execute(new MyThread());
                  service.execute(new MyThread());
                  service.execute(new MyThread());
                  service.execute(new MyThread());
                  //2.关闭连接
                  service.shutdown();
              }
          }
          class MyThread implements Runnable{
          
              @Override
              public void run() {
                  System.out.println(Thread.currentThread().getName());
              }
          }

           

  • 相关阅读:
    hibernate 多对多 最佳实践
    世界上速度最快的输入法 Fleksy 为了支持中国
    他们控制的定义-DragButton
    怎么样linux下的目录名的目录,系统用来操作空间
    无形的力量,看得见的手
    如何设置eclipse在默认模式下打开文件
    【比赛组织和共享源代码】那些红卫兵游戏
    [Oracle] 分析功能(1)- 语法
    SAP ABAP第一,两,三代出口型BADI实现 解释的概念
    JSP简单的练习-功能标签
  • 原文地址:https://www.cnblogs.com/fire-dong/p/13414743.html
Copyright © 2011-2022 走看看