多线程和Lambda表达式

今天的主要内容——线程
线程

线程的基本概念

线程与进程的区别
线程的两种创建方式(掌握)

注意线程两种创建方式的区别

内部类实现线程创建

线程的第三种创建方式——实现Callable接口

Thread类中的方法使用

getName()
setName()
currentthread()
sleep
join
wait
yield
setDaemon
线程的同步(掌握)

同步代码块

同步方法

多线程(线程安全问题)(掌握)

线程的死锁(了解)

多线程和队列实现买卖票(掌握)

Lambda表达式

lambda表达式的几种方式
lambda表达式用途
线程

  1. 线程的基本概念
    在这里插入图片描述

1.什么是线程

线程是程序执行的一条路径, 一个进程中可以包含多条线程
多线程并发执行可以提高程序的效率, 可以同时完成多项工作
进程:其实是一个静态的概念

  • 我们平时所说的进程开始执行,指的是:进程中主线程开始执行了,即main方法开始执行了

计算机中实际运行的都是线程。

操作系统是支持多线程,多进程的,系统是同时执行多个线程的

CPU的执行:交替执行众多线程,因为速度快,表面上是同时执行多个线程,其实不是的。

注:
① Java的线程是通过java.lang.Thread类来实现的

② VM启动时会有一个主方法(main方法)所定义的线程

③ 可通过创建Thread的实例来创建新的线程

④ 每个线程都是通过某个特定的Thread对象所对应的run方法来完成其操作的,方法run()称为线程体

⑤ 通过调用Thread类中的start()方法来启动一个线程。

2. 多线程(多线程并行和并发的区别)(了解)

并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也在进行。(需要多核CPU)
并发是指两个任务都请求运行,而处理器只能按受一个任务,就把这两个任务安排轮流进行,由于时间间隔较短,使人感觉两个任务都在运行。
比如我跟两个网友聊天,左手操作一个电脑跟甲聊,同时右手用另一台电脑跟乙聊天,这就叫并行。
如果用一台电脑我先给甲发个消息,然后立刻再给乙发消息,然后再跟甲聊,再跟乙聊。这就叫并发。
3. 多线程(Java程序运行原理和JVM的启动是多线程的吗)(了解)
A:Java程序运行原理

Java命令会启动java虚拟机,启动JVM,等于启动了一个应用程序,也就是启动了一个进程。该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法。
B:JVM的启动是多线程的吗

JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的。

运行程序证明JVM的多线程

  public class TestJVMMultiThread {   
      public static void main(String[] args) {
          //创建垃圾对象观察垃圾回收线程
          for(int i = 0;i < 100;i++) {
              new Test();     
          }
          
          for(int i = 0;i < 100;i++) {
              System.out.println("主线程在执行");   
          }       
      }   
  }
  
  class Test{
      
      @Override
      public void finalize(){
          System.out.println("垃圾回收线程在执行");
      }   
  }

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

3. 线程两种创建方式程序

创建线程程序1:

在这里插入图片描述
在这里插入图片描述
用lambda表达式简写线程启动过程

  public class TestLambda {
      public static void main(String[] args) {
          
          System.out.println("哈哈哈哈我在测试啊");
          
          Runnable runner = ()->{
              StringBuffer s = new StringBuffer();
              
              for(int i= 0;i < 10;i++)
                  System.out.println(s.append("haha"));
          };
          
          new Thread(runner).start();  //注意:同一个线程不可以启动多次
          
          System.out.println("任务完成");
      }   
  }
  /*
   *  在JDK1.8中输出结果为:
   *  ------------------------------------------
   *  哈哈哈哈我在测试啊
      任务完成
      haha
      hahahaha
      hahahahahaha
      hahahahahahahaha
      hahahahahahahahahaha
      hahahahahahahahahahahaha
      hahahahahahahahahahahahahaha
      hahahahahahahahahahahahahahahaha
      hahahahahahahahahahahahahahahahahaha
      hahahahahahahahahahahahahahahahahahahaha
      ------------------------------------------
   * */

