Java——多线程和锁

article/2025/10/20 18:49:26

多线程

前言:当我们打开一个网站时,不同部分的加载并不是先后出现的,是并行出现的,没有出现一个地方没加载完,别的地方就也加载不出来这种事。这个就是多线程并行运行。

当其中一个线程发生阻塞时,操作系统会自动执行新的线程保证cpu不会闲置???

实现方式1:

继承一个Thread类并重写其中的run()函数

对于想要实现的多线程逻辑直接写在run函数里面即可 

class worker extends Thread {@Overridepublic void run(){for(int i=0;i<10;i++){System.out.println("Helo!"+this.getName());try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

在main函数中实现两个线程并启动

package test;import static java.lang.Thread.sleep;class worker extends Thread {@Overridepublic void run(){for(int i=0;i<10;i++){System.out.println("Helo!"+this.getName());try {sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}public class test {public static void main(String[] args) throws InterruptedException {worker w1=new worker();worker w2=new worker();w1.setName("线程1");w2.setName("线程2");w1.start();w2.start();for(int i=0;i<10;i++){System.out.println("这是主线程");sleep(1000);}}
}

原本的main方法就是一个主线程,当执行到w1.start时就会新开一个线程,执行到w2.start时会再新开一个线程,start方法会自动调用run()方法。然后就会有三个线程在同时执行。

运行代码的输出结果如下图所示,可以看见三个线程在先后的运行,在cpu当中运行时则是一个一个线程进cpu运行输出结果,在外面看起来就是差不多同时。

 实现方法2:

实现Runnable接口

用这个方法实现有多少种线程就要创建多少个Runnable的实现类,并且这个里面是没有setName方法。在开启线程时需要new一个实现类的实例作为参数传进Thread()里面,,然后调用start()方法,会自动运行run()函数。

要么也可以共用一个实例,上面继承的写法是每一个都要有一个实例。

共用一个实例更好实现下面的锁的同步机制

package test;class worker1 implements Runnable{@Overridepublic void run() {for (int i = 0; i < 10; i ++ ) {System.out.println("Hello! " + "thread-1");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
class worker2 implements Runnable {@Overridepublic void run() {for (int i = 0; i < 10; i ++ ) {System.out.println("Hello! " + "thread-2");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
public class test {public static void main(String[] args) throws InterruptedException {new Thread(new worker1()).start();new Thread(new worker2()).start();}
}

常用的API 

start():开启一个线程
Thread.sleep(): 休眠一个线程
join():等待线程执行结束  //等当前线程执行结束后才会继续往下执行,前提是该线程已经开始执行了,也可以设定等待时间
interrupt():从休眠中中断线程 //给当前线程发送一个中断,至于线程如何处理就要看run函数的内部是怎么样的,这个方法只有在run函数内有抛出中断时才有影响。
setDaemon():将线程设置为守护线程。当只剩下守护线程时,程序自动退出,java中有一个垃圾回收机制,当所有用户的线程结束之后就要自动结束,守护线程就可以用于设置一些没用的线程,当别的有用的线程都结束了就自动结束这些无用的 线程。

在如下的代码当中w2设置为了守护线程,w1设置了一个中断结束,因此在w1结束的时候w2也会一起结束。

package test;import static java.lang.Thread.sleep;class worker extends Thread {@Overridepublic void run(){for(int i=0;i<10;i++){System.out.println("Helo!"+this.getName());try {sleep(1000);} catch (InterruptedException e) {System.out.println(e.getMessage());break;}}}
}public class test {public static void main(String[] args) throws InterruptedException {worker w1=new worker();worker w2=new worker();w1.setName("线程1");w2.setName("线程2");w2.setDaemon(true);w1.start();w2.start();w1.interrupt();}
}

输出如下所示

锁锁锁——锁锁world new new!

锁的必要性:对于一个整型变量cnt=5,现在有两个操作同时发生,一个是读,一个是写(自增1)

先读后写读到的就是5

先写后读,读到的就是6。正常情况下不能确定是哪一个结果,我们要的是哪一个结果也不能确定。

锁的意义在这里就体现了出来,当一个线程在对一个变量进行操作时不允许别的线程也同时过来操作。

在上面的多线程也可以实现这一点,使用join函数保证在当前线程结束前别的线程不会启动。

如在下面这个多线程实现类当中,所有实例都共享一个锁和一个cnt属性,一个线程在的执行cnt++之前都会先上锁,不让别的线程修改cnt的值,直到cnt获得了新值之后才会解锁。

class worker extends Thread {private static final ReentrantLock lock=new ReentrantLock();private static int cnt=0;@Overridepublic void run(){for(int i=0;i<10;i++){lock.lock();try{cnt++;}finally {lock.unlock();}}}
}
public class test {public static void main(String[] args) throws InterruptedException {worker w1=new worker();worker w2=new worker();w1.setName("线程1");w2.setName("线程2");w2.setDaemon(true);w1.start();w2.start();w1.join();w2.join();System.out.println(w1.getcnt());}
}

上面代码执行输出如下

成功执行了20次

下面是没加锁的情况 

在操作系统中使用pv操作实现

锁的语法糖

同步(Synchronized)

synchronized(对象) {代码}:当加上这个之后,任何想要访问这个对象的线程都会被阻塞。直到里面的代码执行完成。

写法1:将Synchronized加到代码块上

如下的代码就是锁住了一段代码的count属性,这个count参数是被所有worker实例线程共享的,直到该段代码执行完毕或锁被释放才会执行别的线程的这段代码

package test;class Count{public int cnt=0;
}
class worker extends Thread {public    final Count count;public worker(Count count){this.count=count;}@Overridepublic void run(){synchronized (count) {for (int i = 0; i < 10000; i++) {count.cnt++;}}}
}public class test {public static void main(String[] args) throws InterruptedException {Count count=new Count();worker w1=new worker(count);worker w2=new worker(count);w1.setName("线程1");w2.setName("线程2");w2.setDaemon(true);w1.start();w2.start();w1.join();w2.join();System.out.println(w1.count.cnt);}
}

写法2:将Synchronized加到函数上(锁加到了this对象上)

如果是使用继承写法加到函数上的话像下面

class worker extends Thread {public static Integer cnt=0;private synchronized  void work(){for (int i = 0; i < 10000; i++) {cnt = cnt + 1;}}@Overridepublic void run(){work();}
}

输出结果是错误的

上面的等价于如下的写法

    private  void work(){synchronized (this) {for (int i = 0; i < 10000; i++) {cnt = cnt + 1;}}}

 synchronized里面的参数时是当前这个线程的对象,因为有两个线程,两个对象,所以实际是不会互相影响的,所以在函数上加synchronized不适用于这种多个对象的线程

使用接口形式实现的同一个对象可以运行在多个不同的线程上,接口实现的多线程创建一个实例传进两个线程里面运行,这时候synchronized加在函数上面就可以提现其作用了。

package test;class Worker implements Runnable {public static int cnt = 0;private synchronized void work() {for (int i = 0; i < 100000; i ++ ) {cnt ++ ;}}@Overridepublic void run() {work();}
}public class test {public static void main(String[] args) throws InterruptedException {Worker worker = new Worker();Thread worker1 = new Thread(worker);Thread worker2 = new Thread(worker);worker1.start();worker2.start();worker1.join();worker2.join();System.out.println(Worker.cnt);}
}

 wait与notify

一个是等待一段时间,一个是从等待中退出

needWait=true表示当前线程需要等待

下面代码的逻辑是先开启了五个线程,都被阻塞在了同一个地方,也就是下面的odject.wait()那里,然后使用一个新的线程来随机唤醒一个旧的线程,一个旧线程被唤醒会先输出一个被唤醒的信息,然后经过一秒再去唤醒其他随机一个线程。

也可以使用notifyAll()直接唤醒所有被睡眠的线程。

wait()当中可以传参进去,wait(1000)表示睡眠一秒就自动唤醒了。

package test;class worker extends Thread {private static final Object object=new Object();private final boolean needWait;public worker(boolean needWait){this.needWait=needWait;}@Overridepublic void run() {synchronized (object){try {if(needWait) {System.out.println(this.getName()+"婴儿般的睡眠");object.wait();//进入等待后就一直卡在这里System.out.println(this.getName()+"被唤醒了");//直到的外部有操作来唤醒了Thread.sleep(1000);object.notify();}else{object.notify();//全部唤醒 ,单一个notify就是随便唤醒一个}} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}public class test {public static void main(String[] args) throws InterruptedException {for(int i=0;i<5;i++){worker worker=new worker(true);worker.setName("thread-"+i);worker.start();}worker worker=new worker(false);  //传个false进去唤醒其余线程worker.setName("thread-救世主");worker.start();}
}

Thread.sleep(1100)在哪个线程使用就睡眠哪个线程。


http://chatgpt.dhexx.cn/article/GUSm3DvA.shtml

相关文章

Java-多线程中的“锁“

文章目录 Java多线程中的锁1. 什么是锁&#xff1f;2. 锁的作用3. 锁的类型4. 锁的使用示例5.乐观锁和悲观锁6. 锁的注意事项总结 Java多线程中的锁 在Java多线程编程中&#xff0c;锁是一种重要的同步机制&#xff0c;用于保护共享资源的访问。使用锁可以防止多个线程同时对共…

JAVA三种线程锁

内置锁&#xff1a;synchriozed&#xff0c;关键字&#xff0c;同步代码块&#xff0c;object.wait和object.notify/notifyall 显示锁&#xff1a;Lock&#xff0c;JUC包下的类&#xff0c;同步代码块&#xff0c;condition.await和condition.signal/signalall 原子类&#xff…

Java多线程中锁的理解与使用

1.简介 锁作为并发共享数据&#xff0c;保证一致性的工具&#xff0c;在JAVA平台有多种实现(如 synchronized 和 ReentrantLock等 ) 。 2.Java锁的种类 公平锁/非公平锁可重入锁独享锁/共享锁互斥锁/读写锁乐观锁/悲观锁分段锁偏向锁/轻量级锁/重量级锁自旋锁 上面是很多锁…

java多线程的15种锁

1 java锁分类 下面我们依照序号依次的介绍每一种锁 2 悲观锁和乐观锁 悲观锁和乐观锁是一种广义的概念&#xff0c;体现的是看待线程同步的不同的角度 悲观锁认为自己在使用数据的时候&#xff0c;一定有别的线程来修改数据&#xff0c;在获取数据的时候会先加锁&#xff0c…

Java多线程 - 锁

Java多线程 - 锁 三性 可见性 指的是线程之间的可见性&#xff0c;一个线程对状态的修改&#xff0c;对其他线程是可见的。在 Java中 volatile、synchronized 和 final 实现可见性。 原子性 如果一个操作是不可分割的&#xff0c;我们则称之为原子操作&#xff0c;也就是有原…

Java多线程与锁

前文中&#xff0c;我们已经了解了什么是线程&#xff0c;线程间常用通信方式&#xff0c;线程池以及其相关特性&#xff0c;可以看出锁在多线程环境中充当着重要作用&#xff0c;不管是线程间的数据通信&#xff0c;还是线程间的等待和唤醒&#xff0c;都依赖于锁&#xff0c;…

JAVA基础-多线程中锁机制

多线程锁 多线程锁机制锁的定义锁的分类公平锁/非公平锁可重入锁独享锁/共享锁互斥锁/读写锁乐观锁/悲观锁分段锁偏向锁/轻量级锁/重量级锁自旋锁 锁的使用AQSAQS框架展示AQS定义两种资源共享方式AQS常用的几种方法&#xff08;自定义同步器实现时&#xff09;自定义同步器实现…

多线程系列-Java中的锁(简介)

前言 Java提供了种类丰富的锁&#xff0c;每种锁因其特性的不同&#xff0c;在适当的场景下能够展现出非常高的效率。本文旨在对锁相关源码&#xff08;本文中的源码来自JDK 8和Netty 3.10.6&#xff09;、使用场景进行举例&#xff0c;为读者介绍主流锁的知识点&#xff0c;以…

JAVA如何在线程中加锁(四种方法)

JAVA多线程锁 线程的生命周期 ​ 总共六种状态&#xff0c;可归结为五种&#xff0c;线程的最终是死亡&#xff0c;阻塞不是最终状态&#xff0c;只是一个临时状态。只有调用了start方法&#xff0c;线程才进入就绪阶段。 //新生 ​ NEW, //运行​ RUNNABLE, //阻塞​ BLOCKE…

大数据平台_大数据应用场景有哪些

大数据时代的出现&#xff0c;简单的讲是海量数据同完美计算能力结合的结果&#xff0c;确切的说是移动互联网、物联网产生了海量的数据&#xff0c;大数据计算技术完美地解决了海量数据的收集、存储、计算、分析的问题。一些公司也成立了大数据部门&#xff0c;大数据得到了企…

银行业9大数据科学应用案例

在银行业中使用数据科学不仅仅是一种趋势,它已成为保持竞争的必要条件。 银行必须认识到, 大数据技术可以帮助他们有效地集中资源,做出更明智的决策并提高绩效。 以下我们罗列银行业使用的数据科学用例清单, 让您了解如何处理大量数据以及如何有效使用数据。 [TOC] 1 欺诈识…

某银行大数据体系架构设计与演进

近年来&#xff0c;随着大数据与人工智能相关技术的迅速发展&#xff0c;新技术逐步在全社会各行各业得到应用。银行业作为一个高度信息化的行业&#xff0c;首当其冲面临着互联网新技术应用的挑战。民生银行在 2013 年开始布局分布式、大数据及人工智能技术等领域&#xff0c;…

图解大数据 | 大数据生态与应用导论

作者&#xff1a;韩信子[ShowMeAI](https://www.showmeai.tech 教程地址&#xff1a;https://www.showmeai.tech/tutorials/84 本文地址&#xff1a;https://www.showmeai.tech/article-detail/167 声明&#xff1a;版权所有&#xff0c;转载请联系平台与作者并注明出处 收藏S…

大数据应用于生活,目前主要应用在哪些领域?

如果提到“大数据”时&#xff0c;你会想到什么?也许大部分人会联想到庞大的服务器集群;或者联想到销售商提供的一些个性化的推荐和建议。 如今大数据的深度和广度远不止这些&#xff0c;大数据已经在人类社会实践中发挥着巨大的优势&#xff0c;其利用价值也超出我们的想像。…

大数据应用管理模式及内容

通过调研&#xff0c;数据应用管理可总结为分散管理型、职能复用型、集中管理型三种模式&#xff0c;数据应用管理模式中重点关注组织管理、需求管理、建设管理、成果管理四大领域。 &#xff08;1&#xff09; 管理模式 分散管理型&#xff1a;各部门分散开展数据应用&#xf…

大数据应用于各个行业,大数据在各行各业的具体应用是什么?

大数据无处不在&#xff0c;大数据应用于各个行业&#xff0c;包括金融、汽车、餐饮、电信、能源、体能和娱乐等在内的社会各行各业都已经融入了大数据的印迹&#xff0c;下面详细介绍一下大数据在各行各业的具体应用。      制造业&#xff0c;利用工业大数据提升制造业水…

大数据应用的重要性体现在方方面面

大数据应用的重要性&#xff0c;自全国提出“数据中国”的概念以来&#xff0c;我们周围默默地在发挥作用的大数据逐渐深入人们的心中&#xff0c;大数据的应用也越来越广泛&#xff0c;具体到金融、汽车、餐饮、电信、能源、体育和娱乐等领域&#xff0c;下面就通过本文&#…

4大案例分析金融机构的大数据应用

就“大数据金融”思维利用而言&#xff0c;国外金融机构有着十足丰富的体现&#xff0c;已经将大数据技术在风险控制、运营管理、销售支持及商业模式创新等领域进行了全面的尝试。 案例一&#xff1a;汇丰银行-风险管理 汇丰银行在防范信用卡和借记卡欺诈的基础上&#xff0c;利…

金融业大数据应用场景

如果能够引入外部数据&#xff0c;还可以进一步加快数据价值的变现。外部数据中比较好的有社交数据、电商交易数据、移动大数据、运营商数据、工商司法数据、公安数据、教育数据和银联交易数据等。 大数据在金融行业的应用范围较广&#xff0c;典型的案例有花旗银行利用 IBM 沃…

【行业|分析】大数据对于银行七大应用

如今,Hadoop几乎存在于各个方面,其通过利用大数据来分析信息和增加竞争力。许多金融机构和公司已经开始使用Hadoop成功地解决问题,即便他们本没有计划这样做。因为如果他们不这样做,就会面临市场份额损失的巨大风险。以下是一些特别有趣且重要的大数据和Hadoop用例。 诈骗侦…