一、多线程的实现

1.继承Thread类

​ a.子类继承Thread类具备多线程能力

​ b.启动线程:子类对象.start()

c.不建议使用:避免OOP单继承局限性

package com.tang;
/*
* 多线程学习的第一个demo
* */
public class Demoted01 extends Thread {
public void run(){
// run方法线程体
for (int i = 0; i < 20; i++) {
System.out.println("我在看代码---"+i);
}
} public static void main(String[] args) {
// main线程,主线程 // 创建一个线程对象
Demoted01 demoted01 = new Demoted01();
// 调用start() 方法开启线程
demoted01.start(); for (int j = 0; j < 30; j++) {
System.out.println("我在学习多线程---"+j);
}
}
}

2.实现Runnable接口

​ a.实现接口Runnable具有多线程能力

​ b.启动线程:传入目标对象+Thread对象.start()

c.推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用

实现步骤:

​ 1.创建一个Runnable接口的实现类。

​ 2.在实现类中重写Runnable接口的run方法,设置线程任务。

​ 3.创建一个Runnable接口的实现类对象

​ 4.创建Thread类对象,构造方法中传递Runnable接口的实现类对象。

​ 5,。调用Thread类中的start方法,开启新的线程执行方法。

package com.tang;
/*
*1.定义MyRunnable类实现Runnable接口
*2.实现run方法,编写线程执行体
*3.创建线程对象,调用start()方法启动线程
*
*
* 推荐使用的创建线程的方式
* 实现runnable接口,重写run方法,=
* */
public class DemoRunnable implements Runnable {
@Override
public void run() {
// run方法线程体
for (int i = 0; i < 20; i++) {
System.out.println("我在看代码---"+i);
}
} public static void main(String[] args) {
// 创建runnable接口的实现类对象
DemoRunnable demoRunnable = new DemoRunnable();
// 创建线程对线,通过线程对象来开启我们的线程,代理
new Thread(demoRunnable).start(); for (int i = 0; i < 30; i++) {
System.out.println("我在学习多线程---"+i);
}
}
}

//一份资源
startThread4 station = new StartThread4(); //多个代理
new Thread(station,"小明").start();
new Thread(station,"老师").start();
new Thread(station,"小红").start();

2.1实现Runnable接口创建多线程程序的好处:

​ 1.避免了单继承的局限性

​ 一个类只能继承一个类,类继承了Thread类就不能继承其他的类。

​ 实现了Runnable接口,还可以继承其他的类,实现其他的接口。

​ 2.增强了程序的扩展性,降低了程序的耦合性(解耦)

​ 实现Runnable接口的方式,把设置线程任务和开启新线程进行了分离(解耦)。

​ 实现类中,重写了run方法:用来设置线程任务

​ 创建Thread类对象,调用start方法:用来开启新线程。

二、匿名内部内实现线程的创建

1.匿名内部类作用:简化代码

​ 把子类继承父类,重写父类方法,创建子类对象合一步完成。

​ 把实现类接口,重写接口中的方法,创建实现类对象合一步完成。

​ 匿名内部类的最终产物:子类/实现类对象,而这个类没有名字。

2.格式:

new 父类/接口(){
重复父类/接口中的方法
}

3.例子

package DemoInnerClass;

/*
* 匿名内部内创建Thread多线程
* */ public class DemoMain01 {
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("JAVA,你好"+i);
}
}
}.start();
test();
} private static void test() {
for (int i = 0; i < 30; i++) {
System.out.println("HelloWorld"+i);
}
} }
package DemoInnerClass;
/*
* 匿名内部类创建Runnable多线程
* */
public class DemoMain02 {
public static void main(String[] args) {
Runnable ra = new Runnable() { @Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("HelloWorld"+i);
}
}
};
new Thread(ra).start(); test2();
} private static void test2() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"--->"+i);
}
}
}

三、线程安全问题

3.1使用代码块解决线程安全

格式:

synchronized(锁对象){
可能会出现线程安全问题的代码(访问了共享数据)
}

【注意】:

​ 1.通过代码块中的锁对象,可以使用任意的对象。

​ 2.但是必须保证多个线程使用的锁对象是同一个。

​ 3.锁对象作用:把同步代码块锁住,只让一个县城在同步代码块中执行。

实现类代码:

package LockObject;

public class RunnableImpl implements Runnable {
// 定义一个多线程的共享资源
private int ticket = 100;
//定义一个锁对象
Object o1 = new Object();
@Override
public void run() {
while (true) {
synchronized (o1){
if (ticket > 0) {
// sleep提高出现安全问题的概率
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "--->正在售卖第" + ticket + "张票!");
ticket--;
}
}
}
}
}

3.2使用同步方法解决线程安全问题

使用步骤:

​ 1.把访问了共享数据的代码抽取出来,放到方法中。

​ 2.在方法上添加synchronized修饰符

格式:定义方法的格式:

修饰符 synchronized 返回值类型 方法名称(参数列表){
可能会出现线程安全问题的代码(访问了共享数据的代码)
}

实现类代码:

package DemoMethod;

