mysql实用教程第三版电子书,Java多线程详解

注意:

  1. 想要启动多线程,必须调用start()方法。如果手动将调用start()方法的地方改为调用run()方法,那么程序将不会启动多线程模式。

  2. run()方法由JVM调用,什么时候调用,执行的过程控制都有操作系统的CPU****调度决定。

  3. 一个线程对象只能调用一次start()方法启动,如果重复调用了,则将抛出****异常“IllegalThreadStateException”。

四、用实现Runnable接口的方式创建多线程

步骤:

  1. 定义一个子类实现Runnable接口。

  2. 子类中重写Runnable的run()方法。

  3. 在主线程中new一个子类对象。

  4. 将子类对象作为实际参数传递给Thread类的含参构造器中创建线程对象。

  5. 调用该线程对象的start()方法。

package com.hedong;

//自定义一个子类实现Runnable接口

class FirstThread implements Runnable{

//重写run方法

@Override

public void run() {

//遍历输出0——50的偶数

for (int i = 0; i < 50; i++) {

if(i % 2 == 0){

System.out.println(i);

}

}

}

}

/**

  • @author hedong

  • @version 1.0

  • @date 2020/4/8 8:21

*/

public class MyThread {

public static void main(String[] args) {

//创建子类对象

FirstThread firstThread=new FirstThread();

//将子类对象作为一个参数放入Thread的含参构造器中,生成一个线程对象

Thread myThread=new Thread(firstThread);

//调用线程对象的start方法

myThread.start();

}

}

五、 继承方式和实现方式的联系与区别

联系:通过源码可以发现Thread类实际上也实现了Runnable接口。

区别:继承Thread类的方式是将线程代码放在Thread子类的run()方法中。而实现Runnable接口的方式是将线程代码放在Runnable接口子类的run()方法中。

实现Runnable接口的方式的优点:用此种方式实现的多个线程可以共享实现Runnable接口子类中定义的对象。简单来说就是多个线程共用同一份数据资源。

六、线程的生命周期

七、线程安全问题

**问题举例:**假如卡里原先有3000元,A、B两人同时取这张卡里的钱,两人的操作视作两个不同的线程。A打算取2000元,线程A进入if判断3000>2000后发生阻塞,此时B打算也取2000元,并且线程B在线程A发生阻塞的时候顺利取走了2000元,此时卡里只剩1000元,等线程A阻塞结束后继续扣去卡里2000元,这时悲剧发生:卡里最终金额为-1000。

问题的原因: 当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。

解决办法: 对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。Java对于多线程的安全问题提供了专业的解决方式: 同步机制。

八、线程安全问题的解决——Synchronized同步机制

Java对于多线程的安全问题提供了专业的解决方式:同步机制。同步机制可以分为同步代码块和同步方法。

**1、同步代码块:**代码如下,同步监视器相当于一把锁,这个锁可以为任意的类对象,若多线程是用实现Runnable接口的方式实现,则考虑使用this充当对象,若多线程是用继承Thread类的方式实现,则考虑使用当前类充当对象。其中需要被同步的代码即为run()方法中的代码。注意:多个线程必须使用同一把锁,即同一个对象。

synchronized (同步监视器){

// 需要被同步的代码;

}

  • 同步代码块处理实现Runnable接口方式的线程安全的问题:

//自定义一个子类实现Runnable接口

class FirstThread implements Runnable{

//重写run方法

@Override

public void run() {

//遍历输出0——50的偶数

synchronized(this) {

for (int i = 0; i < 50; i++) {

if(i % 2 == 0){

System.out.println(i);

}

}

}

}

}

  • 同步代码块处理继承Thread类方式的线程安全的问题:

//自定义一个子类继承Thread类

class FirstThread extends Thread{

//重写run方法

@Override

public void run() {

synchronized (FirstThread.class){//

for (int i = 0; i < 50; i++) {

if(i % 2 == 0){

System.out.println(i);

}

}

}

}

}

**2、同步方法:**synchronized还可以放在方法声明中,表示整个方法为同步方法。同步方法仍然涉及到同步监视器,只是不用我们自己去声明,当为非静态的同步方法时,同步监视器是this;当为静态的同步方法时,同步监视器是当前类。

  • 同步方法处理实现Runnable接口方式的线程安全的问题:

