JavaSE进阶:多线程(一)

目录

1.多线程简介

1.多任务与多线程的初步了解

2.普通方法的调用与多线程

3.程序.进程.线程

4.多线程核心概念

5.并发与并行

6.线程同步与异步

​ 2.线程实现

1.Thread类

获取和修改线程名称

买票案例

2.Runnable接口

匿名内部类实现Runnable接口

龟兔赛跑案例

四个窗口各卖100张票案例

帅哥和美女的存钱取钱过程(两种写法)

3.Callable接口(了解即可)

1.Callble接口实现

2.线程池实现Callble接口

4.Future接口

两个线程并发计算1~50,51~100的和,再汇总

统计

5.Lamda表达式

6.静态代理


1.多线程简介

1.多任务与多线程的初步了解

多任务

现实中太多这样同时做多件事情的例子了,看起来是多个任务都在做,其实本质上我们
的大脑在同一时间依旧只做了一件事情。例:吃饭玩手机,上厕所玩手机

多线程

原来是一条路,慢慢因为车太多了,道路堵塞,效率极低。
为了提高使用的效率,能够充分利用道路,于是加了多个车道。
从此,妈妈再也不用担心道路堵塞了。

2.普通方法的调用与多线程

3.程序.进程.线程

一个进程可以有多个线程,形成一个程序

如:视频中同时听声音、看图像、看字幕等等

Process(进程)与Thread(线程)

◆说起进程,就不得不说下程序。程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。


◆而进程则是执行程序的一次执行过程,它是一个动态的概念。 是系统资源分配的单位

应用程序(typerpa,word,IDEA)运行的时候进入到内存程序在内存中占用的内存空间叫做进程


◆通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程是CPU调度和执行的的单位。

程序中如果只有一条执行路径,那么这个程序就是单线程的程序

多线程是指从软硬件上实现多条执行流程的技术。


注意:很多 多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务
器。如果是模拟出来的多线程,即在一个cpu的情况下, 在同一个时间点,cpu只能
执行一个代码,因为切换的很快,所以就有同时执行的错局。

补充知识点:人间一天(s),计算机十年(ns)

4.多线程核心概念

  • 线程就是独立的执行路径
  • 在程序运行时,即使没有自己创建线程,后台也会有多个线程,比如主线程,GC线程
  • main()称之为主线程,为系统的入口,用于执行整个程序
  • 在一个进程中,如果开辟了多个线程,线程的运行是由调度器安排调度的,调度器是与操作系统紧密相关的,先后顺序是不能人为干预的
  • 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制
  • 线程会带来额外的开销,如CPU调度时间,并发控制开销
  • 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

5.并发与并行

正在运行的程序(软件)就是一个独立的进程, 线程是属于进程的,多个线程其实是并发与并行同时进行的

并发:

CPU同时处理线程的数量有限。

CPU会轮询为系统的每个线程服务,由于CPU切换的速度很快,给我们的感觉这些线程在同时执行,这就是并发。

并行:

在同一个时刻上,同时有多个线程在被CPU处理并执行。

并发性

多个线程操作同一资源

同一对象被多个线程操作

例:上万人抢100张票、两个银行同时取钱

6.线程同步与异步

同步:形容一次方法调用,同步一旦开始,调用者必须等待该方法返回,才能继续

单条执行路径:单线程

 异步:形容一次方法调用,一旦开始,像是一次消息传递,调用者告知之后立即返回。二者竞争时间片,并发执行

多条执行路径:多线程

 2.线程实现

三种创建方式

Thread class继承Thread类(重点)

Runnable接口     实现Runnable接口(重点)

Callable接口    实现Callable接口(了解)

直接调用run方法会当成普通方法执行,此时相当于还是单线程执行。

只有调用start方法才是启动一个新的线程执行

三种方式对比
方式优点缺点
继承Thread类编程简单,可以直接使用Thread类中的方法扩展性较差,不能再继续继承其他的类,不能返回线程执行的结果
实现Runnable接口扩展性强,实现该接口的同时还可以继承其他的类变成相对复杂,不能返回线程执行的结果
实现Callable接口扩展性强,实现该接口的同时还可以继承其他的类,可以的到线程执行的结果编程相对复杂

1.Thread类

Thread类是线程对象的描述类

自定义线程类继承Thread类

重写run()方法,编写线程执行体

创建线程对象,调用start()方法启动线程

线程不一定立即执行,CPU安排调度

优缺点:

优点:编码简单

缺点:线程类已经继承Thread,无法继承其他类,不利于扩展。

