并发与并行

  • 并发:两个或者多个事件在同一时间段发生(交替执行)
  • 并行:两个或者多个事件在同一时刻发生(cpu多核、同时执行)

线程与进程

  • 进程:是一个内存中运行的应用程序,有自己独立的内存空间,一个应用程序至少有一个进程,一个进程至少有一个线程;

  • 线程: 线程是进程中的一个执行单元,是CPU调度和分派的基本单位,能独立运行的基本单位,同一进程中的多个线程之间可以并发执行。

    线程调度:

  • 分时调度:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPUde 时间

  • 抢占式调度:优先让优先级高的线程使用cpu,如果线程的优先级相同,就随机选择一个线程(线程随机性);Java使用的为抢占式调度;

主线程:

  • 单线程程序:Java程序中只有一个线程
  • 执行从main方法开始,从上到下依次执行

如何创建一个多线程

  1. 通过继承Thead类,java.lang.Thead;重写Thead中的run方法,在run方法中设置我们的线程任务,调用start()方法,开启新的线程,执行run方法
public class TheadTest extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++){
System.out.println(i);
}
}
TheadTest theadTest = new TheadTest();
theadTest.start();

多线程的运行原理:

多线程内存图解:

多个线程之间是互不影响的,因为在不同的栈空间

Thred类的常用方法

  1. 获取线程名:
  • getName()
  • static currentThread() 返回当前正在执行的线程对象的引用。
  • Thread.currentThread().getName()
  1. 设置线程的名称:
  • setName(String name) 改变该线程的名称等于参数 name。
  • 创建带参的构造方法
  1. static void sleep(long millis)
  • 当前正在执行的线程休眠(暂停执行)为指定的毫秒数,根据精度和系统定时器和调度的准确性。

实现Runnable 接口

public class RunnableThead implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5 ; i++) {
System.out.println(i);
}
}
RunnableThead runnableThead = new RunnableThead();
new Thread(runnableThead).start();
  • Thread(Runnable target) 分配一个新的 Thread对象。
  • Thread(Runnable target, String name) 分配一个新的 Thread对象。

- Runnable接口创建多线程和继承Thread类创建多线程的区别:

  • 类只能单继承,实现Runnable还可以继承其他的类
  • Runnable设置线程任务和开启线程分离。

匿名内部类的方式实现线程的创建

@Test
public void testThread(){
new Thread("线程一"){
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(getName()+i);
}
}
}.start();
}
@Test
public void testRunnable(){
new Thread( new Runnable(){
@Override
public void run() {
System.out.println("匿名线程");
}
}).start();
}

线程安全问题:

  • 多线程访问了共享的数据就会产生线程安全问题;(多线程操作了同一批数据)
public class TicketThead extends Thread{
public static void saleTicket(String ThreadName){
for (int i = 10; i > 0 ; i--){
System.out.println(ThreadName+"_"+ i);
}
}
@Test
public void testTicketThrea(){
TicketThead ticketThead = new TicketThead();
new Thread("线程1"){
@Override
public void run() {
String name = getName();
ticketThead.saleTicket(name);
}
}.start();
new Thread("线程2"){
@Override
public void run() {
ticketThead.saleTicket(getName());
}
}.start();
new Thread("线程3"){
@Override
public void run() {
ticketThead.saleTicket(getName());
}
}.start();
}
}

多线程安全问题的原理分析:

4.2 线程同步技术解决线程安全问题

  1. 同步代码块
格式:synchronized(锁对象){
可能出现线程安全问题的代码(访问了共享数据的代码)
}
  • 锁对象可以为任意的对象(一般为共享资源的对象),定义的锁对象一般放在run方法之外,同步代码块放在run方法体内。
  • 锁对象作用:把同步代码块锁住,只让一个线程在同步代码块中执行,线程没有执行完毕,不会释放锁
 private int ticket = 100;
Object obj = new Object();
@Override
public void run() {
while (true) {
synchronized(obj){
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket--);
}
}
}
}

同步代码块的原理

  1. 同步方法
  • 同步方法的锁对象就是当前实现类对象this
public void run() {
while (true) {
saleTicket();
}
}
private synchronized void saleTicket(){
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket--);
}
}
  • 静态同步代码块的锁对象是当前实现类的class文件对象
  private static /*synchronized*/ void saleTicket(){
synchronized(Runnable.class){
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket--);
}
}
}

3. 锁机制

  • 接口 Lock -->java.util.concurrent.locks
  • 实现类 java.util.concurrent.locks.ReentrantLock
  • 所有方法 接口方法 抽象方法 Modifier and Type Method and Description
  • void lock() 获取锁。
  • void unlock() 释放锁。
  • 使用步骤:在成员位置创建一个ReentrantLock对象
