Java进阶泛型和多线程

Posted Ricky_0528

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java进阶泛型和多线程相关的知识,希望对你有一定的参考价值。

文章目录

1. 泛型

1.1 初始泛型的应用

  • 泛型就是允许在定义类、接口、方法时使用类型形参(泛型),这个类型形参将在声明变量、创建对象、调用方法时动态地指定,进而让程序具备编译时检查的能力
  • 泛型是JDK1.5提供的1特性,最常见的泛型应用场景便是在使用集合时通过泛型指定集合内对象的类型,为程序代码提供了编译时纠错的能力
package com.ricky.parameterized;

import java.util.ArrayList;

public class ListSample 

    public static void main(String[] args) 
        ArrayList list = new ArrayList();
        list.add("hello");
        list.add("haha");
        list.add(12345);

        String str = (String)list.get(1);
        System.out.println(str.substring(0, 2));
    

不使用泛型同样可以插入元素,但编译器不会对元素的类型进行检查,取出元素时返回的都是Object对象

package com.ricky.parameterized;

import java.util.ArrayList;

public class ListSample 

    public static void main(String[] args) 
        ArrayList<String> list = new ArrayList();
        list.add("hello");
        list.add("haha");
        // list.add(12345); // 报错

        System.out.println(list.get(1).substring(0, 2));
    

1.2 自定义泛型及其应用

①创建自定义泛型类

自定义泛型类,需要在类名后增加"<标识符>":如:public class SampleClass<T> ...

<T>与<E>的区别,标识符的字母本身并无强制要求,常见写法有两种:

  • <T>是Type单词的首字母,说明传入的是类型
  • <E>是Element元素的首字母,代表是集合中的元素
package com.ricky.parameterized;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class CollectionUtils<E> 

    private List<E> data = new ArrayList();
    public void add(E element) 
        data.add(element);
    
    public E randomSelect() 
        Random random = new Random();

        return data.get(random.nextInt(data.size()));
    

    public static void main(String[] args) 
        CollectionUtils<String> utils = new CollectionUtils<>();
        utils.add("haha");
        utils.add("xixi");
        utils.add("heihei");
        utils.add("huhu");
        System.out.println(utils.randomSelect());
    


② 泛型方法

JDK 1.5以后还提供了泛型方法的支持,允许在类没有声明泛型的前提下让方法独立使用泛型进行开发

package com.ricky.parameterized;

import java.util.ArrayList;
import java.util.List;

public class PtMethod 
    public <T> List<T> transferToList(T[] array) 
        List<T> list = new ArrayList<>();
        for (T item : array) 
            list.add(item);
        
        return list;
    

    public static void main(String[] args) 
        PtMethod ptMethod = new PtMethod();
        String[] array = new String[]"A", "B", "C", "D";
        List<String> list = ptMethod.transferToList(array);
        System.out.println(list);
    

1.3 泛型通配符

泛型的匹配规则

  • 当明确指定泛型类型后,就必须强制使用该类类型传入,该类型的子类也同样会报“类型不匹配”错误
  • 为了增加泛型的匹配范围,泛型通配符<?>应运而生
  • <?>代表所有类型均可传入

通过extends与super限定范围

  • extends关键字代表必须传入Shape或者子类才通过检查

    public void doSth(List<? extends Shape> shapeList)
    
  • super关键字代表必须传入Rectangle或者其父类才能通过检查

    public void doSth(List<? super Rectangle> shapeList)
    

2. 线程

2.1 多线程的概念

程序是静态文件,进程是程序运行后在内存中的实例

  • 当一个程序执行进入内存运行时,即变成一个进程
  • 进程的资源是彼此隔离的,其他进程不允许访问

线程是进程内执行的“任务”

  • 线程是进程内的一个“基本任务”,每个线程都有自己的功能,它是CPU分配与调度的基本单位
  • 一个进程内可以包含多个线程,反之一个线程只能隶属于某一个进程
  • 进程内至少拥有一个“线程”,这个线程叫“主线程”,主线程消亡则进程结束

Java中进程与线程

  • 一般称只有一个main主线程的程序为“单线程”程序,又垃圾收集线程不考虑
  • 由main主线程创建出来线程,成为多线程程序

2.2 创建多线程的三种方式

① 继承Thread类创建线程

package com.ricky.thread;

import java.util.Random;

public class ThreadSample 
    class Runner extends Thread 
        @Override
        public void run() 
            Integer speed = new Random().nextInt(10);
            for (int i = 1; i <= 10; i++) 
                try 
                    Thread.sleep(1000);
                 catch (InterruptedException e) 
                    e.printStackTrace();
                
                System.out.println("第" + i + "秒:" + this.getName() + "已跑到" + (i * speed) + "米(" + speed + "米/秒)");
            
        
    

    public void start() 
        Runner threadA = new Runner();
        threadA.setName("A");
        Runner threadB = new Runner();
        threadB.setName("B");
        Runner threadC = new Runner();
        threadC.setName("C");
        Runner threadD = new Runner();
        threadD.setName("D");
        threadA.start();
        threadB.start();
        threadC.start();
        threadD.start();
    
    public static void main(String[] args) 
        ThreadSample threadSample = new ThreadSample();
        threadSample.start();
    

