多线程技术

线程与进程

进程

  • 是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间。

线程

  • 是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行,一个进程最少有一个线程。
  • 线程实际上是在进程基础上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分为若干个线程。

线程调度

分时调度

  • 所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间。

抢占式调度

  • 优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),java使用的为抢占式调度。
  • CPU使用抢占式调度模式在多个线程间进行着高速的切换,对于CPU的核心而言,某个时刻,只能执行一个线程,而CPU的在多个线程间切换速度相对于我们的感觉要快,看上去就是在同一时刻运行,其实,多线程程序并不能提高程序的运行速度,但是能够提高程序运行效率,让CPU的使用率更高。

同步与异步

同步:排队执行,效率低但是安全。
异步:同时执行,效率高但是不安全。

并发与并行

并发:指两个或多个事件在同一时间内发生。
并行:指两个或多个事件在同一时刻发生(同时发生)。

继承Thread

在java中实现多线程程序

//Thread
pulic class MyThread extends Thread{
        /**
        *run方法就是线程要执行的任务方法
        */
    public void run(){
            //这里的代码,就是一条新的执行路径
            //这个执行路径的触发方式,不是调用run方法,而是通过thread对象的start()来启动任务
        for(int i=0;i<10;i++){
                System.out.println("锄禾日当午"+i);
            }
       }
}
public class Demo{
    public static void main(String[] args){
        MyThread m=new MyThread();
        m.start();
        for(int i=0;i<10;i++){
                System.out.println("汗滴禾下土"+i);
            }
       }
}

每个线程都拥有自己的栈空间,共用一分堆内存。

实现Runnable

实现Runnable与继承Thread相比有如下优势:
1.通过创建任务,然后线程分配的方式来实现的多线程,更适合多个线程同时执行相同任务的情况。
2.可以避免单继承所带来的局限性。
3.任务和线程本身是分离的,提高了程序的健壮性。
4.后续学习的线程池技术,接受Runnable类型的任务,不接受Thread类型的线程。

/**
*用于给线程进行执行的任务
*/
public class MyRunnable implements Runnable{
    @Override
    public void run(){
        //线程的任务
        for(int i=0;i<10;i++){
            System.out.println("床前明月光"+i);
        }
    }
}
 //匿名内部类
    //此方法也是继承Thread
    new Thread(){
        @Override
        public void run(){
            for(int i=0;i<10;i++){
                System.out.println("一二三四五"+i);
            }
        }
    }.start();
    for(int i=0;i<10;i++){
        System.out.println("六七八九十"+i);
    }
public static void main(String[] args){
    //实现Rnnable
    //1.创建一个任务对象
    MyRunnable r=new MyRunnable();
    //2.创建一个线程,并为其分配一个任务
    Thread t=new Thread();
    //3.执行这个线程
    t.start();
    for(int i=0;i<10;i++){
        System.out.println("疑是地上霜"+i);
    }
}

Thread类

stop方法已过时,不能使用,是不安全的。

字段
在这里插入图片描述
构造方法
在这里插入图片描述
方法
在这里插入图片描述

设置和获取线程名称

public static void main(String[] args){
    System.out.println( Thread.currentThread().getName());
    new Thread(new MyRunnable()).start();
    new Thread(new MyRunnable()).start();
    new Thread(new MyRunnable()).start();
    
    //也可以创建Thread对象进行启动,设置名称
    Thread t=new Thread(new MyRunnable());
    t.start();
    t.setName();
}




static class MyRunnable implements Runnable(){
    @Override
    public void run(){
        //获取当前线程
        Thread.currentThread().getName();
    }
}

线程休眠sleep

//需要处理异常
for(int i=0;i<10;i++){
    System.out.println(i);
    Thread.sleep(1000);
}

线程阻塞

所有比较消耗时间的操作,如文件读取。

线程的中断

一个线程是一个独立的执行路径,它是否应该结束,应该由其自身决定。

public static void main(String[] args){
     Thread t1=new Thread(new MyRunnable()).start();
    t1.start();
     for(int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
           try{
            Thread.sleep(1000);
           }catch(InterruptedException e){
               e.printStackTrace();
           }
        }
    //给线程t1添加中断标记
    t1.interrupt();
}