创建线程程序2:

在这里插入图片描述
此时Runner线程和main线程交替运行

* 能使用接口实现线程就不要用继承,因为用了接口以后还可以继承和实现其它接口,而如果只继承会比较死板

4. 创建线程的两种方式的区别(掌握)

查看源码的区别:

  • a.继承Thread : 由于子类重写了Thread类的run(), 当调用start()时, 直接找子类的run()方法
  • b.实现Runnable : 构造函数中传入了Runnable的引用, 成员变量记住了它,
    start()调用run()方法时内部判断成员变量Runnable的引用是否为空,
    不为空编译时看的是Runnable的run(),运行时执行的是子类的run()方法

Thread类中的run()源码为:

    public void run() {
        if (target != null) {
            target.run();
        }
    }

5. 匿名内部类实现线程

  • 继承Thread类
  new Thread() {                                 //1.继承Thread类
          public void run(){                      //2.重写run方法
              for(int i = 0;i < 1000;i++) {
                  System.out.println("aaaaaa");
              }
          }
      }.start();                                  //3.开启线程
  • 实现Runnable接口
  new Thread(new Runnable() {                 //1.将Runnable子类对象传递给Thread构造方法
      public void run(){                      //2.重写run方法
          for(int i = 0;i < 1000;i++) {               
              System.out.println("bb");
          }
      }           
  }).start();                                 //3.开启线程

6. 创建线程的两种方式的区别(掌握)

  • 继承Thread
  • 好处是:可以直接使用Thread类中的方法,代码简单
 * 弊端是:如果已经有了父类,就不能用这种方法
  • 实现Runnable接口
  • 好处是:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口,而且接口是可以多实现的
* 弊端是:不能直接使用Thread中的方法需要先获取到线程对象后,才能得到Thread的方法,代码复杂

7. 线程的第三种创建方式

  • 使用Callable接口_java.util.concurrent
    在这里插入图片描述
  • Callable接口与Runnable接口的区别:
  • Runnable没有返回值一说,而且run()方法并不抛出异常
  • Callable中的call方法具有返回值
  • Callable之所以有返回值,也是因为实现了泛型,而Runnable接口不存在泛型

7.1 FutureTask类的学习——java.util.concurrent.FutureTask

在这里插入图片描述

  • FutureTask的构造器
    在这里插入图片描述
  • 此时可理解为:FutureTask的作用就是接收一个实现了Callable接口的实现类对象
  • 此时注意到FutureTask实现了Runnable接口
  • 也就是说可以把FutureTask的实现类对象传入到Runnable类型
  • 而Thread类中的构造器的参数为Runnable类型
  • 因此梳理后可知
    ①先实现Callable接口
  class myThread_3 implements Callable<String>{
      @Override
      public String call() throws Exception {
          return "线程创建成功";
      }
  }

②将Callable实现类对象传给FutureTask构造器

  FutureTask<String> ft = new FutureTask<>(new myThread_3());

  * 因为FutureTask实现了Runnable接口,因此以下代码也正确

      Runnable runner = new FutureTask<String>(new myThread_3());
      * 接口引用指向实现类对象
      * 但是此时runner只能访问Runnable中方法,不可以访问FutureTask中的方法
      * 
②将Callable实现类对象传给FutureTask构造器
  FutureTask<String> ft = new FutureTask<>(new myThread_3());

 * 因为FutureTask实现了Runnable接口,因此以下代码也正确

      Runnable runner = new FutureTask<String>(new myThread_3());
      * 接口引用指向实现类对象
      * 但是此时runner只能访问Runnable中方法,不可以访问FutureTask中的方法
  • ③创建线程并启动
  new Thread(ft).start();