public class RunnableImpl implements Runnable {
// 定义一个多线程的共享资源
private int ticket = 100; @Override
public void run() {
while (true) {
run1();
}
}
public synchronized void run1(){
if (ticket > 0) {
// sleep提高出现安全问题的概率
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "--->正在售卖第" + ticket + "张票!");
ticket--;
}
}
}

3.3使用lock锁解决线程安全问题

java.until.concurrent.locks.lock接口

lock实现提供了比使用synchronized方法和语句可获得的更广泛的锁定操作。

lock接口中的方法:

​ void lock()获取锁

​ void unlock() 释放锁

java.until.concurrent.locks.ReentrantLock implements Lock接口

使用步骤:

​ 1.在成员位置创建一个ReentrantLock对象

​ 2.在可能会出现安全问题的代码前调用lock接口中的方法lock获取锁。

​ 3.在可能会出现安全问题的代码后调用lock接口中的方法unlock释放锁。

实现类的代码:

package DemoLock;

import java.util.concurrent.locks.ReentrantLock;

public class RunnableImpl implements Runnable {
// 定义一个多线程的共享资源
private int ticket = 100; //创建一个ReentrantLock对象
ReentrantLock r1 = new ReentrantLock();
@Override
public void run() {
while (true) {
r1.lock();
if (ticket > 0) {
// sleep提高出现安全问题的概率
try {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName() + "--->正在售卖第" + ticket + "张票!");
ticket--;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
r1.unlock();
} }
}
}
}

调用方法的代码:

package DemoLock;

public class DemoMain {
public static void main(String[] args) {
RunnableImpl ra = new RunnableImpl();
Thread mt1 = new Thread(ra);
Thread mt2 = new Thread(ra);
Thread mt3 = new Thread(ra);
mt1.start();
mt2.start();
mt3.start();
}
}

四、线程之间的通信

4.1等待唤醒案例

​ 创建一个顾客线程(消费者):告知老板要的包子的种类和数量,调用wait方法,放弃cpu的执行,进入到WAITING状态(无线等待)

​ 创建一个老板线程(生产者):花了5秒做包子,做好包子之后,调用notify方法,唤醒顾客吃包子。

【注意】:

​ 顾客和老板线程必须使用同步代码块包裹起来,保证等待和唤醒只能有一个在执行。

​ 同步使用的锁对象必须保证唯一。

​ 只有锁对象才能调用wait和notify方法

Object类中的方法:

​ void wait()

​ 在其他线程调用此对象的notify方法或notifyALL方法,导致当前线程等待。

​ void notify()

​ 唤醒在此对象监视器上等待的单个线程。

​ 会继续执行wait方法之后的代码。

4.2计时等待

​ 1.使用sleep(long m)方法,在毫秒值结束之后,线程睡醒进入到Runnable/Blocked状态。

​ 2.使用wait(long m)方法,wait方法如果在毫秒值结束之后,还没有被notify唤醒,就会自动醒来,线程睡醒进入到Runnable/Blocked状态。

唤醒的方法:

​ void notify() 唤醒在此对象监视器上等待的单个线程。

​ void notifyALL() 唤醒在此对象监视器上等待的所有线程。

五、线程池

5.1线程池:JDK1.5以后提供的:

​ java.util.concurrent.Executors:线程池的工厂类,用来生成线程池。

5.2Executors类中的静态方法:

​ static ExecutorService newFixedThreadPool(int nThreads) 创建一个指定线程数量的线程池。

​ 参数:

​ int nThreads:创建线程池中包含的线程数量。

​ 返回值:

​ ExecutorService接口:返回的是ExecutorService接口的实现类对象,我们可以使用ExecutorService接口接收(面向接口编程)

5.3java.util.concurrent.ExecutorService:线程池接口

​ 用来从线程池中获取线程,调用start方法,执行线程任务。

​ submit(Runnable task) 提交一个Runnable任务用于执行。

​ 关闭/销毁线程池的方法

​ void shutdown()

5.4线程池的使用步骤:

​ 1.使用线程池的工厂类Executors里面提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池

​ 2.创建一个类,实现Runnable接口,重写run方法,设置线程任务

​ 3.调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法

​ 4.调用ExecutorService中的方法shutdown销毁线程池(不建议执行)

六、Lambda表达式

6.1标准格式:

​ 由三部分组成:

​ a.一些参数

​ b.一个箭头

​ c.一段代码

​ 格式:

​ (参数列表) -> {一些重写方法的代码};

​ 解释说明格式:

​ ():接口中抽象方法的参数列表,没有参数,就空着;有参数就写出参数,多个参数使用逗号分隔。

​ ->:传递的意思,把参数传递给方法体{}

​ {}:重写接口的抽象方法的方法体

6.2 Lambda的使用前提

​ 1.使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法

​ 无论是JDK内置的Runnable、Comparator接口还是自定义的接口,只有当接口中的抽象方法存在且唯一时,才可以使用Lambda。

​ 2.使用Lambda必须具有上下文推断