static class MyRunnable implements Runnable(){
    @Override
    public void run(){
       for(int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
           try{
            Thread.sleep(1000);
           }catch(InterruptedException e){
              // e.printStackTrace();
               System.out.println("发现了中断标记,但是我们不死亡");
               
               //自己return表示返回方法,结束线程
               System.out.println("发现中断标记,我们自杀");
               return;
           }
        }
    }
}

守护线程

线程:分为守护线程和用户线程。
用户线程:当一个进程不包括任何的存活的用户线程时,进行结束。
守护线程:守护用户线程的,当最后一个用户线程结束时,所有守护线程自动死亡。
注:直接创建的线程时用户线程

public static void main(String[] args){
     Thread t1=new Thread(new MyRunnable()).start();
    //设置为守护线程
    t1.setDaemon(true);
    t1.start();
    
     for(int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
           try{
            Thread.sleep(1000);
           }catch(InterruptedException e){
               e.printStackTrace();
           }
        }
    //给线程t1添加中断标记
    t1.interrupt();
}

线程安全问题

线程不安全,例如:买票

//线程不安全
public static void main(String[] args){
    Runnable run=new Ticket();
    new Thread(run).start();
    new Thread(run).start();
    new Thread(run).start();
}




static class Ticket implements Runnable(){
    private int count=10;
    @Override
    public void run(){
        while(count>0){
            //卖票
            System.out.println("正在准备卖票");
            try{
            Thread.sleep(1000);
           }catch(InterruptedException e){
               e.printStackTrace();
           }
            count--;
            System.out.println("出票成功,余票:"+count);
            //运行结果会出现-1,-2的情况
        }
    }
}

同步代码块和同步方法都属于隐式锁。

线程安全1-同步代码块

格式: synchronized(锁对象){
}

public static void main(String[] args){
    Runnable run=new Ticket();
    new Thread(run).start();
    new Thread(run).start();
    new Thread(run).start();
}




static class Ticket implements Runnable(){
    private int count=10;
    private Object o=new Object();
    @Override
    public void run(){
        //Object o=new Object();要看同一把锁
        while(true){
            synchronized(o){
            if(count>0){
            //卖票
            System.out.println("正在准备卖票");
            try{
            Thread.sleep(1000);
           }catch(InterruptedException e){
               e.printStackTrace();
           }
            count--;
            System.out.println(Thread.currentThread().getName+"出票成功,余票:"+count);
           }else{
                break;
            }
        }
      }
   }
}

线程安全2-同步方法

public static void main(String[] args){
    Runnable run=new Ticket();
    new Thread(run).start();
    new Thread(run).start();
    new Thread(run).start();
}
static class Ticket implements Runnable(){
    private int count=10;
    private Object o=new Object();
    @Override
    public void run(){
        while(true){
            boolean flag=sale();
            if(!flag){
                break;
            }
   }
}


public synchronized void sale(){
    if(count>0){
            //卖票
            System.out.println("正在准备卖票");
            try{
            Thread.sleep(1000);
           }catch(InterruptedException e){
               e.printStackTrace();
           }
            count--;
            System.out.println(Thread.currentThread().getName+"出票成功,余票:"+count);
                return true;
           }else{
                return false;
            }
}

线程安全3-显示锁Lock

public static void main(String[] args){
    Runnable run=new Ticket();
    new Thread(run).start();
    new Thread(run).start();
    new Thread(run).start();
}




static class Ticket implements Runnable(){
    private int count=10;
    //显示锁 l
    private Lock l=new ReentrantLock();
    @Override
    public void run(){
       while(true){
           l.lock();
            if(count>0){
            //卖票
            System.out.println("正在准备卖票");
            try{
            Thread.sleep(1000);
           }catch(InterruptedException e){
               e.printStackTrace();
           }
            count--;
            System.out.println(Thread.currentThread().getName+"出票成功,余票:"+count);
           }else{
                break;
            }
           l.unlock();
       }
    }
}

显示锁和隐式锁的区别:
一、层面不同
synchronized:Java中的关键字,是由JVM来维护的,是JVM层面的锁。

