Java多线程,stream流学习

一、什么是线程?什么是进程?

简单来说,
进程:⼀个内存中运⾏的应⽤程序,每个进程都有⼀个独⽴的内存空间;
线程:线程是进程中的⼀个执⾏单元,负责当前进程中程序的执⾏,⼀个进程中可以有一个或多个线程

什么是多线程?

如果说我有一个单核的CPU,它某一时刻只能处理一件事,但是它处理事情的速度极快,我可以拿他一边打cs,一边打黑🐒它可以在两个线程之间来回切换,让人感觉这俩是在同时进行的,这能叫多线程吗?肯定不能;真正的多线程并发应该是:A做A的事情,B做B的事情,A与B之间不会相互影响;

线程对象的有生命周期:
①新建状态
②就绪状态
③运行状态
④阻塞状态
⑤死亡状态

作用:多线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的。

线程的构造方法

线程中最基础的方法

怎么使用多线程? 实现线程有两种方式;

第一种方式:继承

编写一个类,直接继承Thread,然后再重写其中的run()方法;然后可以调用该线程对象的start()方法启动该线程;
eg

public class ThreadTest02 {
    public static void main(String[] args) {
        MyThread t = new MyThread();
        // 启动线程
        //t.run(); // 不会启动线程,不会分配新的分支栈。(这种方式就是单线程。)
        t.start();
        // 这里的代码还是运行在主线程中。
        for(int i = 0; i < 1000; i++){
            System.out.println("主线程--->" + i);
        }
    }
}

class MyThread extends Thread {
    @Override
    public void run() {
        // 编写程序,这段程序运行在分支线程中(分支栈)。
        for(int i = 0; i < 1000; i++){
            System.out.println("分支线程--->" + i);
        }
    }
}

如果直接调用t.run()不会启动线程,也不会分配新的分支栈;
而t.start()会启动一个分支线程,再JVM中开辟一个新的栈空间;有了空间之后,线程就自动启动了,然后会调用重写的run()方法

第二种方式:实现Runna接口来实现run()方法;

eg

public class ThreadTest03 {
    public static void main(String[] args) {
        Thread t = new Thread(new MyRunnable()); 
        // 启动线程
        t.start();
        for(int i = 0; i < 100; i++){
            System.out.println("主线程--->" + i);
        }
    }
}

// 这并不是一个线程类,是一个可运行的类。它还不是一个线程。
class MyRunnable implements Runnable {
    @Override
    public void run() {
        for(int i = 0; i < 100; i++){
            System.out.println("分支线程--->" + i);
        }
    }
}

第二种方法相比于第一种方法,可以再去继承另一个类更加灵活;

线程中常用的方法

                      方法名

                   作用
static Thread currentThread()获取当前线程对象
String getName()获取线程对象名字
void setName(String name)修改线程对象名字
void sleep(long millis)让当前线程休眠millis秒
void interrupt()终止线程睡大觉
void stop()强行结束一个线程(不推荐使用,会丢失数据)

当线程没有名字时,默认为Thread-0,Thread-1.......

public class demo {
    public static void main(String[] args) {
        MyThread t = new MyThread();
        t.start();
        System.out.println(Thread.currentThread().getName());//main中的进程默认名称为main
    }
}

class MyThread extends Thread {
    @Override
    public void run() {
        Thread t = Thread.currentThread();
        System.out.println(t.getName());//Thread-0
        t.setName("PatrickStar's Thread");
        System.out.println(t.getName());//PatrickStar's Thread
        }
}

sleep方法可以让线程直接去睡大觉,可惜单位是毫秒,只能休息一会,这么做的作用是让线程进入阻塞状态,放弃占有CPU时间片,把它让给其它的线程用;

run()方法内的异常只能使用try-catch而不能使用throws,因为run()方法在父类中没有抛出任何异常,子类不能比父类抛出更多的异常;

一个线程只能启动一次,启动多次会报错

public static void main(String[] args) {
        MyThread t = new MyThread();
        t.start();
        System.out.println(Thread.currentThread());
        System.out.println(Thread.currentThread().getName());
        t.start();
    }//编译的时候并不会有异常,但是运行的时候会抛出异常

既然stop方法不推荐那么我们应该怎么终止线程?可以用一个Boolean类型的标记点,
eg.

package threadTest;