④使用FutureTask类中方法

  public V get() throws InterruptedException,ExecutionException

  通过Thread启动线程之后 可以通过FutureTask存在get方法可以取得call()方法中的返回值

  /*用到Callable有什么用?只是为了返回一个值?执行线程的代码run用什么代替?*/
  /*回答:此时线程启动start()方法调用的是call()方法,将需要执行的代码放到call()方法中,同时call()方法的特点在于有返回值*/
  • 程序
  public class TestCallable{
      public static void main(String[] args) {
          FutureTask<String> ft = new FutureTask<String>(new myThread_3());
          
          new Thread(ft).start();  //此时的启动需要调用的是call()方法      
          String result = null;
  
          try {
              result = ft.get();
          } catch (InterruptedException | ExecutionException e) {
              e.printStackTrace();
          }       
          System.out.println(result);     
          
      }   
  }
  
  class myThread_3 implements Callable<String>{
  
      @Override   
      public String call() throws Exception {     
          System.out.println("此时start()方法调用的是call()方法");
          return "线程创建成功";
      }
  }

  /*  在JDK1.8中输出结果为:
   *  ----------------------------
   *  此时start()方法调用的是call()方法
      线程创建成功
      ----------------------------
   * */

8. 多线程(获取名字和设置名字)(掌握)

  • 获取名字
  • 通过getName()方法获取线程对象的名字
  public final String getName();
  • 设置名字
  • 通过构造函数可以传入String类型的名字
  public Thread(String name)

  public Thread(Runnable target,String name)  

通过setName(String)方法可以设置线程对象的名字

  public final void setName(String name);
  • 程序实现
  new Thread("线程一") {             //设置线程名字
          public void run() {
              for(int i = 0;i < 100;i++) {
                  System.out.println(this.getName() + "....aaaa");   //获取线程名字
              }
          }
      }.start();
  
  new Thread("线程二") {                 //设置线程名字
      public void run() {
          for(int i = 0;i < 100;i++) {
              System.out.println(this.getName() + "....bb");
          }
      }
  }.start();
  
  new Thread() {
      public void run() {
          this.setName("线程3");       //设置线程名字
          
          for(int i = 0;i < 100;i++) {
              System.out.println(this.getName() + "....哈哈");
          }
      }
  }.start();

9. 多线程(获取当前线程的对象)(掌握)

public static Thread currentThread()

Thread.currentthread();——注意返回值是Thread

//匿名内部类实现当前线程对象的获取
new Thread() {          
    public void run() {
        this.setName("线程A");
        
        for(int i = 0;i < 1000;i++) {
            System.out.println( Thread.currentThread().getName() + "....aaaa");
        }
    }
    
}.start();

new Thread(new Runnable() {
    public void run() {
        for(int i = 0;i < 1000;i++) {
            System.out.println( Thread.currentThread().getName() + "....bb");
        }
    }
},"线程B").start();       

10. 多线程(休眠线程)(掌握)

 - public static void sleep(long millis) throws InterruptedException
 - 抛出InterruptionException
        public class TestSleepOfThread {
            public static void main(String[] args) {
                
                Runnable r = ()->{
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    
                    System.out.println("Thread A");
                };
                
                new Thread(r).start();
                
                System.out.println("main Thread");
            }
        }
        /*
         * 在JDK1.8中输出结果为:
         * -------------
         * main Thread
           Thread A
           -------------
         * */

11. 多线程(守护线程)(掌握)

  • setDaemon(), 设置一个线程为守护线程, 该线程不会单独执行, 当其他非守护线程都执行结束后, 自动退出
  • public final void setDaemon(boolean on)
  • on - if true, marks this thread as a daemon thread
   public class TestSetDaemon {
      public static void main(String[] args) {
          Thread t1 = new Thread("线程A") {
              public void run() {
                  for(int i = 0;i < 2;i++) {
                      System.out.println(Thread.currentThread().getName() + "....aaaa");
                  }
              }
          };
          
          Thread t2 = new Thread(new Runnable() {
              public void run() {
                  for(int i = 0;i < 50;i++) {
                      System.out.println(Thread.currentThread().getName() + "....bb");
                  }
              }
          },"线程B");
          
          t2.setDaemon(true);          //将t2设置为守护线程
          
          t1.start();
          t2.start();
          
      }
  }
  /*
   *  在JDK1.8中输出结果为:
   *  ------------------------
   *  线程A....aaaa
      线程B....bb
      线程A....aaaa
      线程B....bb      //存在时间缓冲问题
      线程B....bb
      线程B....bb
      线程B....bb
      线程B....bb
      线程B....bb
      线程B....bb
   * */