  • synchronized底层是通过monitorenter进行加锁
    底层是通过monitor对象来完成的,其中的wait/notify等方法也是依赖于monitor对象的。并且只有在同步块或同步方法中,JVM才会调用monitory对象的,才可以调用wait/notify等方)

  • 通过monitorexit来退出锁
    Lock:是JDK5以后才出现的具体的类。使用lock是调用对应的API,是API层面的锁。

  • lock是通过调用对应的API方法来获取锁和释放锁的。

二、使用方式不同

  • synchronized
    程序能够自动获取锁和释放锁。Sync是由系统维护的,如果非逻辑问题的话话,不会出现死锁。
  • Lock
    需要手动的获取和释放锁。如果没有释放锁,就有可能导致出现死锁的现象。
    手动获取锁方法:lock.lock()。释放锁:unlock方法。并且需要配合tyr/finaly语句块来完成。

三、等待是否可中断

  • synchronized
    不可中断,除非抛出异常或者正常运行完成。
  • Lock
    可以中断的。

中断方式

  • 调用设置超时方法tryLock(long timeout ,timeUnit unit)
  • 调用lockInterruptibly()放到代码块中,然后调用interrupt()方法可以中断

四、加锁的时候是否可以设置成公平锁

  • synchronized
    只能为非公平锁。
  • lock:两者都可以的。默认是非公平锁
    在其构造方法的时候可以传入Boolean值。true:公平锁、false:非公平锁

五、锁绑定多个条件来condition

  • synchronized
    不能精确唤醒线程。要么随机唤醒一个线程;要么是唤醒所有等待的线程。
  • Lock
    用来实现分组唤醒需要唤醒的线程,可以精确的唤醒。

六、性能区别

  • synchronized
    托管给JVM执行,Java1.5中,由于需要调用操作接口,可能导致加锁消耗时间过长,与Lock性比性能低。1.6以后,语义定义更加清晰,有适应自旋、锁粗化、锁消除、轻量级锁、偏向锁等,可进行许多优化,性能提高了,与Lock差不多。
  • Lock
    java写的控制锁的代码,性能高。

公平锁和非公平锁

公平锁:当线程锁解开时,先来先执行,有一个排队的流程。
非公平锁:当线程锁解开时,一块抢占。

//公平锁
//显示锁 l:fair参数为true,就表示是公平锁


private Lock l=new ReentrantLock(true);

线程死锁

public static void main(String[] args){
    Culprit c=new Culprit();
    Polic p=new Polic();
    new MyThread(c,p).start();
    
    c.say(p);
    
}


static class MyThread extends Thread{
    private Culprit c;
    private Police p;
    public MyThread(Culprit c,Police p){
        this.c=c;
        this.p=p;
    }
    @Override
    public void say(){
        p.say(c);
    }
    
}


//罪犯
static class Culprit{
    public synchronized void say(Polic p){
        System.out.println("罪犯:你放了我,我放人质");
        p.fun();
    }
    public synchronized void fun()[
        System.out.println("罪犯被放走了,罪犯也放了人质");
    ]
}
//警察
static class Polic{
    public synchronized void say(Culprit c){
        System.out.println("警察:你放了人质,我就放过你");
        c.fun();
    }
    public synchronized void fun(){
        System.out.print("警察救了人质,但是罪犯跑了");
    }
}






//运行结果
//罪犯:你放了我,我放人质
//警察:你放了人质,我就放过你

多线程通信问题

多线程通信问题,生产者与消费者问题

生产者与消费者

public static void main(String[] args){
    Food f=new Food();
    new Cook(f).start();
    new Waiter(f).start();
}
/**
*会出现的问题:
*线程紊乱 本应该是:
*        服务员端走的菜的名称是:老干妈小米粥,味道是:甜辣味
*        但是实际出来的是:
*        服务员端走的菜的名称是:老干妈小米粥,味道是:甜辣味
*原因:厨师刚设置好set方法,名称刚设置好,服务员就进行了get方法端菜,导致不协调的问题
*/




//厨师
static class Cook extends Thread{
    private Foood();
    public Cook(Food f){
        this.f=f;
    }
    @Overrride
    public void run(){
        for(int i=0;i<100;i++){
            if(i%2==0){
                f.setNameAndTaste("老干妈小米粥","香辣味");
            }else{
                f.setNameAndTaste("煎饼果子","甜辣味");
            }
        }
    }
}
//服务生
ststic class Waiter extends Thread{
    private Food();
    public Waiter(Food f){
        this.f=f;
    }
    @Override
    public void run(){
        for(int i=0;i<100;i++){
            try{
                Thread.sleep(100);
            }catch(InterruptException e){
                e.printStackTrace();
            }
            f.get();
        }
    }
}
//食物
static class Food {
    private String name;
    private String taste;
    
