zoukankan      html  css  js  c++  java
  • Java SE入门(二十)——多线程、Stream流和方法引用

    iwehdio的博客园:https://www.cnblogs.com/iwehdio/

    1、多线程

    • 进程:当前正在运行的程序,即一个应用程序在内存中的执行区域。

    • 线程:进程中的一个执行控制单元。一个进程可以有一个线程,也可以有多个线程。

    • 单线程:安全性高但是效率低。多线程:安全性低但是效率高。

    • CPU执行线程具有随机性。

    • 多线程的实现方式1 :Thread 类。

      1. 写一个子类,继承 Thread 类。
      2. 在子类中重写 Thread 类的 run 方法,内容是线程的操作内容。
      3. 创建线程实例。
      4. 使用 .start() 方法启动线程。
      public class MyThread extends Thread {
          @Override
          public void run(){
              System.out.println("线程");
              System.out.println(getName());
          }
      }
      
      MyThread mt = new MyThread();
      mt.setName("线程A");
      mt.start();
      
    • Thread 类中的方法:

      • String getName():获取线程的名字。
      • void setName(String name):改变线程名字。
      • static Thread currentThread():返回对当前正在执行的线程对象的引用。
      • static void sleep(long millis):将线程休眠一段时间。
    • 主方法是单线程的。

    • 多线程的实现方式2 :实现 Runnable 接口。

      1. 写一个子类,实现 Runnable 接口。
      2. 在子类中重写 run 方法,内容是线程的操作内容。
      3. 创建线程实例,并将其作为参数传入 Thread 对象。
      4. 使用 .start() 方法启动线程。
      public class MyThread implements Runnable {
          @Override
          public void run(){
              System.out.println("线程");
          	System.out.println(Thread.currentThread().getName());
          }
      }
      
      MyThread mt = new MyThread(); // 可以只创建一个,但是所有 Thread 对象会共享成员变量
      Thread t1 = new Thread(mt);	// 将实现的接口的对象作为参数传入
      t1.setName("线程A");
      t1.start();
      Thread t2 = new Thread(mt);
      t2.setName("线程B");
      t2.start();
      
    • 为什么多线程需要第二种方法的接口实现:因为 Java 中单一继承的原因,继承了 Thread 类就无法继承其他类。

    • 线程的生命周期:

      1. 新建:创建线程对象。
      2. 就绪:具备了执行条件,但没有执行权利。
      3. 运行:具备了执行权利。
      4. 死亡:作为垃圾回收。
      • 等待与唤醒:void wait():使当前线程等待。void notify():唤醒等待的线程。需要使用锁对象调用,调用线程被唤醒后先进入就绪状态。

    2、同步

    • 多个线程共享同一个数据并且并发访问共享的数据,可能会出错。

    • synchronized关键字:表示同步,可以修饰代码块和方法。被修饰的代码块和方法一旦被某个线程访问,则直接锁住,其他线程无法访问。

      • 同步代码块:

        synchronized(锁对象){
            // 执行代码
        }
        
      • 同步方法:

        private synchronized void method(){
            // 执行代码
        }
        
      • 锁对象需要被所有的线程所共享,非静态同步方法的锁对象默认为 this ,静态同步方法的锁对象默认为当前类的字节码对象。

    • 同步:安全性高,效率低。

    3、分票案例

    public class Ticket implements Runnable{
    	int tickets = 100;
    	Object obj = new Object();
    	
    	@Override
    	public void run(){
    		while(true){
    			method();
    		}
    	}
    	public synchronized void method(){
    		if(tickets > 0){
    			try {
    				Thread.sleep(1000);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			System.out.println(Thread.currentThread().getName() + ":" + tickets--);
    		}
    	}
    
    }
    
    public class Demo {
    	public static void main(String[] args) {
    		Ticket t = new Ticket();
    		Thread td1 = new Thread(t);
    		td1.setName("A");
    		Thread td2 = new Thread(t);
    		td2.setName("B");
    		Thread td3 = new Thread(t);
    		td3.setName("C");
    		
    		td1.start();
    		td2.start();
    		td3.start();
    	}
    }
    
    

    3、Stream流

    用于对集合和数组进行操作,用于JDK1.8之后。

    • 流式思想:建立生产线,按照生产线来生产产品。
    • Stream 流是一个集合元素的函数模型,不是集合也不是数据结构,本身不存储任何元素。
    • 由于 Lamda 表达式的延迟特性,只有最后一步执行时,整个模型才会按序依此执行。
    • Stream 操作的特征:
      • pipelining:中间操作都返回流对象本身,多个操作可以串联成一个管道。
      • 内部迭代。
      • 属于管道流,只能被消费(使用)一次。第一个 Stream 流调用方法,数据就会传到下一个流,第一个流就不能再使用了。
    • Stream 操作步骤:
      • 获取数据源、数据转换、执行操作。
      • 每次转换原有的 Stream 流对象不变,返回一个新的 Stream 对象。
    • 获取 Stream 流:
      • 所有的 Collection 集合都可以通过stream()方法获取流。
      • 对于 Map 集合,可以将其键值对(entrySet)通过stream()方法获取流。
      • 通过 Stream 中的of(T... value)静态方法方法将数组转换为Stream流。
    • Stream 流中的常用方法。
      • 延迟方法:返回值仍然是 Stream 接口自身的方法,可以链式调用。
        • filter(Predicate<? super T> predicate):传入的是 Predicate 接口,将一个流转换为另一个流(过滤)。比如只将以A开头的字符串传递到下一个流。
        • map(Function<? super T, extends R> mapper):传入的是 Function 接口,将一个流转换为另一个流(映射)。比如将String类型的整数转换为Integer类型。
        • limit(long maxSize):截取方法,只取前几个元素传入下一个流。
        • skip(long n):跳过前几个元素,将后边的元素传入下一个流。
        • concat(Stream<? extends T> a, Stream<? extends T> b):静态方法,将两个流合并为一个流。
      • 终结方法:返回值不再是 Stream 接口自身的方法,不可以链式调用。
        • void forEach(Consumer<? super T> action):传入的是 Consumer 接口,用来遍历流中的数据。
        • long count():获取元素格式。

    4、方法引用

    用于简化 Lamda 表达式。

    • 适用情形:

      • Lamda 表达式中的代码实现,已经有地方存在相同的实现,则可以直接调用这个实现。

      • 前提是,所调用的对象和方法都是以及存在的。

      • 示例:

        //函数式接口
        public interface Printable {
            void print(String s);
        }
        //方法以函数式接口为参数
        public static void printString(Printable p) {
            p.print("Hello");
        }
        //对于 Lamda 表达式
        printString((s)->{
            System.out.println(s);
        });
        //方法引用优化
        printString(System.out::println);
        
      • 因为 System.out 对象是自动创建好的,所有可以直接让 System.out 中的 println 方法取代 Lamda 表达式。

      • ::为引用运算符,其所在的表达式被称为方法引用。

      • Lamda 表达式中传递的参数一定是方法引用中的那个方法可以接收的类型。

    • 通过对象名引用成员方法:

      • 对象名和成员方法都是已经存在的,否则要先创建对象。
      • 通过对象名::成员方法来引用。
    • 通过类名引用静态方法:

      • 类名和静态方法都是已经存在的,不需要实例化对象。
      • 通过类名::静态方法来引用。
    • 通过 super 引用父类成员方法:

      • 存在父类和父类中存在此方法,不需要实例化父类对象。
      • 通过super::成员方法来引用。
    • 通过 this 引用本类成员方法:

      • 本类中存在此方法。
      • 通过this::成员方法来引用。
    • 类的构造器引用:

      • Lamda 表达式返回的是,通过构造方法一个新创建的对象。
      • 通过类名::new来引用。
    • 数组的构造器引用:

      • 返回的是一个新创建的数组。
      • 通过数据类型[]::new来引用。

    iwehdio的博客园:https://www.cnblogs.com/iwehdio/
  • 相关阅读:
    使用postMan调用web services wsdl接口
    Python的入门基础(Linux、python、git)
    CrossoverQA文档
    Linux_磁盘分区、挂载、查看
    Linux为什么要挂载
    图解Windows10+优麒麟双系统安装
    Linux 软件安装与卸载
    ventroy 制作多系统启动盘
    字节跳动面试官:请你实现一个大文件上传和断点续传
    关于本博客和博主
  • 原文地址:https://www.cnblogs.com/iwehdio/p/12896615.html
Copyright © 2011-2022 走看看