​ 也就是方法的参数或局部变量类型必须为Lambda对应的借口了偶像,才能使用Lambda作为该接口的实例。

备注:有且仅有一个抽象方法的接口,称为“函数式接口”。

Java学习之多线程详解的更多相关文章

  1. Java中的多线程详解

    如果对什么是线程.什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内. 用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现.说这个话其 ...

  2. Java学习之参数传递详解

    Java中的参数传递问题: 基本类型:形式参数的改变对实际参数没有影响.在参数传递过程中,形参和实参占用了两个完全不同的内存空间. 引用类型:形式参数的改变直接影响实际参数.在参数传递的过程中,形参和 ...

  3. Java 多线程详解(四)------生产者和消费者

    Java 多线程详解(一)------概念的引入:http://www.cnblogs.com/ysocean/p/6882988.html Java 多线程详解(二)------如何创建进程和线程: ...

  4. java中多线程详解-synchronized

    一.介绍 当多个线程涉及到共享数据的时候,就会设计到线程安全的问题.非线程安全其实会在多个线程对同一个对象中的实例变量进行并发访问时发生,产生的后果就是“脏读”.发生脏读,就是取到的数据已经被其他的线 ...

  5. Java基础学习总结(24)——Java单元测试之JUnit4详解

    Java单元测试之JUnit4详解 与JUnit3不同,JUnit4通过注解的方式来识别测试方法.目前支持的主要注解有: @BeforeClass 全局只会执行一次,而且是第一个运行 @Before  ...

  6. java.lang.Thread类详解

    java.lang.Thread类详解 一.前言 位于java.lang包下的Thread类是非常重要的线程类,它实现了Runnable接口,今天我们来学习一下Thread类,在学习Thread类之前 ...

  7. Java内存模型(JMM)详解

    在Java JVM系列文章中有朋友问为什么要JVM,Java虚拟机不是已经帮我们处理好了么?同样,学习Java内存模型也有同样的问题,为什么要学习Java内存模型.它们的答案是一致的:能够让我们更好的 ...

  8. Java并发关键字Volatile 详解

    Java并发关键字Volatile 详解 问题引出: 1.Volatile是什么? 2.Volatile有哪些特性? 3.Volatile每个特性的底层实现原理是什么? 相关内容补充: 缓存一致性协议 ...

  9. Java 8 Stream API详解--转

    原文地址:http://blog.csdn.net/chszs/article/details/47038607 Java 8 Stream API详解 一.Stream API介绍 Java8引入了 ...

随机推荐

  1. 4.2 Go switch

    4.2 Go switch switch语句用于基于不同条件执行不同动作,每一个case分支唯一,自上而下逐一测试,直到匹配结束,默认自动终止,不需要break. 2. switch基本语法 swit ...

  2. 解决el-tree横向滚动条问题

    代码如下 效果如图 仅做下记录,不做过多解释

  3. iOS [AFHTTPSessionManager GET:parameters:progress:success:failure:]: unrecognized selector sent to

    AFN更新到4.0.1后,崩溃[AFHTTPSessionManager GET:parameters:progress:success:failure:]: unrecognized selecto ...

  4. OpenStack的Swift组件详解

    一:简介     一.背景 1. Swift 最初是由 Rackspace 公司开发的高可用分布式对象存储服务(Object  Storage Service),并于 2010 年贡献给 OpenSt ...

  5. Django创建应用以及路由的配置

    Django简介:是一个开放源代码的web 应用框架,由python 写成.初次发布于2005年7月,并于2008年9月发布了第一个正式版本1.0. 文件说明:manage.py:一个命令行工具,可以 ...

  6. Linux学习(一):常用命令

    init 0:关机 init 3:命令行模式 init 5:图形界面模式 init 6:重启 shutdown -h now:立马关机 ls:文件列表 参数:-l 详细列表 cd:切换目录 用法实例: ...

  7. 前端星计划笔记-day1

    前端 功能,美观,安全,无障碍,性能,兼容,体验 前端编程思想 WA doctype: 文档版本 浏览器决定渲染模式 语义化: 所有的标签都有自己的含义,属性 可读性 前端规范 whatwg css显 ...

  8. Python编程基本规范

    1.命名规范 类:类的名称一般为名词,且以驼峰形式(即每个单词首字母要大写,其余字母小写,单词之间无间隔符号)给出. 函数:一般以动词开头,函数名称要准确.简要地概括本函数的作用.函数名一律小写,如有 ...

  9. remote desktop能实现什么?远程桌面管理的意义是什么?

    随着互联网时代的发展,向人请教来说,视频教学已经不算便捷了,而远程桌面就发挥了重要作用.它意味着您可以从家里连接到工作计算机,并访问所有应用程序.文件和网络资源,好像正坐在工作计算机前面.您可以让程序 ...

  10. URL跳转与钓鱼

    从登录页跳转到另一个页面就叫做URL跳转. 1.URL跳转 URL跳转一般分为两种,(1)客户端跳转:(2)服务端跳转.对用户来说,两种跳转都是透明的,都是指向或者跳转到另一个页面,页面发生了改变.但 ...