    public void SetNameAndTaste(String name,String taste){
        this.name=name;
        try{
            Thread.sleep(100);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        this.taste=taste;
    }
    private void get(){
        System.out.println("服务员端走的菜是:"+name+"味道是:"+taste);
    }
}


/**
*如果使用synchronized 排队来解决问题,会出现一个更乱的问题
*当厨师制作完一道菜时,锁解开,但是注意这个锁不是公平锁,厨师也会抢占,又开始做饭
*会发现有些菜没有被端走
*
*/




//厨师
static class Cook extends Thread{
    private Foood();
    public Cook(Food f){
        this.f=f;
    }
    @Overrride
    public void run(){
        for(int i=0;i<100;i++){
            if(i%2==0){
                f.setNameAndTaste("老干妈小米粥","香辣味");
            }else{
                f.setNameAndTaste("煎饼果子","甜辣味");
            }
        }
    }
}
//服务生
ststic class Waiter extends Thread{
    private Food();
    public Waiter(Food f){
        this.f=f;
    }
    @Override
    public void run(){
        for(int i=0;i<100;i++){
            try{
                Thread.sleep(100);
            }catch(InterruptException e){
                e.printStackTrace();
            }
            f.get();
        }
    }
}
//食物
static class Food {
    private String name;
    private String taste;
    
    public synchronized  void SetNameAndTaste(String name,String taste){
        this.name=name;
        try{
            Thread.sleep(100);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        this.taste=taste;
    }
    private synchronized  void get(){
        System.out.println("服务员端走的菜是:"+name+"味道是:"+taste);
    }
}




/**
*解决方法:当厨师做菜时,服务员睡着,做完饭唤醒服务员上菜;当服务员上菜,厨师睡着,服务员上完菜,唤醒厨师做饭
*
*/




//厨师
static class Cook extends Thread{
    private Foood();
    public Cook(Food f){
        this.f=f;
    }
    @Overrride
    public void run(){
        for(int i=0;i<100;i++){
            if(i%2==0){
                f.setNameAndTaste("老干妈小米粥","香辣味");
            }else{
                f.setNameAndTaste("煎饼果子","甜辣味");
            }
        }
    }
}
//服务生
ststic class Waiter extends Thread{
    private Food();
    public Waiter(Food f){
        this.f=f;
    }
    @Override
    public void run(){
        for(int i=0;i<100;i++){
            try{
                Thread.sleep(100);
            }catch(InterruptException e){
                e.printStackTrace();
            }
            f.get();
        }
    }
}
//食物
static class Food {
    private String name;
    private String taste;
    //true 表示可以生产
    private boolean flag=true;
    
    public synchronized  void SetNameAndTaste(String name,String taste){
        if(flag){
            this.name=name;
            try{
                Thread.sleep(100);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            this.taste=taste;
            flag=false;
            this.notifyAll();
            try{
                this.wait();
            }catch(InterruptException e){
                e.printStackTrace;
            }
        }
    }
    private synchronized  void get(){
        if(!flag){
            System.out.println("服务员端走的菜是:"+name+"味道是:"+taste);
            flag=true;
            this.noifyAll();
            try{
                this.wait();
            }catch(InterruptException e){
                e.printStackTrace();
            }
        }
    }
}

线程的六种状态

线程状态

  • NEW 尚未启动的线程处于此状态
  • RUNNABLE 在Java虚拟机中执行的线程处于此状态
  • BLOCKED 被阻塞等待监视器锁定的线程处于此状态
  • WAITING 无限期等待另一个线程执行特定操作的线程处于此状态
  • TIMED_WAITING 正在等待另一个线程执行最多等待时间的操作的线程出于此状态
  • TERMINATED 已退出的线程处于此状态

状态转换
在这里插入图片描述

带返回值的线程Callable

Runnable 与 Callab

//接口定义
//Callable接口
public interface Callable<V> {
V call() throws Exception;
}
//Runnable接口
public interface Runnable {
public abstract void run();
}

Callable使用

//1. 编写类实现Callable接口 , 实现call方法
class XXX implements Callable<T> {
@Override
public <T> call() throws Exception {
return T;
       }
}
//2. 创建FutureTask对象 , 并传入第一步编写的Callable类对象
FutureTask<Integer> future = new FutureTask<>(callable);
//3. 通过Thread,启动线程
new Thread(future).start();

Runnable 与 Callable的相同点

