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接口
- 实现Runnable接口
- 覆盖run()方法
- 创建实现类对象
- 创建线程对象
- 调用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接口
- 实现Callable接口,需要返回值类型
- 重写call方法,需要抛出异常
- 创建目标对象
- 创建执行服务: ExecutorService es = Executors.newFixedThreadPool(1);
- 提交执行: Future
future= es.submit(t1); - 获取结果: Integer r1 = suture.get()
- 关闭服务: 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("结婚之前布置现场");}
}
//静态代理模式总结://真实对象和代理对象都要实现统一接口//代理对象要代理真实对象
//好处://代理对象可以做很多真实对象做不了的事情//真实对象专注做自己的事情
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