//自定义一个子类实现Runnable接口

class FirstThread implements Runnable{

//重写run方法

@Override

public void run() {

show();

}

private synchronized void show(){//此时同步监视器为:this

for (int i = 0; i < 50; i++) {

if(i % 2 == 0){

System.out.println(i);

}

}

}

}

  • 同步方法处理继承Thread类方式的线程安全的问题:

//自定义一个子类继承Thread类

class FirstThread extends Thread{

//重写run方法

@Override

public void run() {

show();

}

private static synchronized void show(){//静态方法,此时同步监视器为当前类:FirstThread.class

for (int i = 0; i < 50; i++) {

if(i % 2 == 0){

System.out.println(i);

}

}

}

}

九、线程安全的单例模式之懒汉式

**单例模式:**保证类在内存中只能有一个对象。单例模式分为 懒汉式饿汉式

  • **懒汉式:**默认不会实例化,什么时候用什么时候创建对象。

  • **饿汉式:**类加载的时候就实例化,并且创建单例对象。

懒汉式

public class Lazy {

private static Lazy lazy;

public static Lazy getInstance(){

//用的时候才去创建

if(lazy == null){

lazy = new Lazy();

}

return lazy;

}

}

饿汉式

public class Hungry{

//私有化构造器

private Hungry(){}

//类加载的时候就实例化,并且创建单例对象

private static final Hungry hungry=new Hungry();

public static Hungry getInstance(){

return hungry;

}

}

线程安全问题:

  • **饿汉式线程安全 :**在线程还没出现之前就已经实例化了,因此饿汉式线程一定是安全的。

  • **懒汉式线程不安全:**因为懒汉式是用的时候才创建,这时可能会发生这种情况:线程A进入getInstance()方法发现没有lazy对象,于是准备创建,而在此时线程B也进入了getInstance()方法,也发现没有lazy对象,于是也创建了一个lazy对象,这样A、B两个线程就创建了两个不同的lazy对象,这就不满足我们的单例模式的要求,因此说线程不安全。

同步机制将懒汉式优化为线程安全的单例模式!!!

优化一:效率稍低,每个线程都需要等着前面的线程释放锁之后才能进去拿着对象出来。

public class Lazy {

private static Lazy lazy;

public static synchronized Lazy getInstance(){//静态方法,此时同步监视器为当前类:Lazy.class

if(lazy == null){

lazy = new Lazy();

}

return lazy;

}

}

优化二:效率高,越靠后的线程越不易等待,前面的线程已经创建好了对象之后,后面的线程只需要在最外层判断一下是否有对象即可,若对象存在的话直接就可以拿着对象走了。

public class Lazy {

private static Lazy lazy;

public static Lazy getInstance(){

if(lazy == null){

synchronized (Lazy.class) {

if(lazy == null)

lazy = new Lazy();

}

}

return lazy;

}

}

十、死锁问题

死锁: 不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。

出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续。

**死锁的简单示例:**如下面的死锁代码样例,线程一拿到a锁后发生阻塞,此时线程二拿到b锁发生阻塞,当两个线程阻塞结束后,线程一需要继续拿到b锁,而此时b锁在线程二手里,同样,线程此时需要继续拿到a锁,但a锁在线程一手上,双方这时都占用着对方需要的同步资源不放弃,发生死锁现象。

package com.hedong;

/**

  • @author hedong

  • @version 1.0

  • @date 2020/4/8 15:05

*/

public class DeadLock {

public static String a = “a”;

public static String b = “b”;

public static void main(String[] args){

Thread a = new Thread(new MyThread1());

Thread b = new Thread(new MyThread2());

a.start();

b.start();

}

}

class MyThread1 implements Runnable{

@Override

public void run(){

try{

while(true){

synchronized(DeadLock.a){//同步监视器:a

System.out.println(“线程一,拿到a锁”);

Thread.sleep(3000);//让线程一睡眠,充当阻塞状态

synchronized(DeadLock.b){//同步监视器:b

System.out.println(“线程一,拿到b锁”);

}

}

}

}catch(Exception e){

e.printStackTrace();

}

}

}