  • 都是接口
  • 都可以编写多线程程序
  • 都采用Thread.start()启动线程

Runnable 与 Callable的不同点

  • Runnable没有返回值;Callable可以返回执行结果
  • Callable接口的call()允许抛出异常;Runnable的run()不能抛出

Callable获取返回值
Callalble接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。

public static void main()extends ExcutionException,InterruptedException{
   Callable<Integer> c=new Callable();
    FutureTask<Integer> task=new FutureTake<>(c);
     //task.isDone() 判断这个线程的任务是否执行完毕
    //task.cancel() 取消任务
    new Thread(task).start();
    //如果不调用get方法会一直执行下去
    Integer j=task.get();
    System.out.println(j);
     for(int i=0;i<10;i++){
          try{
                Thread.sleep(100);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
         System.out.println(i);
    }
}




static class MyCallable implement Callable<Integer>{
     @Override
     public Integer call() throws Exception{
         //Thread.sleep(3000);
         for(int i=0;i<10;i++){
             try{
                Thread.sleep(100);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
             System.out.println(i);
         }
         return 100;
     }
}

线程池概述

线程流程

  • 创建流程
  • 创建任务
  • 执行任务
  • 关闭流程

线程池 Executors
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低 系统的效率,因为频繁创建线程和销毁线程需要时间. 线程池就是一个容纳多个线程的容器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。

线程池的好处

  • 降低资源消耗。
  • 提高响应速度。
  • 提高线程的可管理性。

Java中的四种线程池 . ExecutorService
缓存线程池
(长度无限制)
执行流程:
1. 判断线程池是否存在空闲线程
2. 存在则使用
3. 不存在,则创建线程 并放入线程池, 然后使用

/**
*缓存线程池
*(长度无限制)
*任务加入后的执行流程:
*        1.判断线程池是否存在空闲空闲流程
*        2.存在则使用
*        3.不存在,则创建线程并放入线程池,然后使用
*/


public static void main(String[] args){
    ExecutorService service=Executors.newFixedThreadPool();
    //指挥线程池中执行新的任务
    service.execute(new Runnable(){
        @Override
        public void run(){
            System.out.println(Thread.currectThread().getName()+"锄禾日当午");
        }
    });
    service.execute(new Runnable(){
        @Override
        public void run(){
            System.out.println(Thread.currectThread().getName()+"锄禾日当午");
        }
    });
    service.execute(new Runnable(){
        @Override
        public void run(){
            System.out.println(Thread.currectThread().getName()+"锄禾日当午");
        }
    });
    try{
           Thread.sleep(100);
      }catch(InterruptedException e){
            e.printStackTrace();
      }
     service.execute(new Runnable(){
        @Override
        public void run(){
            System.out.println(Thread.currectThread().getName()+"锄禾日当午");
        }
    });
}

定长线程池
(长度是指定的数值)
执行流程:
1. 判断线程池是否存在空闲线程
2. 存在则使用
3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程

/**
*定长线程池
*(长度是指定的数值)
*任务加入后的执行流程:
*        1.判断线程池是否存在空闲线程
*        2.存在则使用
*        3.不存在空闲线程,且线程池未满的情况下,则创建线程并放入线程池,然后使用
*        4.不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
*/
public static void main(String[] args){
    ExecutorService service=Executors.newCachedThreadPool(2);
    service.execute(new Runnable(){
        @Override
        public void run(){
            System.out.println(Thread.currectThread().getName()+"锄禾日当午");
            try{
                   Thread.sleep(100);
              }catch(InterruptedException e){
                e.printStackTrace();
              }


        }
    });
    service.execute(new Runnable(){
        @Override
        public void run(){
            System.out.println(Thread.currectThread().getName()+"锄禾日当午");
            try{
                   Thread.sleep(100);
              }catch(InterruptedException e){
                e.printStackTrace();
              }


        }
    });
    service.execute(new Runnable(){
        @Override
        public void run(){
            System.out.println(Thread.currectThread().getName()+"锄禾日当午");
        }
    });
}

单线程线程池
效果与定长线程池 创建时传入数值1 效果一致.
执行流程:
1. 判断线程池 的那个线程 是否空闲
2. 空闲则使用
3. 不空闲,则等待 池中的单个线程空闲后 使用

/**
*定长线程池
*(长度是指定的数值)
*任务加入后的执行流程:
*        1.判断线程池是否存在空闲线程
*        2.存在则使用
*        3.不存在空闲线程,且线程池未满的情况下,则创建线程并放入线程池,然后使用
*        4.不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
*/
public static void main(String[] args){
    ExecutorService service=Executors.newCachedThreadPool(2);
    service.execute(new Runnable(){
        @Override
        public void run(){
            System.out.println(Thread.currectThread().getName()+"锄禾日当午");
            try{
                   Thread.sleep(100);
              }catch(InterruptedException e){
                e.printStackTrace();
              }


        }
    });
    service.execute(new Runnable(){
        @Override
        public void run(){
            System.out.println(Thread.currectThread().getName()+"锄禾日当午");
            try{
                   Thread.sleep(100);
              }catch(InterruptedException e){
                e.printStackTrace();
              }


        }
    });
    service.execute(new Runnable(){
        @Override
        public void run(){
            System.out.println(Thread.currectThread().getName()+"锄禾日当午");
        }
    });
}

周期性任务定长线程池
执行流程:
1. 判断线程池是否存在空闲线程
2. 存在则使用
3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
周期性任务执行时:
定时执行, 当某个时机触发时, 自动执行某任务 。

/**
*周期任务 定长线程池
*执行流程:
*        1.判断线程池是否存在空闲线程
*        2.存在则使用
*        3.不存在空闲线程,且线程池未满的情况下,则创建线程并放入线程池,然后使用
*        4.不存在空闲线程,且线程池已满的情况下,则等待线程池存入空闲线程
*
*周期性任务执行时:
*        定时任务,当某个时机触发时,自动执行某任务。
*/
public static void main(String[] args){
    SchedulExecutorService service=Executors.newCachedThreadPool(2);
    /**
    *定时执行一次
    *参数1:定时执行的任务
    *参数2:时长数字
    *参数3:时长数字的时间单位,TimeUnit的常量指定


    service.schedule(new Runnable(){
        @Override
        public void run(){
           System.out.println("锄禾日当午");   
        }
    },5,TimeUnit.SECONDS);    */
    
    
    /**
    *周期性执行任务
    *参数1:任务
    *参数2:延迟时长数字(第一次执行在什么时间以后)
    *参数3:周期时长数字(每隔多久执行一次)
    *参数4:时长数字单位
    */
    service.scheduleAtFixedRate(new Runnable(){
        @Override
        public void run(){
           System.out.println("汗滴禾下土");  
    },5,1,TimeUnit.SECONDS);
    
}

Lambda表达式

函数式编程思想
面向对象:创建对象对用方法解决方法。

public static void main(String[] args){
    //冗余的Runnable代码
   /*MyRunnable r=new MyRunnable();
    Thread t=new Thread(r);
    t.start();
    
    
    //匿名内部类
    Thread t=new Thread(new Runnable()){
       @Override
        public void run(){
            System.out.println("锄禾日当午");
        }
    });
    t.start();
    */
    
    //Lambda表达式
    Thread t=new Thread(()-> {
        System.out.println("锄禾日当午")
    });
    t.start();
}


static class MyRunnable implements Runnable{
    @Override
    public void run(){
        System.out.println)("锄禾日当午");
    }
}
public static void mian(String[] args){
    print(new MyMath(){
        @Override
        public int sum(int x,int y){
            return x+y;
        }
    },200,300);
    
    print((int x,int y)->{
        return x+y;
    },200,300);
}


public static void print(MyMath m,int x,int y){
    int num=m.sum(x,y);
    System.out.println(num);
}


static interface MyMath{
    int sum(int x,int y);
}

注:

显示锁和隐式锁的区别参考自:日常自闭的HXH

链接:https://blog.csdn.net/qq_43570075/article/details/106243873

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值