多线程的创建,并发,静态代理,Lambda表达式
程序是指令和数据的有序集合,本身没有任何运行的含义。是一个静态的概念。
在操作系统中运行的程序就是进程(Process),如:QQ,播放器,游戏等等。
进程是程序的一次执行过程,是一个动态的概念,是系统资源分配的单位。
一个进程可以有多个线程(Thread),如视频中同时听到声音,看图像,看弹幕等等。线程是CPU调度和执行的单位
注意:很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器。
如果是模拟出来的多线程,即在一个cpu的情况下,在同一时间点,cpu只能执行一个代码,因为切换的很快,所以就有同时执行的错觉。
线程是独立的执行路径。
在程序运行时,即使自己没有创建线程,后台也会有多个线程,如主线程,gc线程。gc:垃圾回收机制。守护线程。
main()为主线程,是系统的入口。
在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为干预的。
对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制。
线程会带来额外的开销,如cpu调度时间,并发控制开销。
每个线程在自己的工作内存交互,内存控制不当会造成数据不一致。
线程不一定立即执行,由cpu调度安排。
1.方式一 :继承Thread类
步骤:自定义线程类继承Thread类
重写run()方法,编写线程执行体
创建线程对象,调用start()方法启动线程。
public class ThreadDemo extends Thread{ //1继承Thread类 public static void main(String[] args) { ThreadDemo threadDemoo = new ThreadDemo(); //3.创建线程对象 threadDemoo.start(); //调用start方法,开启该线程 //主线程 for (int i = 0;i<2000;i++){ System.out.println("我在看弹幕"+i); } } @Override public void run() { //2.重写run方法。线程体 for (int i = 0;i<2000;i++){ System.out.println("我在听音乐。"+i); } } }
练习:
实现多线程同步下载图片。
引入一个工具类:implementation "commons-io:commons-io:2.7"
为了使用里面的copyURLToFile(new URL(url),new File(name)); ------>将一个网络地址的内容变成文件。
步骤:
(1)创建一个下载器类 WebDownLoader
(2)创建一个多线程类,继承Thread。
代码如下:
package com.java.multithreading; import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.net.URL; //练习thread,实现多线程同步下载图片 public class TestThread extends Thread{ private String url; private String name; //构造器 public TestThread(String url ,String name){ this.url = url; this.name = name; } @Override public void run() { WebDownLoader webDownLoader = new WebDownLoader(); webDownLoader.downloader(url,name); System.out.println("下载了"+name+"文件"); } public static void main(String[] args) { TestThread testThread01 = new TestThread("https://img2.baidu.com/it/u=450745774,808114680&fm=253&fmt=auto&app=138&f=JPEG?w=370&h=500","xixi01.jpg"); TestThread testThread02 = new TestThread("https://img0.baidu.com/it/u=1145082949,2832027117&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=914","xix02.jpg"); TestThread testThread03 = new TestThread("http://t14.baidu.com/it/u=2571612918,2603191067&fm=224&app=112&f=JPEG?w=500&h=333","xixi03.jpg"); testThread01.start(); testThread02.start(); testThread03.start(); } } //下载器 class WebDownLoader{ public void downloader(String url,String name){ try { FileUtils.copyURLToFile(new URL(url),new File(name)); //将一个网络地址变成文件 } catch (IOException e) { e.printStackTrace(); System.out.println("IO异常,downloader方法出现问题"); } } }
运行结果如下:
线程是同步执行的,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为干预的。
Thread类是实现了Runnable接口的,我们继承Thread类,其实也就是实现了Runnable接口,然后重写了run方法。
通过new ThreadDemo().start();开启多线程, 它执行了Thread的无参构造方法
2.方式二:实现Runnable接口
步骤:自定义线程类实现Runnable接口
实现run()方法,编写线程执行体
创建线程对象,调用start()方法启动线程。
package com.java.multithreading; public class RunnableDemo implements Runnable{ @Override public void run() { //线程体 for (int i = 0;i<200;i++){ System.out.println("我在听音乐。"+i); } } public static void main(String[] args) { RunnableDemo runnableDemo = new RunnableDemo(); //创建Runnable接口的实现类对象 Thread thread = new Thread(runnableDemo); //创建线程对象,通过线程对象来开启我们的线程 thread.start(); //开启线程 //可以将上面代码简写为: new Thread(runnableDemo).start(); ; //调用start方法,开启该线程 //主线程 for (int i = 0;i<1000;i++){ System.out.println("我在看弹幕"+i); } } }
练习:
实现多线程同步下载图片。
此处只需将方式一的代码修改两步:
将继承Thread类改为实现Runnable接口。
将new ThreadDemo().start改为RunnableDemo runnableDemo = new RunnableDemo(); new Thread(runnableDemo).start();
引入一个工具类:implementation "commons-io:commons-io:2.7"
为了使用里面的copyURLToFile(new URL(url),new File(name)); ------>将一个网络地址的内容变成文件。
步骤:
(1)创建一个下载器类 WebDownLoader。这里直接用方式一的下载器。
(2)创建一个多线程类,实现Runnable接口。
代码如下:
package com.java.multithreading; import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.net.URL; public class TestRunnable implements Runnable{ private String url; private String name; //构造器 public TestRunnable(String url ,String name){ this.url = url; this.name = name; } @Override public void run() { WebDownLoader webDownLoader = new WebDownLoader(); webDownLoader.downloader(url,name); System.out.println("下载了"+name+"文件"); }
public static void main(String[] args) {
TestRunnable testRunnable01 = new TestRunnable("https://img2.baidu.com/it/u=450745774,808114680&fm=253&fmt=auto&app=138&f=JPEG?w=370&h=500","xixi01.jpg");
TestRunnable testRunnable02 = new TestRunnable("https://img0.baidu.com/it/u=1145082949,2832027117&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=914","xix02.jpg");
TestRunnable testRunnable03 = new TestRunnable("http://t14.baidu.com/it/u=2571612918,2603191067&fm=224&app=112&f=JPEG?w=500&h=333","xixi03.jpg");
// testThread01.start();
// testThread02.start();
// testThread03.start();
new Thread(testRunnable01).start();
new Thread(testRunnable02).start();
new Thread(testRunnable03).start();
}
}
结果如下:
两者比较:
继承Thread类:
- 子类继承Thread类具备多线程能力
- 启动线程:子类对象.start()
- 不建议使用:避免oop(面向对象编程)单继承局限性
实现Runnable接口:
- 实现接口Runnable具有多线程能力
- 启动线程:new Thread(Runnable接口的实现类对象).start()
- 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用。
3.方式三:实现Callable接口
步骤:
实现Callable接口,需要返回值类型
重写call方法,需要抛出异常
创建目标对象 t1
创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
提交执行:Future<Boolean> result = ser.submit(t1);
获取结果:boolean r1 = result.get();
关闭服务:ser.shutdownNow();
package com.java.multithreading; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class CallableDemo implements Callable<Boolean>{ private String url; private String name; public CallableDemo(String url,String name){ this.url = url; this.name = name; } @Override public Boolean call() throws Exception { WebDownLoader webDownLoader = new WebDownLoader(); webDownLoader.downloader(url,name); System.out.println("下载了文件:"+name); return true; } public static void main(String[] args) throws ExecutionException, InterruptedException { CallableDemo callableDemo1 = new CallableDemo("https://img2.baidu.com/it/u=450745774,808114680&fm=253&fmt=auto&app=138&f=JPEG?w=370&h=500","xixi01.jpg"); CallableDemo callableDemo2 = new CallableDemo("https://img0.baidu.com/it/u=1145082949,2832027117&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=914","xixi02.jpg"); CallableDemo callableDemo3 = new CallableDemo("http://t14.baidu.com/it/u=2571612918,2603191067&fm=224&app=112&f=JPEG?w=500&h=333","xixi03.jpg"); //创建执行服务 ExecutorService ser = Executors.newFixedThreadPool(1); //提交线程 Future<Boolean> result1 = ser.submit(callableDemo1); Future<Boolean> result2 = ser.submit(callableDemo2); Future<Boolean> result3 = ser.submit(callableDemo3); //获取结果 boolean r1 = result1.get();//此处的异常直接抛出 boolean r2 = result2.get(); boolean r3 = result3.get(); //关闭服务 ser.shutdownNow(); } }
运行结果:
4.初始并发问题
多个线程同时操作同一个对象,如购买过车票。
package com.java.multithreading; public class //初识并发问题 。多个线程同时操作同一个对象,如购买过车票。线程紊乱,不安全。 ConcurrencyDemo implements Runnable{ private int ticketNums = 5; //总票数 @Override public void run() { while (true){ if (ticketNums <= 0){ break; } System.out.println(Thread.currentThread().getName()+"---->拿到了第"+ticketNums--+"票"); } } public static void main(String[] args) { ConcurrencyDemo concurrencyDemo = new ConcurrencyDemo(); //多条线程操作同一个实现类。即三条线程一起去抢总数为10的火车票。 new Thread(concurrencyDemo,"一号").start(); new Thread(concurrencyDemo,"二号").start(); new Thread(concurrencyDemo,"三号").start(); } }
运行结果如下:
会出现一号将票全部拿完的情况。
现在我们模拟延时看看:
package com.java.multithreading; public class //初识并发问题 。多个线程同时操作同一个对象,如购买过车票。线程紊乱,不安全。 ConcurrencyDemo implements Runnable{ private int ticketNums = 5; //总票数 @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) { ConcurrencyDemo concurrencyDemo = new ConcurrencyDemo(); //多条线程操作同一个实现类。即三条线程一起去抢总数为10的火车票。 new Thread(concurrencyDemo,"一号").start(); new Thread(concurrencyDemo,"二号").start(); new Thread(concurrencyDemo,"三号").start(); } }
运行结果如下:
出现了紊乱。此刻的线程就是不安全的。
5.静态代理模式
package com.java.multithreading; //静态代理模式对比Thread /* 举例:结婚 结婚需要实现结婚的接口。 结婚不必新娘新郎亲自布置现场,可让婚庆公司帮忙 结婚时只需新人到场举行婚礼即可。 注意:真实对象和代理对象都要实现同一个接口 */ public class StaticProxy { public static void main(String[] args) { Groom groom = new Groom(); //WeddingCompany实现了Marry接口,Thread实现Runnable接口; new WeddingCompany(groom).Wedding(); new Thread(() -> System.out.println("like")).start(); //Thread类是一个代理,代理中间的真实对象,然后调用start方法 } } //结婚的接口 interface Marry{ void Wedding();//婚礼 } //真实角色,实现结婚接口,目的是参加婚礼 class Groom implements Marry{ @Override public void Wedding() { System.out.println("新郎参加婚礼"); } } //代理角色,实现结婚接口,目的是筹备婚礼 class WeddingCompany implements Marry{ private Marry target; public WeddingCompany(Marry target){ this.target = target; } @Override public void Wedding() { System.out.println("婚庆公司筹备婚礼"); this.target.Wedding();//然后真实对象来参加婚礼 } }
6.Lambda表达式
避免匿名内部类过多,其实质属于函数式编程的概念。
函数式接口的定义:
- 任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口。
如Runnable接口:
package java.lang; @FunctionalInterface public interface Runnable { void run(); }
- 对于函数式接口,我们可以通过Lamda表达式来创建该接口对象。
推导 lambda表达式:
原始代码:
package com.java.multithreading; //推导Lamda表达式 public class LambdaDemo { public static void main(String[] args) { IStudy iStudy = new IStudy(); iStudy.lambda(); } } //定义一个函数式接口 interface Study{ void lambda();//在接口里的方法,就是抽象方法 } //实现类 class IStudy implements Study{ @Override public void lambda() { System.out.println("我在学习"); } }
静态内部类
package com.java.multithreading; //推导Lamda表达式 public class LambdaDemo { //实现类 static class IStudy implements Study{ @Override public void lambda() { System.out.println("我在学习"); } } public static void main(String[] args) { IStudy iStudy = new IStudy(); iStudy.lambda(); } } //定义一个函数式接口 interface Study{ void lambda();//在接口里的方法,就是抽象方法 }
局部内部类
package com.java.multithreading; //推导Lamda表达式 public class LambdaDemo { public static void main(String[] args) { //实现类 class IStudy implements Study{ @Override public void lambda() { System.out.println("我在学习"); } } IStudy iStudy = new IStudy(); iStudy.lambda(); } } //定义一个函数式接口 interface Study{ void lambda();//在接口里的方法,就是抽象方法 }
匿名内部类
package com.java.multithreading; //推导Lamda表达式 public class LambdaDemo { public static void main(String[] args) { //实现类 Study iStudy = new Study() { @Override public void lambda() { System.out.println("我在学习"); } }; iStudy.lambda(); } } //定义一个函数式接口 interface Study{ void lambda();//在接口里的方法,就是抽象方法 }
用lambda简化
package com.java.multithreading; //推导Lamda表达式 public class LambdaDemo { public static void main(String[] args) { //实现类 Study iStudy = ()->{ System.out.println("我在学习"); }; iStudy.lambda(); } } //定义一个函数式接口 interface Study{ void lambda();//在接口里的方法,就是抽象方法 }
说明:非原创,跟着b站狂神敲的