程序是指令和数据的有序集合,本身没有任何运行的含义。是一个静态的概念。

在操作系统中运行的程序就是进程(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站狂神敲的

多线程的创建,并发,静态代理,Lambda表达式的更多相关文章

  1. 第40天学习打卡(静态代理 Lambda表达式 线程状态 线程同步 同步方法)

    静态代理  package com.kuang.demo03; //静态代理模式总结 //真实对象和代理对象都要实现同一个接口 //代理对象要代理真实角色 //好处:  //代理对象可以做很多真实对象 ...

  2. 01 语言基础+高级:1-7 异常与多线程_day07 【线程池、Lambda表达式】

    day07[线程池.Lambda表达式] 主要内容 等待与唤醒案例 线程池 Lambda表达式 教学目标 -[ ] 能够理解线程通信概念-[ ] 能够理解等待唤醒机制-[ ] 能够描述Java中线程池 ...

  3. 第三章 Lambda表达式

    第三章 Lambda表达式 3.1 函数式编程思想概述 在数学中,函数就是有输入量.输出量的一套计算方案,也就是“拿什么东西做什么事情”.相对而言,面向对象过分强调“必须通过对象的形式来做事情”,而函 ...

  4. Util应用程序框架公共操作类(八):Lambda表达式公共操作类(二)

    前面介绍了查询的基础扩展,下面准备给大家介绍一些有用的查询封装手法,比如对日期范围查询,数值范围查询的封装等,为了支持这些功能,需要增强公共操作类. Lambda表达式公共操作类,我在前面已经简单介绍 ...

  5. 函数式编程--lambda表达式对比匿名内部类

    从前面的整理中我们看出了,Lambda表达式其实是匿名内部类的一种简化,因此它可以部分取代匿名内部类. 1,Lambda表达式与匿名内部类存在如下相同点: 1),Lambda表达式与匿名内部类一样,都 ...

  6. JavaSE Lambda表达式(JDK1.8新特性)

    在前面有一篇写到了Lambda表达式,现在可以给你们介绍什么是Lambda表达式 现在有很多老程序员都不喜欢这个函数式编程思想 主要就一点 : 老程序员习惯了 面向过程 写程序,而Lambda表达式是 ...

  7. Lambda01 编程范式、lambda表达式与匿名内部类、函数式接口、lambda表达式的写法

    1 编程范式 主要的编程范式有三种:命令式编程,声明式编程和函数式编程. 1.1 命令式编程 关注计算机执行的步骤,就是告诉计算机先做什么后做什么 1.2 声明式编程 表达程序的执行逻辑,就是告诉计算 ...

  8. Java学习笔记-Lambda表达式

    Lambda表达式支持将代码块作为方法参数,Lambda表达式允许使用简洁的代码来创建只有一个抽象方法的接口(这种接口被称为函数是接口)的实例 意义 自从Java 8开始,Java支持Lambda表达 ...

  9. Java疯狂讲义笔记——Lambda表达式

    Java8新增的Lambda表达式 [特性]支持将代码块作为方法参数,Lambda表达式允许使用更简洁的代码来创建只有一个抽象方法的接口(这种接口被称为函数式接口)的实例. [组成部分]1,形参列表 ...

  10. Java——Lambda表达式

    一.Lambda表达式入门 我们先来看一段代码:匿名内部类的方式实现参数的传递 interface Command{ public abstract void test(); } public cla ...

随机推荐

  1. GeneralUpdate20220323里程碑版本发布

    大家好我是juster,GeneralUpdate的开源项目作者.这次将发布GeneralUpdate里程碑版本,该版本发生了巨大改变历时4个月的时间终于要和大家见面了.开源不易希望大家能多多支持.可 ...

  2. BTree和B+Tree 简单区别

    本篇作用于各种树之间的区别,非算法详细介绍,只是给我们这种非科班出身的一种大概的印象,现在网上更多是讲各种树的怎么实现的细节问题,本篇不涉及那么高深,如果详细了解可以查阅他人的资料,很多大神已经说的很 ...

  3. Java连接ArtemisMQ,出现Timed out waiting to receive cluster topology. Group:null异常

    完整异常内容:org.springframework.jms.UncategorizedJmsException: Uncategorized exception occurred during JM ...

  4. mysql行锁、表锁。乐观锁,悲观锁

    锁定用于确保事务完整性和数据库一致性. 锁定可以防止用户读取其他用户正在更改的数据,并防止多个用户同时更改相同的数据. 如果不使用锁定,数据库中的数据可能在逻辑上变得不正确,而针对这些数据进行查询可能 ...

  5. Homebrew安装环境

    p.p1 { margin: 0; font: 11px Menlo; color: rgba(0, 0, 0, 1) } span.s1 { font-variant-ligatures: no-c ...

  6. java-关于getResourceAsStream

    1111class.getClassLoader().getResourceAsStream InputStream ips = testResource.class.getClassLoader() ...

  7. 《手把手教你》系列基础篇(八十四)-java+ selenium自动化测试-框架设计基础-TestNG日志-上篇(详解教程)

    1.简介 TestNG还为我们提供了测试的记录功能-日志.例如,在运行测试用例期间,用户希望在控制台中记录一些信息.信息可以是任何细节取决于目的.牢记我们正在使用Selenium进行测试,我们需要有助 ...

  8. 《剑指offer》面试题4:替换空格

    面试题4:替换空格 题目:请实现一个函数,把字符串中的每个空格替换成"%20",例如输入"We are happy.",则输出"we%20are%20 ...

  9. PCB常用低速、高速板材参数性能(2)

  10. 前端入门-day2(常见css问题及解答)

    写在前面 今天是入门前端的day2, 小伙伴们应该已经看了一些HTML的基础和CSS的基础了,是不是遇到了很多关于CSS的问题呢.因为HTML很少有太复杂的问题,所以直接写一篇关于CSS的常见问题及解 ...