12. 多线程(加入线程)(掌握)

  • join(), 当前线程暂停, 等待指定的线程执行结束后, 当前线程再继续
  • join(int), 可以等待指定的毫秒之后继续
  • public final void join() throws InterruptedException
  public class TestJoin {
      public static void main(String[] args) {
          
          Thread t1 = new Thread("线程A") {
              public void run() {
                  for(int i = 0;i < 100;i++) {
                      System.out.println(getName() + "....aaaaaaa");
                  }
              }
          };
          
          Runnable r = ()->{
              for(int i = 0;i < 50;i++) {
                  
                  if(i == 2) {
                      try {
                          t1.join();
                          //t1.join(1000); t1插队执行1秒后,t1和t2线程再抢占CPU交替执行
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }               
                  
                  System.out.println(Thread.currentThread().getName() + "....bb");
              }
          };
          
          t1.start();
          new Thread(r).start();
      }
  }

13. 多线程(礼让线程)(了解)

  • yield让出cpu

14. 多线程(设置线程的优先级)(了解)

  • setPriority()设置线程的优先级

15. 多线程(同步代码块)(掌握)

  • 1.什么情况下需要同步
  • 当多线程并发, 有多段代码同时执行时, 我们希望某一段代码执行的过程中CPU不要切换到其他线程工作. 这时就需要同步.
  • 如果两段代码是同步的, 那么同一时间只能执行一段, 在一段代码没执行结束之前, 不会执行另外一段代码.
  • 2.同步代码块
  • 使用synchronized关键字加上一个锁对象来定义一段代码, 这就叫同步代码块
  • 锁对象不可以用匿名对象,因为匿名对象不是同一个对象
  • 多个同步代码块如果使用相同的锁对象, 那么他们就是同步的
  public class TestTickets_2 {
      public static void main(String[] args) {
          MyThread_6 myt = new MyThread_6();
          
          new Thread(myt,"线程A").start();
          new Thread(myt,"线程B").start();
      }
  }
  
  class MyThread_6 implements Runnable{
  
      private int ticket = 5;
      
      @Override
      public void run() {
              while(true) {
                  if(this.ticket > 0) {
                      System.out.println(Thread.currentThread().getName() + "开始卖票 = " + ticket--);
                  }
                  else {
                      System.out.println("票已经卖完");
                      break;
                  }
              }   
          }   
  }

16. 多线程(同步方法)(掌握)

  • public class TestSynchronized_2 {

    public static void main(String[] args) {
        Printer_2 p = new Printer_2();
        
        Runnable r = ()->{
            for(int i = 0;i < 100;i++) {
                p.print1();
            }
        };
        
        Thread t2 = new Thread() {
            public void run() {
                for(int i = 0;i < 100;i++) {
                    p.print2();
                }
            }
        };
        
        new Thread(r).start();
        t2.start();     
    } }
    

    class Printer_2{
    public synchronized void print1() {
    System.out.print(“早”);
    System.out.print(“上”);
    System.out.print(“好”);
    System.out.print(“啊”);
    System.out.print("\r\n");
    }

    public void print2() {
        synchronized(this) {
            System.out.print("河");
            System.out.print("正");
            System.out.print("宇");
            System.out.print("好");
            System.out.print("帅");  
            System.out.print("\r\n");
        }       
    }    }
    

17. 多线程(线程安全问题)(掌握)

  • 多线程并发操作同一数据时, 就有可能出现线程安全问题
  • 使用同步技术可以解决这种问题, 把操作数据的代码进行同步, 不要多个线程一起操作
    /*
    • 铁路售票,一共100张,通过四个窗口卖完
    • */
  public class TestSynchronized_3 {
      public static void main(String[] args) {
          new Ticket().start();
          new Ticket().start();
          new Ticket().start();
      }   
  }
  
  class Ticket extends Thread{
      private static int ticket = 100;
      
      public void run() {
          while(true) {
              //synchronized(this)
              synchronized(Ticket.class) {
                  if(ticket == 0) {
                      break;
                  }
                  
                  System.out.println(getName() + "这是第" + ticket-- + "号票");
              }
          }
      }
  }

18. 多线程(火车站卖票的例子用实现Runnable接口)(掌握)

public class TestSynchronized_4 {
    public static void main(String[] args) {
        Ticket_2 t = new Ticket_2();
        
        new Thread(t,"线程A").start();
        new Thread(t,"线程B").start();
        new Thread(t,"线程C").start();
        new Thread(t,"线程D").start();
    }
}

class Ticket_2 implements Runnable{
    private int ticket = 100;  //此时ticket不需要设置为共享,因为均为同一对象
    
    @Override
    public void run() {
        while(true) {
            synchronized(this) {
                if(ticket == 0) {
                    break;
                }
                
                System.out.println(Thread.currentThread().getName() + " 这是第" + ticket-- + "号票");
            }
        }
    }
}

19. 多线程(死锁)(了解)

public class TestDeadLock {
    private static Object o1 = new Object();
    private static Object o2 = new Object();
    
    
    public static void main(String[] args) {
        new Thread("线程A") {
            public void run() {
                while(true) {
                    synchronized(o1) {
                        System.out.println(getName() + "正在使用o1,等待o2");
                        synchronized(o2) {
                            System.out.println(getName()  +"等到o2,执行成功");
                        }
                    }
                }               
            }
        }.start();
        
        new Thread("线程B") {
            public void run() {
                while(true) {
                    synchronized(o2) {
                        System.out.println(getName() + "正在使用o2,等待o1");
                        synchronized(o1) {
                            System.out.println(getName() + "等到o1,执行成功");
                        }
                    }
                }               
            }
        }.start();
    }
}
/*
 *  在JDK1.8中输出结果为:
 *  ----------------------
    线程A正在使用o1,等待o2
    线程A等到o2,执行成功
    线程A正在使用o1,等待o2
    线程B正在使用o2,等待o1
    -------------------------
 * */
  • 因此:synchronized不要嵌套使用,容易出错

20. 多线程和队列实现买卖票

public interface Queue {
    
    public void append(Object obj)throws Exception;
    
    public Object delete()throws Exception;

    public Object getFront()throws Exception;   
    
    public boolean isEmpty();
}

Class MyQueue

public class MyQueue implements Queue{
        
        //1 设置队列的默认长度
        
        static final int DEFAULT_SIZE=10;  //默认长度为10
        
        //2 设置队头
        
        int front; 
        
        //3 设置队尾
        
        int rear;
        
        //4 定义统计元素的变量
        
        int count;
        
        //5 队的最大长度
        
        int maxSize;
        
        Object[] queue;  //设置队列
        
        //空构造
        public MyQueue() {
            
            this.init(DEFAULT_SIZE);  //用户给定长度 默认长度为10
        }
        
        
        //有参数的构造
        public MyQueue(int size) {
            
            this.init(size);  //开辟用户给定的长度
        }
        
        /**
         * 初始化方法
         * @param size
         */
        public void init(int size) {
            
            //初始化属性
            this.maxSize=size;  //外部传进来的size
            
            //空队列
            front=rear=0;
            
            count=0;
            
            queue=new Object[size];
        }
        
        @Override
        public void append(Object obj) throws Exception {
            // TODO Auto-generated method stub
            //首先队列是否已满
            if(count>0&&front==rear) {  //判断队列是否已满
                
                throw new Exception("队列已满");
            }
            
            this.queue[rear]=obj;
            rear=(rear+1)%maxSize;
            count++;
        }

        @Override
        public Object delete() throws Exception {
            // TODO Auto-generated method stub
            if(this.isEmpty()) {
                
                throw new Exception("队列为空队");
            }
            
            Object obj=this.queue[front];
            front=(front+1)%maxSize;
            count--;
            
            return obj;
            
        }

        @Override
        public Object getFront() throws Exception {
            // TODO Auto-generated method stub
            if(!this.isEmpty()) {
                
                return this.queue[front];
            }
            return null;
        }

        @Override
        public boolean isEmpty() {
            // TODO Auto-generated method stub
            return this.count==0;
        }
    }

Class WindowQueue

public class WindowQueue { //卖票的窗口

    //定义卖票队列
    
    int maxSize=10;
    
    MyQueue queue=new MyQueue(maxSize);
    
    int num=0; //最多卖100张票
    
    boolean flag=true ; //判断是否继续卖票
    
    
    //排队买票
    public synchronized void producer()throws Exception{
        
        if(this.queue.count<maxSize) {
            
            this.queue.append(num++); //等待买票的数量++
            System.out.println("第"+num+"个客户排队买票");
            this.notifyAll(); //唤醒等待的线程
        }else {
            
            System.out.println("队列已满 请等待");
            this.wait(); 
        }
    }
    
    
    //卖票
    public synchronized void consumer()throws Exception{
        
        if(this.queue.count>0) {
            Object obj=this.queue.delete();  //出队
            int temp=Integer.parseInt(obj.toString());
            System.out.println("第"+(temp+1)+"个客户买到票离开队列");
            
            //如果当前的队列为空 并且卖出票数大于100
            if(this.queue.isEmpty()&&this.num>=100) {
                
                this.flag=false;
            }
            this.notifyAll(); //唤醒等待的线程
        }else {
            
            System.out.println("队列已空 请等待");
            this.wait();
        }
    }
}

Class Producer

public class Producer implements Runnable{

    WindowQueue queue;
    
    public Producer(WindowQueue queue) {
        
        this.queue=queue;
    }
    
    @Override
    public void run() {
        // TODO Auto-generated method stub
        while(queue.num<100) {  //必须小于100张票 才可以买票
        try {
            Thread.sleep(1000);
            queue.producer();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        }
    }

}

Class Consumer

public class Consumer implements Runnable{
    
    WindowQueue queue;
    
    public Consumer(WindowQueue queue) {
        
        this.queue=queue;
    }
    
    @Override
    public void run() {
        // TODO Auto-generated method stub
        while(queue.flag) {  //如果队列为空 并且票数大于100 就不会卖票了
            
            try {
                Thread.sleep(1000);
                queue.consumer();
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

}

Class QueueTest

public class QueueTest {        
    public static void main(String[] args) throws Exception {       
        WindowQueue queue=new WindowQueue();
        Producer p=new Producer(queue);
        
        Consumer con=new Consumer(queue);
        
        //以上的代码一定要注意 传入的是同一个对象
        
        Thread t1=new Thread(p);
        
        Thread t2=new Thread(con);
        
        
        t1.start();
        
        t2.start();
    }
}

Lambda表达式

  • Lambda表达式的形式

    参数,箭头(→)以及一个表示

Lambda适用于接口中一个抽象方法时候使用

  • 举例1——函数式接口使用
  Runnable runner = ()->{
      StringBuffer s = new StringBuffer();
      
      for(int i= 0;i < 10;i++)
          System.out.println(s.append("haha"));
  };
  • 举例2——只有一个抽象方法的接口的实现类的使用
  Thread t1 = new Thread(()->{
      StringBuffer s = new StringBuffer();
      
      for(int i= 0;i < 10;i++)
          System.out.println(s.append("haha"));
  });
  • 区别于匿名内部类——需要写出方法声明
  new Thread(new Runnable() {
      public void run() {
          StringBuffer s = new StringBuffer();
          
          for(int i= 0;i < 10;i++)
              System.out.println(s.append("haha"));
      }           
  }).start(); 



  Thread t1 = new Thread() {
      public void run() {
          StringBuffer s = new StringBuffer();
          
          for(int i= 0;i < 10;i++)
              System.out.println(s.append("haha"));
      }
  };
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值