package java_se.java_jinjie.duoxiancheng.demo01;
//创建线程方式一:继承Thread类,重写run()方法,调用start开启线程
//总结:注意,线程开启不一定立即执行,由CPU调度执行
public class Demo1 extends Thread{@Overridepublic void run() {//run()方法线程体for (int i = 0; i < 2; i++) {System.out.println("我在看代码---+i" +i);}}public static void main(String[] args) {//main线程,主线程//创建一个线程对象Demo1 demo1 = new Demo1();//调用start()方法demo1.start();for (int i = 0; i < 20; i++) {System.out.println("我在学习多线程---"+i);}}
}

获取和修改线程名称

  • 获取线程ID和线程名称
    • 1.在Thread的子类调用this.getId()或this.getName()
    • 2.使用Thread.currentThread().getId()和Thread.currentThread().getName()
  • 修改线程名称
    • 1.调用线程对象的setName()方法
    • 2.使用线程子类的有参构造方法赋值

不建议使用:避免OOP单继承局限性

package java_se.java_jinjie.duoxiancheng.demo01;import org.jetbrains.annotations.NotNull;//创建线程方式一:继承Thread类,重写run()方法,调用start开启线程
//总结:注意,线程开启不一定立即执行,由CPU调度执行
public class Demo1 extends Thread{@Overridepublic void run() {//run()方法线程体  启动线程,不能run方法,使用startfor (int i = 0; i < 2; i++) {
//            //第一种方式   仅限继承Thread类
//            //获得输出的线程ID和线程名称this.getID和this.getName
//            System.out.println( "线程ID:"+this.getId()+"线程名称"+this.getName()+"我在看代码---+i" +i);//第二种方式//Thread.currentThread() 获取当前线程(正在执行的线程)System.out.println("线程ID:"+Thread.currentThread().getId()+",线程名称:"+Thread.currentThread().getName()+"子线程");}}public Demo1() {}public Demo1(@NotNull String name) {super(name);}public static void main(String[] args) {//main线程,主线程//创建一个线程对象Demo1 demo1 = new Demo1();//获取线程ID
//        System.out.println(demo1.getId());//无参构造Demo1 demo2 = new Demo1();//有参构造Demo1 demo11 = new Demo1("我的子线程11");Demo1 demo22 = new Demo1("我的子线程22");//修改线程名称
//        demo1.setName("我的子线程1");
//        demo2.setName("我的子线程2");//调用start()方法demo1.start();demo2.start();demo11.start();demo22.start();for (int i = 0; i < 2; i++) {System.out.println("我在学习多线程---"+i);}}
}

买票案例

package java_se.java_jinjie.duoxiancheng.demo01;public class TestTicketWin {public static void main(String[] args) {//创建四个窗口TicketWin w1 = new TicketWin("窗口1");TicketWin w2 = new TicketWin("窗口2");TicketWin w3 = new TicketWin("窗口3");TicketWin w4 = new TicketWin("窗口4");//启动线程w1.start();w2.start();w3.start();w4.start();}
}
/*
package java_se.java_jinjie.duoxiancheng.demo01;import org.jetbrains.annotations.NotNull;public class TicketWin extends Thread{private int ticket=10;//票@Overridepublic void run() {//买票功能while (true){if (ticket <= 0) {break;}//模拟延时try {Thread.sleep(200);}catch (InterruptedException e){e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"卖了第"+ticket+"张票");ticket--;}}public TicketWin(@NotNull String name) {super(name);}public TicketWin() {}
}*/

2.Runnable接口

  1. 实现Runnable接口
  2. 覆盖run()方法
  3. 创建实现类对象
  4. 创建线程对象
  5. 调用start()方法

优缺点:

优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强。 缺点:编程多一层对象包装,如果线程有执行结果是不可以直接返回的。

推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用

package java_se.java_jinjie.duoxiancheng.demo01;public class TestRunnable {public static void main(String[] args) {//1.创建MyRunnable对象,表示线程要执行功能MyRunnable runnable = new MyRunnable();//2.创建线程对象Thread thread = new Thread(runnable,"我的线程1");thread.start();//合并写法new Thread(runnable,"我的线程2").start();for (int i = 0; i < 5; i++) {System.out.println("main----"+i);}}
}
/*
package java_se.java_jinjie.duoxiancheng.demo01;public class MyRunnable implements Runnable{@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName()+"...");}}
}*/

匿名内部类实现Runnable接口

package java_se.java_jinjie.duoxiancheng.demo01;//匿名内部类实现Runnable
public class TestRunnable1 {public static void main(String[] args) {//创建可运行对象Runnable runnable=new Runnable() {@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName()+"..."+i);}}};//创建线程对象Thread thread = new Thread(runnable,"我的线程1");//启动线程thread.start();//主线程for (int i = 0; i < 10; i++) {System.out.println("main..."+i);}}}

龟兔赛跑案例

package java_se.java_jinjie.duoxiancheng.demo01;
//龟兔赛跑
public class Race implements Runnable{//胜利者private static String winner;@Overridepublic void run() {for (int i = 0; i <= 100; i++) {//模拟兔子休息if (Thread.currentThread().getName().equals("兔子")&&i%10==0){try {Thread.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}//判断比赛是否结束//如果比赛结束了就停止程序boolean flag=gameOver(i);if (flag){break;}System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");}}//判断是否完成比赛private boolean gameOver(int steps){//判断是否有胜利者if (winner!=null){//已经存在胜利者了return true;} {//这里不是if else,但是这里不写else和else有一样的功能,这里的大括号也可以直接省略掉,因为里面的if语句有返回值if (steps==100){winner=Thread.currentThread().getName();System.out.println("winner is"+winner);return true;}return false;}}public static void main(String[] args) {Race race = new Race();new Thread(race,"兔子").start();new Thread(race,"乌龟").start();}
}

四个窗口各卖100张票案例

package java_se.java_jinjie.duoxiancheng.demo01;public class TestTicket {public static void main(String[] args) {//10创建票对象Ticket ticket = new Ticket();//2.创建线程对象Thread t1 = new Thread(ticket,"窗口1");Thread t2 = new Thread(ticket,"窗口2");Thread t3 = new Thread(ticket,"窗口3");Thread t4 = new Thread(ticket,"窗口4");//3.启动线程t1.start();t2.start();t3.start();t4.start();}
}
/*
package java_se.java_jinjie.duoxiancheng.demo01;//票类(共享资源)
public class Ticket implements Runnable{private  int ticket=100;@Overridepublic void run() {while (true){if (ticket<=0){break;}System.out.println(Thread.currentThread().getName()+"卖了第"+ticket+"张票");ticket--;}}
}*/

帅哥和美女的存钱取钱过程(两种写法)

(1)封装写法

package java_se.java_jinjie.duoxiancheng.demo01;public class TestBankCard {public static void main(String[] args) {//1.创建一张银行卡BankCard card = new BankCard();//2.创建存钱,取钱AddMoney add = new AddMoney(card);RemoveMoney remove = new RemoveMoney(card);//3.创建两个线程Thread shuaige = new Thread(add,"帅哥");Thread meinv = new Thread(remove,"美女");//启动线程shuaige.start();meinv.start();}
}
/*
package java_se.java_jinjie.duoxiancheng.demo01;
//银行卡
public class BankCard{private double money;//余额public double getMoney() {return money;}public void setMoney(double money) {this.money = money;}}package java_se.java_jinjie.duoxiancheng.demo01;
//存钱
public class AddMoney implements Runnable{private BankCard card;public AddMoney(BankCard card) {this.card = card;}@Overridepublic void run() {for (int i = 0; i < 10; i++) {card.setMoney(card.getMoney()+1000);System.out.println(Thread.currentThread().getName()+"存了1000,余额是:"+card.getMoney());}}
}package java_se.java_jinjie.duoxiancheng.demo01;
//取钱
public class RemoveMoney implements Runnable{private BankCard card;public RemoveMoney(BankCard card) {this.card = card;}@Overridepublic void run() {for (int i = 0; i < 10; i++) {
//            try {
//                Thread.sleep(20);
//            }catch (InterruptedException e){
//                e.printStackTrace();
//            }if (card.getMoney()>=1000){card.setMoney(card.getMoney()-1000);System.out.println(Thread.currentThread().getName()+"取了1000,余额是"+card.getMoney());}else {System.out.println("余额不足,请赶快存取");//如果余额不足就把钱还原回去i--;}}}
}*/

(2)匿名内部类写法

package java_se.java_jinjie.duoxiancheng.demo01;public class TestBankCard2 {public static void main(String[] args) {BankCard card = new BankCard();//存钱Runnable add = new Runnable() {@Overridepublic void run() {for (int i = 0; i < 10; i++) {card.setMoney(card.getMoney() + 1000);System.out.println(Thread.currentThread().getName() + "存了1000,余额是:" + card.getMoney());}}};//取钱Runnable sub = new Runnable() {@Overridepublic void run() {for (int i = 0; i < 10; i++) {
//            try {
//                Thread.sleep(20);
//            }catch (InterruptedException e){
//                e.printStackTrace();
//            }if (card.getMoney() >= 1000) {card.setMoney(card.getMoney() - 1000);System.out.println(Thread.currentThread().getName() + "取了1000,余额是" + card.getMoney());} else {System.out.println("余额不足,请赶快存取");//如果余额不足就把钱还原回去i--;}}}};//创建线程对象new Thread(add,"明明").start();new Thread(sub,"丽丽").start();}
}

3.Callable接口

1.Callble接口实现

  • public interface Callable{public V call() throws Exception;}
    • JDK5加入,与Runnable接口类似,实现之后代表一个线程任务。
    • Callable具有泛型返回值、可以声明异常(Runnable没有)
package java_se.java_jinjie.duoxiancheng.demo06;import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;//演示Callable按口的使用
//Callable和Runnable接口的区别
//(1)Callable按口中call方法有返回值, Runnable按口中run方法没有返回值
//(2)callable接口中call方法有声明异常,Runnable接口中run方法没有异常
public class Demo2 {public static void main(String[] args) throws ExecutionException, InterruptedException {//功能需求,使用Callable实现1-100和//1.创建Callable对象Callable callable=new Callable() {@Overridepublic Integer call() throws Exception {System.out.println(Thread.currentThread().getName()+"开始计算");int sum=0;for (int i = 0; i <=100; i++) {sum+=i;Thread.sleep(10);}return sum;}};//2.把Callable对象转成可执行任务FutureTask task=new FutureTask<>(callable);//3.创建线程Thread thread = new Thread(task);//4.启动线程thread.start();//5.获取结果(等待call方法执行完毕,才会返回)Integer sum = task.get();System.out.println("结果是:"+sum);}
}

2.线程池实现Callble接口

  1. 实现Callable接口,需要返回值类型
  2. 重写call方法,需要抛出异常
  3. 创建目标对象
  4. 创建执行服务: ExecutorService es = Executors.newFixedThreadPool(1);
  5. 提交执行: Future future= es.submit(t1);
  6. 获取结果: Integer r1 = suture.get()
  7. 关闭服务: es.shutdownNow();

优缺点:

优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强。 可以在线程执行完毕后去获取线程执行的结果。 缺点:编码复杂一点。
package java_se.java_jinjie.duoxiancheng.demo06;import java.util.concurrent.*;//使用线程池计算1-100和
public class Demo3 {public static void main(String[] args) throws ExecutionException, InterruptedException {//1创建线程池ExecutorService es = Executors.newFixedThreadPool(1);//2.提交任务 Future:表示将要执行完任务的结果Future future = es.submit(new Callable() {@Overridepublic Integer call() throws Exception {System.out.println(Thread.currentThread().getName() + "开始计算");int sum = 0;for (int i = 0; i <= 100; i++) {sum += i;
//                    Thread.sleep(10);}return sum;}});//3.获取任务结果System.out.println(future.get());//4.关闭线程池es.shutdown();}
}

4.Future接口

表示将要完成的任务

表示ExecutorService.submit()所返回的状态结果,就是call()的返回值

方法:V get()以阻塞形式等待Future的异步处理结果(call()的返回值 )

两个线程并发计算1~50,51~100的和,再汇总

package java_se.java_jinjie.duoxiancheng.demo06;import java.util.concurrent.*;public class Demo4 {public static void main(String[] args) throws ExecutionException, InterruptedException {//1.创建线程池ExecutorService es = Executors.newFixedThreadPool(2);//2.提交任务Future future1 = es.submit(new Callable() {@Overridepublic Integer call() throws Exception {int sum=0;for (int i = 1; i <= 50; i++) {sum+=i;}System.out.println("1-50计算完毕");return sum;}});Future future2 = es.submit(new Callable() {@Overridepublic Integer call() throws Exception {int sum=0;for (int i = 51; i <= 100; i++) {sum+=i;}System.out.println("51-100计算完毕");return sum;}});//3.获取结果int sum=future1.get()+future2.get();System.out.println(sum);//4.关闭线程池es.shutdown();}
}

5.Lamda表达式

  • λ希腊字母表中排序第十-位的字母,英语名称为Lambda
  • 避免匿名内部类定义过多
  • 其实质属于函数式编程的概念
    • (params) -> expression [表达式]
    • (params) -> statement [ 语句]
    • (params) -> { statements }

特点:

  • 避免匿名内部类定义过多
  • 可以让你的代码看起来很简洁
  • 去掉了一堆没有意义的代码,只留下核心的逻辑

理解Functional Interface(函数是接口)是学习java8 lambda表达式的关键所在

函数是接口的定义:

任何接口,如果质保函唯一 一个抽象方法,那么它就是一个函数式接口

public interface Runnable{public abstract void run();
}

对于函数式接口,我们可以通过lambda表达式来创建该接口的对象

lambda表达式总结

        1.lambda表达式只能有一行代码的情况下才能简化称为一行,如果有多行,那么就用代码块{}

        2.前提:接口为函数式接口(只有一个方法)

        3.多个参数也可以去掉参数类型,要去掉就都去掉,多个参数必须加上括号

lambda简化:

love =(int a)->{System.out.println(a);};        未简化

love = (a)->{System.out.println(a);};            简化参数类型

love=a->{System.out.println(a);};                简化括号

love=a-> System.out.println(a);                  简化大括号

package java_se.java_jinjie.duoxiancheng.demo02;public class TestLambda1 {//3.静态内部类static class Like2 implements ILike{@Overridepublic void lambda() {System.out.println("i like lambda3");}}public static void main(String[] args) {ILike like = new Like();like.lambda();like = new Like2();like.lambda();//4.局部内部类class Like3 implements ILike{@Overridepublic void lambda() {System.out.println("i like lambda4");}}like = new Like3();like.lambda();//5.匿名内部类like = new ILike() {@Overridepublic void lambda() {System.out.println("i like lambda4");}};//用lambda简化like = ()->{System.out.println("i like lambda5");};like.lambda();}
}
//1.定义一个函数式接口
interface ILike{void lambda();
}
//2.实现类
class Like implements ILike{@Overridepublic void lambda() {System.out.println("i like lambda2");}
}
package java_se.java_jinjie.duoxiancheng.demo02;public class TestLambda2 {static class Love2 implements Ilove{@Overridepublic void love(int a) {System.out.println("i love yuu2->"+a);}}public static void main(String[] args) {Ilove love = new Love();love.love(1);love = new Love2();love.love(2);class Love3 implements Ilove{@Overridepublic void love(int a) {System.out.println("i love yuu3->"+a);}}love = new Love3();love.love(3);love = new Ilove() {@Overridepublic void love(int a) {System.out.println("i love yuu4->"+a);}};love.love(4);love = new Love3();love =(int a)->{System.out.println("i love yuu5->"+a);};love.love(520);//1.简化1:参数类型love = (a)->{System.out.println("i love yuu5->"+a);};love.love(521);//简化2:简化括号love=a->{System.out.println("i love yuu5->"+a);};love.love(522);//简化3:去掉化{}love=a-> System.out.println("i love yuu5->"+a);love.love(523);//总结://1.lambda表达式只能有一行代码的情况下才能简化称为一行,如果有多行,那么就用代码块{}//2.前提:接口为函数式接口(只有一个方法)//3.多个参数也可以去掉参数类型,要去掉就都去掉,多个参数必须加上括号}
}interface Ilove{void love(int a);
}class Love implements Ilove{@Overridepublic void love(int a) {System.out.println("i love yuu1");}
}

6.静态代理

你:                真实角色
婚庆公司:     代理你,帮你处理结婚的事
结婚:             实现都实现结婚接口即可

静态代理模式总结:
    真实对象和代理对象都要实现统一接口
    代理对象要代理真实对象
好处:
    代理对象可以做很多真实对象做不了的事情
    真实对象专注做自己的事情

package java_se.java_jinjie.duoxiancheng.demo02;public class StaticProxy {public static void main(String[] args) {WeddingCompany weddingCompany = new WeddingCompany(new You());weddingCompany.HappyMarry();}}
interface Marry{void HappyMarry();
}//真实角色,你去结婚
class You implements Marry{@Overridepublic void HappyMarry() {System.out.println("姜某要结婚了,超开心!");}
}
//代理角色:帮助你完成结婚
class WeddingCompany implements  Marry{private Marry target;public WeddingCompany(Marry target) {this.target = target;}@Overridepublic void HappyMarry() {before();this.target.HappyMarry();after();}private void after() {System.out.println("结婚之后收尾款");}private void before() {System.out.println("结婚之前布置现场");}
}
//静态代理模式总结://真实对象和代理对象都要实现统一接口//代理对象要代理真实对象
//好处://代理对象可以做很多真实对象做不了的事情//真实对象专注做自己的事情


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部