多线程和Lambda表达式入门
-
什么是线程
进程:一个进程包括有操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,知道所有分守护线程都结束后才能结束。多线程能满足程序员编写高效率的程序来达到充分利用CPU的目的。
-
一个线程的生命周期
线程是一个动态执行的过程,它有一个从产生到死亡的过程。
-
新建状态:
使用new 关键字和Thread类或其子类建立一个线程对象后,该项成对象就处于新建状态。它保持这个状态直到程序start()这个线程。
-
就绪状态:
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要登台JVM里线程调度器的调度。
-
运行状态:
如果就绪状态的线程获取CPU资源,就可以执行run(),此时线程变处于运行状态了。处于运行状态的线程最为复杂,它可以编程阻塞状态、就绪状态和死亡状态。
-
阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
-
等待阻塞:运行状态中的线程执行wait()方法,使线程进入到登台阻塞状态。
-
同步阻塞:线程在获取synchronized同步锁失败(因为同步锁被其他线程占用)。
-
其他阻塞:通过调用线程的sleep()或join()发出了I/O请求时,线程就会进入阻塞状态。当sleep()状态超时,join()等待线程终止或超时,获取I/O处理完毕,线程重新转入就绪状态。
-
-
死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就会切换到终止状态。
线程的优先级
每一个Java线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。
Java线程的优先级是一个整数,其取值范围是1(Thread.MIN_PRIORITY)-10(Thread.MAM_PRIORITY)。默认情况下,每一个线程都会分配一个优先线程NORM_PRIORITY(5)。
具有较高优先级的线程对程序更重要,并且应该在低优先级的县城之前分配处理器资源。但是,线程优先级不能保证线程执行的顺序,儿啊且非常依赖于平台。
-
-
创建一个线程
-
通过继承Thread类本身
-
通过实现Runable接口
-
通过实现Callable和Future创建线程
-
①:继承java.lang.Thread类, 重写run()方法
创建一个线程的第一种方法是创建一个新的类,该类继承Thread类,然后创建一个该类的实例。
继承类必须重写run()方法,该方法是新县城的入口点。他必须调用start()方法才能执行。
该方法尽管被列为了一种多线程的实现方式,但是本质上也是实现了Runnable接口的一个实例。
package com.kkb.spring.hello.helleThread.test;
//创建线程方式:继承Thread类,重写run()方法,调用start开启线程
//总结:线程开启不一定立即执行,由CPU调度执行
public class TestThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("我在看代码"+i);
}
}
public static void main(String[] args) {
//创建一个线程对象
TestThread testThread=new TestThread();
//调用start()方法
testThread.start();
for (int i = 0; i < 20; i++) {
System.out.println("我在学习多线程"+i);
}
}
}
②:实现Runable接口,重写run()方法然后使用Thread类来包装
package com.kkb.spring.hello.helleThread.test;
//通过实现runnable接口创建线程
public class TestThread3 implements Runnable{
public static void main(String[] args) {
TestThread3 testThread3=new TestThread3();
new Thread(testThread3).start();
for (int i = 0; i < 20; i++) {
System.out.println("我在看代码"+i);
}
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("我在学习多线程"+i);
}
}
}
这两种方式都是围绕着Thread和Runable,继承Thread类把run()方法写到类中,实现Runable接口是把run()方法写到接口中,然后再用Thread类来包装,两种方式都是调用了Thread类的start()方法来启动线程的。
两者在本质上没有明显的区别,只是在外观上有很大的区别,第一种方法是继承Thread类,由于java是单继承,如果继承了Thread类,就没办法继承其它的类了,在继承上有点受制,不灵活。第二种方法就是为了解决第一种单继承不灵活的问题。所以平常使用就使用第二种方法。
其他变体写法:
public static void main(String[] args) {
//new MyThread().start();
//new Thread(new MyRunable()).start();
//匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"\t"+Thread.currentThread().getId());
}
}).start
//尾部代码块,是对内部匿名类形式的语法糖
new Thread(){
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"\t"+Thread.currentThread().getId());
}
}.start();
//Runnable是函数式接口,所以可以使用Lamda表达式
Runnable runnable=()->{
System.out.println(Thread.currentThread().getName()+"\t"+Thread.currentThread().getId());
};
new Thread(runnable).start();
}
③:实现Callable接口,重写call()方法,然后包装成java.util.concurrent.FutureTask,再然后包装成Thread callable:有返回值的线程,能取消线程,可以判断线程是否执行完毕。
package com.kkb.spring.hello.helleThread.test;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
//线程创建方式3:实现callabile接口
public class TestCallable implements Callable {
private String url;//图片地址
private String name;//文件名称
public TestCallable(String url, String name) {
this.url = url;
this.name = name;
}
@Override
public Boolean call() throws Exception {
WebDownloader1 webDownloader=new WebDownloader1();
webDownloader.doenloader(url,name);
System.out.println("下载了文件名为:"+name);
return true;
}
public static void main(String[] args) {
TestCallable t1=new TestCallable("https://img.kaikeba.com/736161901202lzci.png?imageMogr2/quality/85/format/webp","1.JPG");
TestCallable t2=new TestCallable("https://img.kaikeba.com/411121111202oauc.jpg?imageMogr2/quality/85/format/webp","2.JPG");
TestCallable t3=new TestCallable("https://img.kaikeba.com/742160111202ryal.jpg?imageMogr2/quality/85/format/webp","3.JPG");
//创建执行服务
ExecutorService service= Executors.newFixedThreadPool(3);
//执行提交
Future<Boolean> s2 = service.submit(t1);
Future<Boolean> s1 = service.submit(t2);
Future<Boolean> s3 = service.submit(t3);
//获取结果
try {
Boolean r2 = s2.get();
Boolean r3 = s1.get();
Boolean r1 = s3.get();
System.out.println(r1);
System.out.println(r2);
System.out.println(r3);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
//关闭服务
service.shutdown();
}
}
class WebDownloader1{
public void doenloader(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,doenloader方法出现问题");
}
}
}
静态代理
你:真实角色
婚庆公司:代理你,帮助你处理结婚的事
结婚:两个角色都实现结婚接口
package com.kkb.spring.hello.helleThread.test;
//静态代理模式
//真是对象和代理对象都要实现同一个接口
//代理对象要代理真是角色
//代理对象可以做很多真实对象做不了的事情
//真是对象专注于做自己的事情
public class StaticProxy {
public static void main(String[] args) {
You you=new You();//你要结婚
new Thread(()-> System.out.println("i love you")).getName();
new WeddingCompany(new You()).happyMarry();
WeddingCompany weddingCompany=new WeddingCompany(new 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() {
befor();
this.target.happyMarry();//这个是真实对象
after();
}
private void after() {
System.out.println("结婚之后收尾款");
}
private void befor() {
System.out.println("结婚之前,布置婚礼现场");
}
}
Lamda表达式
Lambda 表达式是 JDK8 的一个新特性,可以取代大部分的匿名内部类,写出更优雅的 Java 代码,尤其在集合的遍历和其他集合操作中,可以极大地优化代码结构。
-
λ希腊字母表中排序第是一位的字母,英语名称为Lambda
-
避免匿名内部类定义过多
-
其实质属于函数式编程的概念
为什么使用lambda表达式
避免匿名内部类定义过多
可以让你的代码看起来很简洁
去掉了一堆没有意义的代码,只留下了核心的逻辑
FunctionalInterface(函数式接口)
任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口。
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
对于函数式接口,我们可以通过lambda表达式来创建接口对象。
package com.kkb.spring.hello.helleThread.lamda;
/**
* 推到lambda表达式
*/
public class TestLambda {
//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();
Like2 like2 = new Like2();
like2.lambda();
//4、局部内部类
class Like3 implements ILike{
@Override
public void lambda() {
System.out.println("i like lambda3");
}
}
Like3 like3 = new Like3();
like3.lambda();
//5、匿名内部类
like=new ILike(){
@Override
public void lambda() {
System.out.println("i like lambda4");
}
};
like.lambda();
//6、用lambad简化
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");
}
}