public class ThreadTest10 {
    public static void main(String[] args) {
        MyRunable4 r = new MyRunable4();
        Thread t = new Thread(r);
        t.setName("t");
        t.start();

        // 模拟6秒
        try {
            Thread.sleep(6000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 终止线程
        // 你想要什么时候终止t的执行,那么你把标记修改为false,就结束了。
        r.flag = false;
    }
}

class MyRunable4 implements Runnable {

    // 打一个布尔标记
    boolean flag = true;

    @Override
    public void run() {
        for (int i = 0; i < 10; i++){
            if(flag){
                System.out.println(Thread.currentThread().getName() + "--->" + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else{
                // return就结束了,你在结束之前还有什么没保存的。
                // 在这里可以保存呀。
                //save....
                //终止当前线程
                return;
            }
        }
    }
}//前后经过五秒,输出从0到4

线程调度的优先级;

  • 抢占式调度模型:
    那个线程的优先级比较高,抢到的CPU时间片的概率就高一些/多一些。
    java采用的就是抢占式调度模型

  • 均分式调度模型:
    平均分配CPU时间片。每个线程占有的CPU时间片时间长度一样。
    平均分配,一切平等。
    有一些编程语言,线程调度模型采用的是这种方式。

实例方法最低优先级为1,默认为5,最高为10;优先级高的会比优先级低的获取CPU时间片的可能高一点;在优先级设置的时候有几个常量可以使用

            Thread t = new Thread(new MyRunnable7());
            t.setPriority(Thread.MAX_PRIORITY);
            System.out.println(t.getPriority());//打印10

静态方法;让当前线程从”运行状态“变成”就绪状态“,所有的同等优先级的线程都可以抢到执行权,包括它自己也可以;

实例方法;join

        public static void main(String[] args) {
            System.out.println("main begin");

            Thread t = new Thread(new MyRunnable7());
            t.setName("t");
            t.start();
            //合并线程
            try {
                t.join(); // t合并到当前线程中,当前线程受阻塞,t线程执行直到结束。
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("main over");
        }
    }

    class MyRunnable7 implements Runnable {

        @Override
        public void run() {
            for(int i = 0; i < 10000; i++){
                System.out.println(Thread.currentThread().getName() + "--->" + i);
            }
        }

正常来讲多线程应该是并行的, 那么如果没有join()方法,上述代码中的"main over"应该是在线程t打印1到10000的过程中打印出来,但是join()方法可以把t线程合并到当前线程,然后执行完t线程之后再打印"main over";所有join()方法可以让当前线程进入"阻塞状态",然后执行完合并进来的线程后再继续刚才的线程;

多线程并发下的安全问题;

什么情况下会存在安全问题:
①多线程并发
②有共享数据
③共享数据有修改行为

当我的线程A对数据temp有修改,线程B对temp也要有修改,这时数据就是不安全的;那如果我们有共享的数据,又对这个数据要修改,这个时候就要线程排队执行,不能并发处理用排队执行来解决安全问题,这种机制叫做线程同步机制;排队执行自然线程的效率就会降低,但是在数据安全面前,放弃一点效率也不是大问题;

其实这里有两个专业术语:

异步编程模型:也就是并发,线程t1只用管线程t1的线程t2只用管线程t2的;
同步编程模型:也就是排队,线程t2只能在线程t1完成之后再执行

synchronized 线程同步

synchronized(共享的数据){
	// 线程同步代码块。
}

 这里我们创建一个类ThreadSyn;

public class ThreadSyn implements Runnable{
    private  volatile static int count = 0;
    @Override
    public void run() {
        synchronized(this) {
            for (int i = 0; i < 5; i++) {
                try {
                    System.out.println(Thread.currentThread().getName() + ":" + (count++));
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

然后执行下面的语句

        ThreadSyn threadSynOne = new ThreadSyn();
        Thread thread1 = new Thread(threadSynOne,"ThreadSynOne");
        Thread thread2 = new Thread(threadSynOne,"ThreadSynTwo");
        thread1.start();
        thread2.start();

 想想如果没有synchronized语句会输出什么,有了synchronized又会输出什么;

没有synchronized的话线程并发处理并且对这同一个数据操作输出的结果ThreadSynOne与Two交叉出现而且每次执行之后count的值结果都不相同
有了同步之后的输出结果如图

详细的Java多线程:Java多线程(超详细!)-CSDN博客
有关线程安全的:
synchronized可以看这个:Java多线程——synchronized使用详解_synchronized用法-CSDN博客
还有一个volatile和并发的三个基本概念:Java volatile关键字最全总结:原理剖析与实例讲解(简单易懂)-CSDN博客

二、stream流

什么是stream流

Stream将要处理的元素集合看作一种流,在流的过程中,借助Stream API对流中的元素进行操作;
它可以让我们以更加简洁的方式对集合进行操作;

对stream流的操作分为两种:
1.中间操作 :每次会返回一个新的流,可以有多个
2.终端操作:每个流只能进行一次终端操作,会产生一个新的集合或值

stream的特性:

  1. stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果。
  2. stream不会改变数据源,通常情况下会产生一个新的集合或一个值。
  3. stream具有延迟执行特性,只有调用终端操作时,中间操作才会执行。

stream流的使用;

使用stream流之前可以先了解一个与它同版本出现的Lambda,Lambda可以让我们将函数作为参数传递给其它方法

基本语法: (parameters) -> expression 或 (parameters) ->{ statements; }
Lambda表达式由三部分组成:

paramaters:类似方法中的形参列表,这里的参数是函数式接口里的参数。这里的参数类型可以明确的声明也可不声明而由JVM隐含的推断。另外当只有一个推断类型时可以省略掉圆括号。

->:可理解为“被用于”的意思

方法体:可以是表达式也可以代码块,是函数式接口里方法的实现。代码块可返回一个值或者什么都不反回,这里的代码块块等同于方法的方法体。如果是表达式,也可以返回一个值或者什么都不反回;

stream的参数中可能会用到这个参数;

1.stream流的创建:

a.从集合类中(如LIst,Map,Set)创建,使用Stream()方法或者parallelStream()方法,前者创建的是顺序流后者创建的是并行流;parallel()方法可以把顺序流变成并并行流;

        List<String> list = Arrays.asList("a", "b", "c");
// 创建一个顺序流
        Stream<String> stream = list.stream();
// 创建一个并行流
        Stream<String> parallelStream = list.parallelStream();

b.用数组创建 

int[] array={1,3,5,6,8};
IntStream stream = Arrays.stream(array);

c. 调用静态方法,of(),iterate(),generate()

        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
        stream.forEach(System.out::println);//1~6

        Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 3).limit(4);
        stream2.forEach(System.out::println);//0~9

        Stream<Double> stream3 = Stream.generate(Math::random).limit(3);
        stream3.forEach(System.out::println);//三个0~1的随机数
2. 中间操作

 代码实例:Stream流中间操作方法_stream的中间操作方法-CSDN博客

3.终端操作:

Java 8 Stream终端操作使用详解_java8 stream api的终端操作-CSDN博客

更多的stream实例可以在这篇后面翻看二十个实例;【java基础】吐血总结Stream流操作_java stream流操作-CSDN博客 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值