JavaSE自学笔记Real_006(多线程基础)
多线程基础概念
1、程序时指令与数据的有序集合,其本身没有任何运行的含义,是一个静态的概念;
2、进程值执行程序的依次执行过程,他是一个动态的概念。是系统资源分配的单位;
3、在一个进程中包含有若干个线程,一个进程中至少有袷线程,线程时CPU调度和执行的单位。
注意:很多多线程时模拟出来的,真正的多线程是指有多个CPU,即多核,如服务器,如果时模拟出来的多线程,即在一个CPU的情况下,在同一个时间点,CPU只能执行一个代码,因为切换的很快,所以就会产生同时执行的错觉。
多线程的创建
创建方式一:继承Thread类
//创建线程的方式一:继承Thread类,重写run()方法,调用start开启线程
public class TestThread1 extends Thread{
/**
* If this thread was constructed using a separate
* {@code Runnable} run object, then that
* {@code Runnable} object's {@code run} method is called;
* otherwise, this method does nothing and returns.
* <p>
* Subclasses of {@code Thread} should override this method.
*
* @see #start()
* @see #stop()
* @see #Thread(ThreadGroup, Runnable, String)
*/
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 20; i++) {
System.out.println("我在看代码--"+i);
}
}
public static void main(String[] args) {
//main线程 主线程
//创建一个线程对象
TestThread1 testThread1 = new TestThread1();
//调用start()方法开启线程
for (int i = 0; i < 20; i++) {
System.out.println("我在学习多线程--"+i);
}
}
}
小练习:多线程同时下载图片
package com.ThreadLearn.Demo002;
import com.ThreadLearn.Demo001.TestThread1;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
//练习Thread,实现多线程同步下载图片
public class TestThread2 extends Thread{
private String url; //网络图片的地址
private String name; //保存的文件名
public TestThread2(String url, String name){
this.url = url;
this.name = name;
}
/**
* If this thread was constructed using a separate
* {@code Runnable} run object, then that
* {@code Runnable} object's {@code run} method is called;
* otherwise, this method does nothing and returns.
* <p>
* Subclasses of {@code Thread} should override this method.
*
* @see #start()
* @see #stop()
* @see #Thread(ThreadGroup, Runnable, String)
*/
@Override
public void run() {
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url, name);
System.out.println("下载了文件。名字为:" + name);
}
public static void main(String[] args){
TestThread2 t1 = new TestThread2("https://www.1fzz.com/content/uploadfile/202007/0ac61594015036.jpg", "1.jpg");
TestThread2 t2 = new TestThread2("https://www.1fzz.com/content/uploadfile/202007/0ac61594015036.jpg", "2.jpg");
TestThread2 t3 = new TestThread2("https://www.1fzz.com/content/uploadfile/202007/0ac61594015036.jpg", "3.jpg");
t1.start();
t2.start();
t3.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方法出现问题!");
}
}
}
创建方式二:Runnable接口实现
import com.ThreadLearn.Demo001.TestThread1;
//创建线程的方式二:实现runnable接口,重写run方法,执行线程须丢入runnable接口实现类,利用Thread类调用start方法
public class TestThread3 implements Runnable{
/**
* If this thread was constructed using a separate
* {@code Runnable} run object, then that
* {@code Runnable} object's {@code run} method is called;
* otherwise, this method does nothing and returns.
* <p>
* Subclasses of {@code Thread} should override this method.
*
* @see #start()
* @see #stop()
* @see #Thread(ThreadGroup, Runnable, String)
*/
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 20; i++) {
System.out.println("我在看代码--"+i);
}
}
public static void main(String[] args) {
//创建runnable接口的实现类对象
TestThread3 testThread3 = new TestThread3();
//创建线程对象,通过线程对象来开启线程
//Thread thread = new Thread(testThread3);
//thread.start();
new Thread(testThread3).start();
for (int i = 0; i < 20; i++) {
System.out.println("我在学习多线程--"+i);
}
}
}
小结
1、继承Thread类:
(1)子类继承Thread类具备不哦线程能力
(2)启动线程:子类对象.strat();
(3)不建议使用,因为只能进行单继承
2、实现Runnable接口
(1)实现接口Runnable具有多线程能力
(2)启动线程:传入目标对象+Thread的对象.start();
(3)推荐使用,避免单继承的局限性,灵活方便,方便同一个对象被多个线程使用
什么是并发问题
小例子:买火车票的问题
//多个线程同时操作同一个对象
//买火车票的例子
//发现问题:多个线程同时操作同一个资源,线程不安全,数据紊乱,这种现象称为并发问题
public class TestThread4 implements Runnable{
//票数
private int ticketNums = 10;
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see Thread#run()
*/
@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) {
TestThread4 ticket = new TestThread4();
new Thread(ticket, "小明").start();
new Thread(ticket, "老师").start();
new Thread(ticket, "黄牛").start();
}
}
运行结果:
可以看出,同一张票被多个人拿到,显然不符合要求
小明-->拿到了第8票
黄牛-->拿到了第9票
老师-->拿到了第10票
小明-->拿到了第6票
老师-->拿到了第5票
黄牛-->拿到了第7票
黄牛-->拿到了第4票
小明-->拿到了第4票
老师-->拿到了第3票
小明-->拿到了第2票
黄牛-->拿到了第2票
老师-->拿到了第1票
Process finished with exit code 0
创建方式三:实现callable接口
实现Callable接口:
1、实现callable接口,需要返回值类型
2、需要重写call方法,需要抛出异常
3、创建目标对象
4、创建执行服务ExecutorService ser = Executors.newFixedThreadPool(1);
5、提交执行 Future<Boolean> r1 = ser.submit(t1);
6、获取结果 boolean rs1 = r1.get();
7、关闭服务 ser.shutdown();
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
//线程的创建方式三:实现callable接口
/*
callable的好处:
1、可以定义返回值
2、可以抛出异常
*/
public class TestThread5 implements Callable<Boolean> {
private String url; //网络图片的地址
private String name; //保存的文件名
public TestThread5(String url, String name){
this.url = url;
this.name = name;
}
/**
* If this thread was constructed using a separate
* {@code Runnable} run object, then that
* {@code Runnable} object's {@code run} method is called;
* otherwise, this method does nothing and returns.
* <p>
* Subclasses of {@code Thread} should override this method.
*
* @see #start()
* @see #stop()
* @see #Thread(ThreadGroup, Runnable, String)
*/
@Override
public Boolean call() {
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url, name);
System.out.println("下载了文件。名字为:" + name);
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestThread5 t1 = new TestThread5("https://www.1fzz.com/content/uploadfile/202007/0ac61594015036.jpg", "4.jpg");
TestThread5 t2 = new TestThread5("https://www.wangyetuku.com/d/file/2019/016a6167e3df41c09e560b8336451075.jpg", "5.jpg");
TestThread5 t3 = new TestThread5("https://www.1fzz.com/content/uploadfile/202007/0ac61594015036.jpg", "6.jpg");
//创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> r1 = ser.submit(t1);
Future<Boolean> r2 = ser.submit(t2);
Future<Boolean> r3 = ser.submit(t3);
//获取结果
boolean rs1 = r1.get();
boolean rs2 = r2.get();
boolean rs3 = r3.get();
//关闭服务
ser.shutdown();
}
}
//下载器
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方法出现问题!");
}
}
}
静态代理模式
//静态代理模式总结
//真实对象和代理对象都要实现接口
//代理对象要实现真实角色
//好处:
//代理对象可以做某些真实对象做不了的事情
//真实对象专注于做自己的事情
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{
@Override
public void HappyMarry() {
System.out.println("I am going to marry!");
}
}
//重点分析这一段
//代理角色
class WeddingCompany implements Marry{
private Marry target;
public WeddingCompany(Marry target){
this.target = target;
}
private void before(){
System.out.println("Prepare the wedding.");
}
private void after(){
System.out.println("Pay for the money.");
}
@Override
public void HappyMarry() {
//============================
before();
this.target.HappyMarry();
after();
//============================
}
}
Lambda表达式基础(λ表达式)
为什么要用:
1、避免匿名内部类定义过多
2、可以让你的代码看起来更简洁
3、去掉了一些没有意义的代码,只留下核心的逻辑
推到lambda表达式,看他是怎么演变过来的:
1、外部实现类:
public class TestLambda1 {
public static void main(String[] args) {
ILike like = new Like();
like.lambda();
}
}
//1、定义一个函数式接口
interface ILike{
void lambda();
}
//2、实现类
class Like implements ILike{
@Override
public void lambda() {
System.out.println("I like lambda!");
}
}
2、静态内部类
public class TestLambda1 {
//2、静态内部类
static class Like implements ILike{
@Override
public void lambda2() {
System.out.println("I like lambda2!");
}
}
public static void main(String[] args) {
ILike like = new Like();
like.lambda2();
}
}
//1、定义一个函数式接口
interface ILike{
void lambda2();
}
3、局部内部类(在main方法里面)
public class TestLambda1 {
public static void main(String[] args) {
//3、局部内部类
class Like implements ILike{
@Override
public void lambda3() {
System.out.println("I like lambda3!");
}
}
ILike like = new Like();
like.lambda3();
}
}
//1、定义一个函数式接口
interface ILike{
void lambda3();
}
4、匿名内部类
public class TestLambda1 {
public static void main(String[] args) {
//4、匿名内部类
ILike like = new ILike() {
@Override
public void lambda4() {
System.out.println("I like lambda4.");
}
};
like.lambda4();
}
}
//1、定义一个函数式接口
interface ILike{
void lambda4();
}
//=====================================================
//简化写法:
//=====================================================
public class TestLambda1 {
public static void main(String[] args) {
//4、匿名内部类
Runnable like = new Runnable() {
@Override
public void run() {
System.out.println("I like lambda4.");
}
};
like.run();
}
}
5、lambda表达式简化
public class TestLambda1 {
public static void main(String[] args) {
//5、lambda表达式
Runnable like = () -> {
System.out.println("I like lambda5.");
};
like.run();
}
}
带参数的lambda表达式简化
public class TestLambda2 {
//带参数的接口实现简化
/*
总结:
1、lambda表达式只能在只有有一行代码的情况下才能简化为银行,去电话括号,弱国有多行大妈,需要代码块包裹
2、使用该表达式前提是为函数型接口,即接口中只有一个函数方法
3、多个参数也可以去掉参数类型,要去掉就都去掉,必须加上括号
*/
public static void main(String[] args) {
ILove love = (int a)-> {
System.out.println("I love you --> " + a);
};
love.love(520);
//简化1:简化参数类型
love = (a)-> {
System.out.println("I love you --> " + a);
};
love.love(520);
//简化2:去掉括号
love = a -> {
System.out.println("I love you --> " + a);
};
love.love(521);
//简化3:去掉花括号
love = a -> System.out.println("I love you --> " + a);
love.love(1314);
}
}
interface ILove{
void love(int a);
}