此方法不太推荐,因为Java是单继承的,继承了Thread类就无法再继承其它的类了,因而我们更偏向于使用接口

② 实现Runnable接口创建线程

更为推荐的方式

package com.ricky.thread;

import java.util.Random;

public class ThreadSample 
    class Runner implements Runnable 

        @Override
        public void run() 
            Integer speed = new Random().nextInt(9) + 1;
            for (int i = 1; i <= 10; i++) 
                try 
                    Thread.sleep(1000);
                 catch (InterruptedException e) 
                    e.printStackTrace();
                
                System.out.println("第" + i + "秒:" + Thread.currentThread().getName() + "已跑到" + (i * speed) + "米(" + speed + "米/秒)");
            
        
    

    public void start() 
        Runner runner = new Runner();
        Thread threadA = new Thread(runner);
        threadA.setName("A");
        threadA.start();
        Thread threadB = new Thread(runner);
        threadB.setName("B");
        threadB.start();
        Thread threadC = new Thread(runner);
        threadC.setName("C");
        threadC.start();
        Thread threadD = new Thread(runner);
        threadD.setName("D");
        threadD.start();
    

    public static void main(String[] args) 
        ThreadSample threadSample = new ThreadSample();
        threadSample.start();
    

③ Callable接口创建线程

允许线程执行后将值进行返回,这是Callable接口的特点

package com.ricky.thread;

import java.util.Random;
import java.util.concurrent.*;

public class ThreadSample 
    class Runner implements Callable<Integer>  // 有泛型是因为使用Callable接口支持返回值
        public String name;

        @Override
        public Integer call() 
            Integer speed = new Random().nextInt(9) + 1;
            Integer totalMeter = 0;
            for (int i = 1; i <= 10; i++) 
                Thread.sleep(1000);
                totalMeter += speed;
                System.out.println("第" + i + "秒:" + this.name + "已跑到" + (i * speed) + "米(" + speed + "米/秒)");
            
            return totalMeter;
        
    

    public void start() throws ExecutionException, InterruptedException 
        ExecutorService executorService = Executors.newFixedThreadPool(4);
        Runner threadA = new Runner();
        threadA.name = "A";
        Runner threadB = new Runner();
        threadB.name = "B";
        Runner threadC = new Runner();
        threadC.name = "C";
        Runner threadD = new Runner();
        threadD.name = "D";
        Future<Integer> r1 = executorService.submit(threadA);
        Future<Integer> r2 = executorService.submit(threadB);
        Future<Integer> r3 = executorService.submit(threadC);
        Future<Integer> r4 = executorService.submit(threadD);
        executorService.shutdown();
        System.out.println(threadA.name + "累计跑了" + r1.get() + "米");
        System.out.println(threadB.name + "累计跑了" + r2.get() + "米");
        System.out.println(threadC.name + "累计跑了" + r3.get() + "米");
        System.out.println(threadD.name + "累计跑了" + r4.get() + "米");
    

    public static void main(String[] args) throws ExecutionException, InterruptedException 
        ThreadSample threadSample = new ThreadSample();
        threadSample.start();
    

④ 总结

  • 继承Thread,Java对继承不友好,不推荐使用
  • 实现Runnable接口,Java编程友好,但无法返回执行后数据
  • 实现Callable接口,可以返回多线程执行结果,编程稍显复杂

2.3 线程同步

代码中的同步机制:synchronized(同步锁)关键字的作用就是利用一个特定的对象设置一个锁lock,在多线程并发访问的时候,同时只允许一个线程可以获得这个锁,执行特定的代码,执行后释放锁,继续由其他线程争抢

① synchronized代码块 - 任意对象即可

package com.ricky.thread;

public class SyncSample 
    class Printer 
        Object lock = new Object(); // 锁对象
        public void print() 
            synchronized (lock) 
                try 
                    Thread.sleep(500);
                    System.out.print("魑");
                    Thread.sleep(500);
                    System.out.print("魅");
                    Thread.sleep(500);
                    System.out.print("魍");
                    Thread.sleep(500);
                    System.out.print("魉");
                    System.out.println();
                 catch (InterruptedException e) 
                    e.printStackTrace();
                
            
        
    

    class PrintTask implements Runnable 
        public Printer printer;
        @Override
        public void run() 
            printer.print();
        
    

    public void start() 
        Printer printer 以上是关于Java进阶泛型和多线程的主要内容,如果未能解决你的问题,请参考以下文章

Java:Effective java学习笔记之 优先考虑泛型和泛型方法

Java:Effective java学习笔记之 优先考虑泛型和泛型方法

java 泛型和object比较

Java泛型和内部类

Java 泛型和静态工厂方法——语法

Java 基础语法Java 的泛型和包装类