public void run() {
while (true) {
lock.lock();
if (ticket > 0) {
try {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket--);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
}

线程的状态

等待唤醒(线程之间的通信)

  • 等待状态,一个正在无限期等待另一个线程执行一个特别唤醒动作的线程处于这一状态。
  • Object类中的方法:
  • wait和notify方法必须要在同步代码块或者同步方法中使用,因俄日锁对象必须是同一个
  • void wait() 使当前线程等待另一个线程调用此对象的方法或 notify() notifyAll()方法。
  • void wait(long timeout) //在指定时间内没有被唤醒,会自动醒过来
  • 使当前线程等待另一个线程调用此对象的方法或 notify() notifyAll()方法,或一个指定的时间流逝。
  • void notify() 唤醒一个在这个对象的监视器上等待的单个线程。
  • void notifyAll() 唤醒正在等待此对象监视器上的所有线程。
public static void main(String[] args) {
Object obj = new Object();
new Thread("顾客线程"){
@Override
public void run() {
synchronized (obj){
System.out.println(getName()+"告知老板要买的包子数量和种类!");
try {
obj.wait(); //释放了锁对象
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("开吃包子咯");
}
}
}.start();
new Thread("老板线程"){
@Override
public void run() {
synchronized (obj){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName()+"告知顾客包子做好了");
obj.notify();
}
}
}.start();
}

等待唤醒机制(线程间的通信)

  • 就是一个线程执行完规定的操作之后就进入等待状态(调用wait()),等待其他线程完成他们指定的代码后再将其唤醒(notify)wait/notify就是线程间的一种协作机制
  • 多个线程在处理同一个资源,但是处理的动作(线程的任务)不一样
  • 多个线程并发执行时,默认情况下CPU是随机切换线程的,当我们需要多个线程共同完成一件任务,希望他们能规律的执行,多线程之间需要协调通信,以此达到多线程共同操作一份数据
//线程一
public class BaoZiPu extends Thread{
private BaoZi bz; public BaoZiPu(BaoZi bz) {
this.bz = bz;
}
@Override
public void run() {
synchronized (bz){
if (bz.flag == true){
try {
bz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (bz.flag == false){
bz.name = "叉烧包";
System.out.println(getName() + ":" + "开始生产"+bz.name+"包子");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("生产好了包子");
bz.flag = true;
bz.notify();
}
}
}
//线程二
public class Consumer extends Thread {
private BaoZi bz; public Consumer(BaoZi bz) {
this.bz = bz;
} @Override
public void run() {
synchronized (bz){
if (bz.flag == false){
try {
bz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (bz.flag == true){
System.out.println( getName() + ":" + "开吃" + bz.name + "包子!");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("包子吃完了!");
bz.notify();
bz.flag = false;
}
}
}
}
//
BaoZi bz = new BaoZi();//传入的锁对象是同一个
new BaoZiPu(bz).start();
new Consumer(bz).start();

线程池:

  • 频繁的创建线程和销毁线程需要消耗时间

  • 其实就是一个容纳多个线程的容器,线程池中的线程是可以反复使用的。

  • java.util.concurrent.Executors

  • static ExecutorService newFixedThreadPool(int nThreads) 创建一个线程池,使用固定数量的线程操作了共享无界队列。

  • Future<?> submit(Runnable task) 提交执行一个Runnable任务并返回一个表示该任务的未来。

  • void shutdown() 启动一个有序的关机,在以前提交的任务被执行,但没有新的任务将被接受。

ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.submit(new RunnableThead());

最后

感谢你看到这里,文章有什么不足还请指正,觉得文章对你有帮助的话记得给我点个赞,每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!

java开发两年,连这些多线程知识都还没掌握,你凭什么涨薪!的更多相关文章

  1. java开发两年,这些线程知识你都不知道,你怎么涨薪?

    前言 什么是线程:程序中负责执行的哪个东东就叫做线程(执行路线,进程内部的执行序列),或者说是进程的子任务. Java中实现多线程有几种方法 继承Thread类: 实现Runnable接口: 实现Ca ...

  2. java开发两年,连Spring的依赖注入的方式都搞不清楚,你工作可能有点悬!

    Spring依赖注入 常的java开发中,程序员在某个类中需要依赖其它类的方法,则通常是new一个依赖类再调用类实例的方法,这种开发存在的问题是new的类实例不好统一管理,spring提出了依赖注入的 ...

  3. 不会吧,你连Java 多线程线程安全都还没搞明白,难怪你面试总不过

    什么是线程安全? 当一个线程在同一时刻共享同一个全局变量或静态变量时,可能会受到其他线程的干扰,导致数据有问题,这种现象就叫线程安全问题. 为什么有线程安全问题? 当多个线程同时共享,同一个全局变量或 ...

  4. java开发两年!这些异常处理的方式你得知道,不然你凭什么涨薪!

    前言 异常是在程序中导致程序中断运行的一种指令流,当异常发生时,程序将直接中断,不再执行后续的任何操作! 示例:两数相除,若不处理任何异常,则只有在正确输入两个数字时,才能显示出运算结果. publi ...

  5. java开发两年了,连个java代理模式都摸不透,你怎么跳槽涨薪?

    前言 代理模式(Proxy Pattern),23种java常用设计模式之一.代理模式的定义:代理类对被代理对象提供一种代理以控制对这个对象的访问.代理类主要负责为委托类预处理消息.过滤消息.把消息转 ...

  6. java开发两年,连Spring中bean的装配都不知道?你怎么涨薪啊

    Spring 1.1.1.1 创建一个bean package com.zt.spring; public class MyBean { private String userName; privat ...

  7. java开发工具比较(16个工具修订版)

    1.JDK (Java Development Kit)Java开发工具集 SUN的Java不仅提了一个丰富的语言和运行环境,而且还提了一个免费的Java开发工具集(JDK).开发人员和最终用户可以利 ...

  8. Java开发工具全面比较

    1.JDK (Java Development Kit)Java开发工具集 从初学者角度来看Java开发工具,采用JDK开发Java程序能够很快理解程序中各部分代码之间的关系,有利于理解Java面向对 ...

  9. java开发,入职半年。对未来迷茫,如何发展

    蛮多人私密我一些问题,关于面试,关于技术的,我只能说有些路只能靠自己去走,没人可以帮到自己,哪怕偶尔帮一到两次,但是技术的路这么长,总归需要自己独自成长的.附一张自己藏书的照片,与各位共勉 工作三年多 ...

随机推荐

  1. centos mysql5.7安装

    1. 安装 1 wget http://repo.mysql.com//mysql57-community-release-el7-11.noarch.rpm 2 rpm -ivh mysql57-c ...

  2. this()与super()

    1. 构造器中第一行默认是super(),一旦直接父类的构造器中没有无参的,那么必须显式调用父类的某个有参构造. 2. 构造器中第一行的super()可以换成this(),但是this()和super ...

  3. js一些注意事项

    0.正则表达式,千万不能加引号 1.json对象的key必须用双引号,否则parse时可能出错: json对象不能直接存储时间对象,需要将时间对象加双引号转为字符串,存储,然后对表示时间的属性进行ne ...

  4. nexus私服部署

    1,下载安装包,解压,执行以下命令启动服务. nexus.exe /run 2,访问http://localhost:8081访问管理界面,添加一个maver2(proxy)的仓库,代理地址填写阿里云 ...

  5. Libevent库基础(2)

    带缓冲区的事件 bufferevent #include <event2/bufferevent.h> read/write 两个缓冲. 借助 队列. 创建.销毁bufferevent: ...

  6. Redis---06主从复制(薪火相传)

    一.什么是薪火相传模式 上一个slave(从机)是下一个slave(从机)的Master(主机) 二.为什么要这样 优点:从机同样可以接收其他从机的连接和同步请求,那么该从机作为了链条中下一个的主机, ...

  7. 正式班D16

    2020.10.27星期二 正式班D16 目录 9.9 字符处理命令 9.9.1 sort排序 9.9.2 uniq去重 9.9.3 cut处理规律文本 9.9.4 tr替换 9.9.5 wc统计 9 ...

  8. lora传输模块的特点概述

    现今Lora已经是一种在物联网中广泛应用的技术,它是一种无线调制的方式,相对于传统的FSK调制技术来说,Lora在抑制同频干扰方面有非常大的优势,它解决了无法同时兼顾距离.抗扰和功耗不足的问题;另外l ...

  9. 从原生web组件到框架组件源码(三)

    快乐的时光都是这么短暂,转眼间,web原生组件的知识点已经学完了,这个虽然暂时不一定有用,但是随着时间的积累,一步一个脚印的积累,你会有相应的收获,希望能变得更强,比如两年前我也会想有现成的东西不用, ...

  10. Stimulsoft Reports和Dashboards发布新版本2020.5具有多项改进

    Stimulsoft仪表工具实现所需的数据可视化和自己的信息图表.该产品能够应用必要的过滤器和排序,汇总数据,执行任何复杂度的计算.该产品的优势在于其多功能性-能够为您的业务,财务,销售,行业等任何领 ...