class MyThread2 implements Runnable{

@Override

public void run(){

try{

while(true){

synchronized(DeadLock.b){

System.out.println(“线程二,拿到b锁”);

Thread.sleep(3000);//让线程二睡眠,充当阻塞状态

synchronized(DeadLock.a){

System.out.println(“线程二,拿到a锁”);

}

}

}

}catch(Exception e){

e.printStackTrace();

}

}

}

运行结果:

所以,在程序代码中我们一定要避免死锁的发生!

十一、线程安全问题的解决——Lock锁

在前面我们使用了Synchronized同步机制来解决线程安全问题,在jdk5.0之后,新增了一种Lock锁用来解决线程安全问题。

class FirstThread extends Thread{

//实例化ReentrantLock

private ReentrantLock lock = new ReentrantLock(true);

//重写run方法

@Override

public void run() {

try{

//调用lock方法上锁

lock.lock();

for (int i = 0; i < 50; i++) {

if(i % 2 == 0){

System.out.println(i);

}

}

} finally {

//调用unlock解锁

lock.unlock();

}

}

}

十二、Lock锁和Synchronized的比较

  • **相同点:**两者都是同步机制,都能解决线程安全问题。

  • **不同点:**synchronized在执行完相应的同步代码后会自动释放同步监视器,而Lock锁需要在同步代码之前手动上锁开启同步,在结束之后也要手动解锁结束同步。

十三、wait()、notify()、notifyAll()

  • **wait()😗*一旦执行此方法,当前线程将会进入阻塞状态,并释放同步监视器。

  • **notify():**一旦执行此方法就会唤醒一个被wait()的线程,若有多个被wait()的线程,则将唤醒优先级最高的线程。

  • **notifyAll():**一旦执行此方法,将会唤醒所有被wait()的线程。

注意:

  • 以上三个方法必须使用在同步代码块或同步方法中。

  • 以上三个方法的调用者必须为同步代码块或同步方法中的同步监视器。

十四、sleep()和wait()的异同

**相同点:**一旦执行两个方法,都能使当前线程进入阻塞状态。

不同点:

  • **两个方法声明的位置不同:**sleep()是在Thread类中声明,而wait()是在Object类中声明。

  • **两个方法调用的要求不同:**sleep()可以在任意需要的地方调用,而wait()只能在同步代码块和同步方法中调用。

  • **关于是否释放同步监视器:**当两个方法都在同步代码块或同步方法中调用时,sleep()不会释放同步监视器,而wait()会释放同步监视器。

十五、用实现Callable接口的方式创建多线程

JDK5.0新增了两种线程创建方式:实现Callable接口的方式创建多线程、使用线程池。我们这里先讲实现Callable接口的方式创建多线程。

步骤:

  1. 定义一个子类实现Callable接口。

  2. 子类中重写Callable的call()方法,注意call方法有返回值。

  3. 在主线程中创建Callable接口实现类的对象。

  4. 将Callable接口实现类的对象作为参数传递给FutureTask类的含参构造器中,创建FutureTask对象。

  5. 将****FutureTask对象作为参数传递给Thread类的含参构造器中,创建线程对象。

  6. 调用线程对象的start()方法。

  7. 如果需要call方法的返回值,可通过FutureTask对象的get()方法获取。

package com.hedong;

import java.util.concurrent.Callable;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.FutureTask;

/**

  • @author hedong

  • @version 1.0

  • @date 2020/4/8 16:46

*/

class MyThread implements Callable {

//重写call方法,call方法有返回值

@Override
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

分布式技术专题+面试解析+相关的手写和学习的笔记pdf

还有更多Java笔记分享如下:

image

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。[外链图片转存中…(img-KdPClPiA-1713506575023)]

[外链图片转存中…(img-uWFSnINl-1713506575026)]

[外链图片转存中…(img-btMN3EEd-1713506575028)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

分布式技术专题+面试解析+相关的手写和学习的笔记pdf

还有更多Java笔记分享如下:

[外链图片转存中…(img-EAwIrVNh-1713506575029)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值