线程、进程、多线程
- 普通方法调用:只有主线程一条执行路径,效率低;多线程:多条执行路径,主线程和子线程并行交替执行,效率高
- 在操作系统中运行的程序就是进程,比如你的QQ,播放器,游戏,IDE等等;进程的单词是Process
- 一个进程可以有多个线程,比如视频中同时听声音,看图像,看弹幕,等等;线程的单词是Thread
- 程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。而进程则是执行程序的一次执行过程,它是一个动态的概念,是系统资源分配的单位。通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程是CPU调度和执行的单位。
- 很多多线程是模拟出来的,真正的多线程是指有多个CPU,即多核,如服务器。如果是模拟出来的多线程,即在一个CPU的情况下,在同一时间点,CPU只能执行一个代码,因为切换的很快,所以就有同时执行的错觉。
- 线程就是独立的执行路径
- 在程序执行时,即使没有自己创建线程,后台也会有多个线程,比如主线程,gc线程
- main()称之为主线程,为系统的入口,用于执行整个程序
- 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为的干预的
- 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制
- 线程会带来额外的开销,如CPU调度时间,并发控制开销
- 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
- 线程有三种创建方式:继承Thread类,实现Runnable接口,实现Callable接口
继承Thread类
package com.zcf.demo1;
//创建线程方式一:继承Thread类,重写run()方法,调用start开启线程
//线程开启不一定立即执行,由CPU调度执行
public class Test1 extends Thread{
@Override
public void run() {
//run方法线程体
for(int i=0;i<20;i++){
System.out.println("线程体中的循环"+i);
}
}
public static void main(String[] args) {
//main线程,主线程
//创建一个线程对象
Test1 test1 = new Test1();
//调用start()方法开启线程
test1.start();
for(int i=0;i<1000;i++){
System.out.println("主线程中的循环"+i);
}
}
}
运行结果为:
主线程和线程中的循环同时执行,所以呈现出两种语句交替出现的运行结果
实现Runnable接口
package com.zcf.demo1;
//创建线程方式二:实现Runnable接口,重写run()方法,执行线程需要丢入Runnable接口实现类,调用start开启线程
public class Test3 implements Runnable{
@Override
public void run() {
//run方法线程体
for(int i=0;i<20;i++){
System.out.println("线程体中的循环"+i);
}
}
public static void main(String[] args) {
//main线程,主线程
//创建Runnable接口的实现类对象
Test3 test3 = new Test3();
//创建线程对象,通过线程对象来开启我们的线程,代理
//Thread thread = new Thread(test3);
//thread.start();两行代码简写成下面这一行
new Thread(test3).start();
for(int i=0;i<1000;i++){
System.out.println("主线程中的循环"+i);
}
}
}
运行结果为:
主线程和线程中的循环同时执行,所以呈现出两种语句交替出现的运行结果
小结
-
继承Thread类
- 子类继承Thread类具备多线程能力
- 启动线程:子类对象.start()
- 不建议使用:避免OOP单继承局限性
-
实现Runnable接口
-
实现Runnable接口具有多线程能力
-
启动线程:传入目标对象+Thread对象.start()
-
推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用,如下:
//一份资源 StartThread4 station = new StartThread4(); //多个代理 new Thread(station,"小明").start(); new Thread(station,"老师").start(); new Thread(station,"小红").start();
-
初识并发问题
多个线程操作同一个资源的情况下,线程不安全,数据紊乱
package com.zcf.demo1;
//多个线程同时操作同一个对象
//买火车票的例子
//发现问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱
public class Test4 implements Runnable{
//票数
private int ticketNums = 10;
@Override
public void run() {
while(true){
if(ticketNums<=0){
break;
}
//模拟延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"票");
}
}
public static void main(String[] args) {
Test4 test4 = new Test4();
new Thread(test4,"小明").start();
new Thread(test4,"老师").start();
new Thread(test4,"黄牛").start();
}
}
运行结果为:
出现并发问题,同一张票有多个人抢到,比如老师和小明都抢到了第9张票
龟兔赛跑
package com.zcf.demo1;
//模拟龟兔赛跑
public class Race implements Runnable{
//胜利者
private static String winner;
@Override
public void run() {
for(int i=1;i<=500;i++){
//模拟兔子睡觉
if(Thread.currentThread().getName().equals("兔子")&& i%10==0){
try {
Thread.sleep(10);
} 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;
}else if(steps>=500){
winner = Thread.currentThread().getName();
System.out.println("胜利者是"+winner);
return true;
}else{
return false;
}
}
public static void main(String[] args) {
Race race = new Race();
new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
}
运行结果为:
兔子跑跑停停,最终都是乌龟赢
静态代理模式
package com.zcf.demo1;
//真实对象和代理对象都要实现同一个接口
//代理对象要代理真实对象
//好处:代理对象可以做很多真实对象做不了的事情,真实对象专注做自己的事情
public class StaticProxy {
public static void main(String[] args) {
You you = new You();
WeddingCompany weddingCompany = new WeddingCompany(you);
weddingCompany.HappyMarry();
}
}
interface Marry{
void HappyMarry();
}
//真实对象,你去结婚
class You implements Marry{
@Override
public void HappyMarry() {
System.out.println("你开心地结婚");
}
}
//代理对象,帮助你结婚
class WeddingCompany implements Marry{
//代理谁:真实目标对象
private Marry target;
public WeddingCompany(Marry target){
this.target=target;
}
@Override
public void HappyMarry() {
before();
this.target.HappyMarry();//真实对象
after();
}
private void before() {
System.out.println("结婚之前,婚庆公司布置现场");
}
private void after() {
System.out.println("结婚之后,婚庆公司收拾现场");
}
}
运行结果为:
Lambda表达式
- λ是希腊字母表中排序第十一位的字母,英语名称为Lambda
- 避免匿名内部类定义过多,可以让你的代码看起来很简洁,去掉了一堆没有意义的代码,只留下核心的逻辑
- 其实质属于函数式编程的概念
(params)->expression[表达式]
(params)->statement[语句]
(params)->{statements}
- 理解Functional Interface(函数式接口)是学习Java8 lambda表达式的关键所在
- 函数式接口的定义:任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口;对于函数式接口,我们可以通过lambda表达式来创建该接口的对象
public interface Runnable{
public abstract void run();
}
- lambda表达式由来的推导:普通接口+实现类+方法调用——>静态内部类+方法调用——>局部内部类+方法调用——>匿名内部类+方法调用——>lambda表达式+方法调用
package com.zcf.lambda;
//推导lambda表达式
public class Test1 {
//3.静态内部类
static class Like2 implements Ilike{
@Override
public void lambda(){
System.out.println("i like lambda2");
}
}
public static void main(String[] args) {
Ilike like = new Like();
like.lambda();
like = new Like2();
like.lambda();
//4.局部内部类
class Like3 implements Ilike{
@Override
public void lambda(){
System.out.println("i like lambda3");
}
}
like = new Like3();
like.lambda();
//5.匿名内部类,没有类的名称,必须借助接口或者父类
like = new Ilike() {
@Override
public void lambda() {
System.out.println("i like lambda4");
}
};
like.lambda();
//6.用lambda简化
like = ()->{
System.out.println("i like lambda5");
};
like.lambda();
}
}
//1.定义一个函数式接口
interface Ilike{
void lambda();
}
//2.实现类
class Like implements Ilike{
@Override
public void lambda(){
System.out.println("i like lambda");
}
}
- lambda表达式的简化以及含多个参数的lambda表达式:
package com.zcf.lambda;
public class Test2 {
public static void main(String[] args) {
//1.含参数的lambda表达式
Ilove love = (int a)->{
System.out.println("i love you->"+a);
};
love.love(1);
//2.简化1:去掉参数类型的lambda表达式
love = (a)->{
System.out.println("i love you->"+a);
};
love.love(2);
//3.简化2:去掉括号的lambda表达式
love = a->{
System.out.println("i love you->"+a);
};
love.love(3);
//4.简化3:去掉花括号的lambda表达式
love = a->
System.out.println("i love you->"+a);
love.love(4);
//5.多个参数的lambda表达式
Ilove2 love2 = (a,b,c)-> System.out.println("多参数"+a+"->"+b+"->"+c);
love2.love2(520,502,250);
/*总结:
* 简化1,2:多个参数也可以去掉参数类型,要去掉就都去掉多个参数时,小括号不能去掉
* 简化3:lambda表达式在只有一行代码的情况下才能简化为一行,如果有多行,那么就用代码块包裹
* 使用lambda表达式的前提是接口类型为函数式接口
* */
}
}
interface Ilove{
void love(int a);
}
interface Ilove2{
void love2(int a,int b ,int c);
}