JUC并发编程-线程和进程、Synchronized 和 Lock、生产者和消费者问题

这篇具有很好参考价值的文章主要介绍了JUC并发编程-线程和进程、Synchronized 和 Lock、生产者和消费者问题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、什么是JUC

源码 + 官方文档 面试高频问!

JUC并发编程-线程和进程、Synchronized 和 Lock、生产者和消费者问题,JUC并发编程,并发编程,JUC,线程安全

java.util 工具包、包、分类

业务:普通的线程代码 Thread Runnable

Runnable 没有返回值、效率相比入 Callable 相对较低!

JUC并发编程-线程和进程、Synchronized 和 Lock、生产者和消费者问题,JUC并发编程,并发编程,JUC,线程安全
JUC并发编程-线程和进程、Synchronized 和 Lock、生产者和消费者问题,JUC并发编程,并发编程,JUC,线程安全

2、线程和进程

线程、进程,如果不能使用一句话说出来的技术,不扎实!

进程:一个程序,QQ.exe Music.exe 程序的集合;
一个进程往往可以包含多个线程,至少包含一个!
Java默认有几个线程? 2 个 mian、GC
线程:开了一个进程 Typora,写字,自动保存(线程负责的)
对于Java而言:Thread、Runnable、Callable
Java 真的可以开启线程吗? 开不了

并发、并行

并发编程:并发、并行
并发(多线程操作同一个资源)

  • CPU 一核 ,模拟出来多条线程,天下武功,唯快不破,快速交替
    并行(多个人一起行走)
  • CPU 多核 ,多个线程可以同时执行; 线程池

并发编程的本质:充分利用CPU的资源

线程有几个状态

public enum State {
	// 创建
	NEW,
	// 运行
	RUNNABLE,
	// 阻塞
	BLOCKED,
	// 等待,死死地等
	WAITING,
	// 超时等待
	TIMED_WAITING,
	// 终止
	TERMINATED;
}

wait/sleep 区别

1、来自不同的类
wait => Object
sleep => Thread
2、关于锁的释放
wait 会释放锁,sleep 睡觉了,抱着锁睡觉,不会释放!
3、使用的范围是不同的
wait:必须在同步代码块中
sleep 可以再任何地方睡
4、是否需要捕获异常
wait 不需要捕获异常
sleep 必须要捕获异常

3、Lock锁(重点)

传统 Synchronized

package com.kuang.demo01;
// 基本的卖票例子
import java.time.OffsetDateTime;
Lock 接口
/**
* 真正的多线程开发,公司中的开发,降低耦合性
* 线程就是一个单独的资源类,没有任何附属的操作!
* 1、 属性、方法
*/
public class SaleTicketDemo01 {
	public static void main(String[] args) {
		// 并发:多线程操作同一个资源类, 把资源类丢入线程
		Ticket ticket = new Ticket();
		// @FunctionalInterface 函数式接口,jdk1.8 lambda表达式 (参数)->{ 代码 }
		new Thread(()->{
			for (int i = 1; i < 40 ; i++) {
				ticket.sale();
			}
		},"A").start();
		new Thread(()->{
			for (int i = 1; i < 40 ; i++) {
				ticket.sale();
			}
		},"B").start();
		new Thread(()->{
			for (int i = 1; i < 40 ; i++) {
				ticket.sale();
			}
		},"C").start();
	}
}
// 资源类 OOP
	class Ticket {
		// 属性、方法
		private int number = 30;
		// 卖票的方式
		// synchronized 本质: 队列,锁
		public synchronized void sale(){
			if (number>0){{System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余:"+number);
		}
	}
}

Lock 接口

JUC并发编程-线程和进程、Synchronized 和 Lock、生产者和消费者问题,JUC并发编程,并发编程,JUC,线程安全

可以在ReentrantLock中设置公平和非公平锁

JUC并发编程-线程和进程、Synchronized 和 Lock、生产者和消费者问题,JUC并发编程,并发编程,JUC,线程安全

公平锁:十分公平:可以先来后到
非公平锁:十分不公平:可以插队 (默认)

Lock三部曲

1. new ReentrantLock();
2. lock.lock(); // 加锁
3. finally=> lock.unlock(); // 解锁

JUC并发编程-线程和进程、Synchronized 和 Lock、生产者和消费者问题,JUC并发编程,并发编程,JUC,线程安全

JUC并发编程-线程和进程、Synchronized 和 Lock、生产者和消费者问题,JUC并发编程,并发编程,JUC,线程安全

测试效果一样

Synchronized 和 Lock 区别

1.Synchronized 内置的Java关键字Lock 是一个Java类
2.Synchronized 无法判断获取锁的状态Lock 可以判断是否获取到了锁
3.Synchronized 会自动释放锁lock 必须要手动释放锁!如果不释放锁,死锁
4.Synchronized 线程 1(获得锁,阻塞)、线程2(等待,傻傻的等);Lock锁就不一定会等待下 去
5.Synchronized 可重入锁不可以中断的非公平Lock可重入锁,可以 判断锁非公平(可以 自己设置)
6.Synchronized 适合锁少量的代码同步问题Lock 适合锁大量的同步代码

4、生产者和消费者问题

1)Synchronzied 版本

面试的:单例模式、排序算法、生产者和消费者、死锁

防止虚假唤醒要用while,不能用if

JUC并发编程-线程和进程、Synchronized 和 Lock、生产者和消费者问题,JUC并发编程,并发编程,JUC,线程安全

JUC并发编程-线程和进程、Synchronized 和 Lock、生产者和消费者问题,JUC并发编程,并发编程,JUC,线程安全

package com.marchsoft.juctest;


public class ConsumeAndProduct {
    public static void main(String[] args) {
        Data data = new Data();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
    }
}

class Data {
    private int num = 0;

    // +1
    public synchronized void increment() throws InterruptedException {
        // 判断等待
        if (num != 0) {
            this.wait();
        }
        num++;
        System.out.println(Thread.currentThread().getName() + "=>" + num);
        // 通知其他线程 +1 执行完毕
        this.notifyAll();
    }

    // -1
    public synchronized void decrement() throws InterruptedException {
        // 判断等待
        if (num == 0) {
            this.wait();
        }
        num--;
        System.out.println(Thread.currentThread().getName() + "=>" + num);
        // 通知其他线程 -1 执行完毕
        this.notifyAll();
    }
}

效果:
JUC并发编程-线程和进程、Synchronized 和 Lock、生产者和消费者问题,JUC并发编程,并发编程,JUC,线程安全

2)存在问题(虚假唤醒)

问题,如果有四个线程,会出现虚假唤醒

JUC并发编程-线程和进程、Synchronized 和 Lock、生产者和消费者问题,JUC并发编程,并发编程,JUC,线程安全

理解文档

JUC并发编程-线程和进程、Synchronized 和 Lock、生产者和消费者问题,JUC并发编程,并发编程,JUC,线程安全

重点理解if和while的线程唤醒问题

解决方式 ,if 改为while即可,防止虚假唤醒

结论:就是用if判断的话,唤醒后线程会从wait之后的代码开始运行,但是不会重新判断if条件,直接继续运行if代码块之后的代码,而如果使用while的话,也会从wait之后的代码运行,但是唤醒后会重新判断循环条件如果不成立再执行while代码块之后的代码块,成立的话继续wait

这也就是为什么用while而不用if的原因了,因为线程被唤醒后,执行开始的地方是wait之后

​ 拿两个加法线程A、B来说,比如A先执行,执行时调用了wait方法,那它会等待,此时会释放锁,那么线程B获得锁并且也会执行wait方法,两个加线程一起等待被唤醒。此时减线程中的某一个线程执行完毕并且唤醒了这俩加线程,那么这俩加线程不会一起执行,其中A获取了锁并且加1,执行完毕之后B再执行。如果是if的话,那么A修改完num后,B不会再去判断num的值,直接会给num+1。如果是while的话,A执行完之后,B还会去判断num的值,因此就不会执行。

JUC并发编程-线程和进程、Synchronized 和 Lock、生产者和消费者问题,JUC并发编程,并发编程,JUC,线程安全

3)JUC版

JUC并发编程-线程和进程、Synchronized 和 Lock、生产者和消费者问题,JUC并发编程,并发编程,JUC,线程安全

package com.marchsoft.juctest;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockCAP {
    public static void main(String[] args) {
        Data2 data = new Data2();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {

                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "D").start();
    }
}

class Data2 {
    private int num = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    // +1
    public  void increment() throws InterruptedException {
        lock.lock();
        try {
            // 判断等待
            while (num != 0) {
                condition.await();
            }
            num++;
            System.out.println(Thread.currentThread().getName() + "=>" + num);
            // 通知其他线程 +1 执行完毕
            condition.signalAll();
        }finally {
            lock.unlock();
        }

    }

    // -1
    public  void decrement() throws InterruptedException {
        lock.lock();
        try {
            // 判断等待
            while (num == 0) {
                condition.await();
            }
            num--;
            System.out.println(Thread.currentThread().getName() + "=>" + num);
            // 通知其他线程 +1 执行完毕
            condition.signalAll();
        }finally {
            lock.unlock();
        }

    }
}

随机执行

JUC并发编程-线程和进程、Synchronized 和 Lock、生产者和消费者问题,JUC并发编程,并发编程,JUC,线程安全

Condition的优势

精准的通知和唤醒的线程!

如何指定线程执行的顺序? 我们可以使用Condition来指定通知进程~

package com.marchsoft.juctest;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;



public class ConditionDemo {
    public static void main(String[] args) {
        Data3 data3 = new Data3();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data3.printA();
            }
        },"A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data3.printB();
            }
        },"B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data3.printC();
            }
        },"C").start();
    }

}
class Data3 {
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
    private int num = 1; // 1A 2B 3C

    public void printA() {
        lock.lock();
        try {
            // 业务代码 判断 -> 执行 -> 通知
            while (num != 1) {
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName() + "==> AAAA" );
            num = 2;
            condition2.signal();
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void printB() {
        lock.lock();
        try {
            // 业务代码 判断 -> 执行 -> 通知
            while (num != 2) {
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName() + "==> BBBB" );
            num = 3;
            condition3.signal();
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void printC() {
        lock.lock();
        try {
            // 业务代码 判断 -> 执行 -> 通知
            while (num != 3) {
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName() + "==> CCCC" );
            num = 1;
            condition1.signal();
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

测试效果:
JUC并发编程-线程和进程、Synchronized 和 Lock、生产者和消费者问题,JUC并发编程,并发编程,JUC,线程安全

JUC并发编程-线程和进程、Synchronized 和 Lock、生产者和消费者问题 的学习笔记到此完结,笔者归纳、创作不易,大佬们给个3连再起飞吧文章来源地址https://www.toymoban.com/news/detail-807843.html

到了这里,关于JUC并发编程-线程和进程、Synchronized 和 Lock、生产者和消费者问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 5.多线程之JUC并发编程2

    1.CompletableFuture异步回调 像ajax,未来再得到执行结果,想服务器不分先后顺序执行,可以用异步回调 2.JMM 面试:对 Volatile的理解 答: Volatile是jvm通过轻量级的同步机制,比sychronized更轻 1.保证可见性 2.不保证原子性 3.禁止指令重排 什么是JMM? 答:java内存模型,是一种规定,不存在的东西

    2024年02月08日
    浏览(40)
  • JUC并发编程学习笔记(十)线程池(重点)

    线程池:三大方法、七大参数、四种拒绝策略 池化技术 程序的运行,本质:占用系统的资源!优化资源的使用!- 池化技术(线程池、连接池、对象池......);创建和销毁十分消耗资源 池化技术:事先准备好一些资源,有人要用就拿,拿完用完还给我。 线程池的好处: 1、

    2024年02月06日
    浏览(45)
  • Java并发编程(三)线程同步 上[synchronized/volatile]

    当使用多个线程来访问同一个数据时,将会导致数据不准确,相互之间产生冲突,非常容易出现线程安全问题,比如多个线程都在操作同一数据,都打算修改商品库存,这样就会导致数据不一致的问题。 所以我们通过线程同步机制来保证线程安全,加入同步锁以避免在该线程没有完成

    2024年02月13日
    浏览(44)
  • JUC并发编程-集合不安全情况以及Callable线程创建方式

    1)List 不安全 ArrayList 在并发情况下是不安全的 解决方案 : 1.Vector 2.Collections.synchonizedList() 3. CopyOnWriteArrayList 核心思想 是,如果有 多个调用者(Callers)同时要求相同的资源 (如内存或者是磁盘上的数据存储),他们 会共同获取相同的指针指向相同的资源 , 直到某个调用者

    2024年01月23日
    浏览(49)
  • Java并发之synchronized关键字和Lock接口

    欢迎点赞阅读,一同学习交流,有疑问请留言 。 GitHub上也有开源 JavaHouse,欢迎star 当开发过程中,我们遇到并发问题。怎么解决? 一种解决方式,简单粗暴:上锁。将千军万马都给拦下来,只允许一个人过独木桥。书面意思就是将并行的程序变成串行的程序。现实的锁有门锁

    2024年02月08日
    浏览(40)
  • 多线程JUC 第2季 synchronized锁升级过程

    用锁能够实现数据的安全,但是会带来性能下降。Synchronized是一个重量级锁,锁的升级过程: 无锁-偏向锁-轻量级锁-重量级锁。 高并发时,同步调用应尽量考虑锁的性能损耗,能用无锁数据结构,就不要用锁,能用区块不要用锁住整个方法体;能有对象锁,就不要用类锁。

    2024年02月09日
    浏览(39)
  • 线程中synchronized关键字和lock接口的异同

    一、synchronized 1.可以用来修饰代码块  2.可以用在方法上 修饰同步方法 while (true) { try { Thread.sleep(200); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } if(sellTicket()) { break; } } 二、lock接口  1.用此接口要用     ReentrantLock l = new ReentrantLock();     

    2024年02月08日
    浏览(44)
  • 多线程|多进程|高并发网络编程

    多进程并发服务器是一种经典的服务器架构,它通过创建多个子进程来处理客户端连接,从而实现并发处理多个客户端请求的能力。 概念: 服务器启动时,创建主进程,并绑定监听端口。 当有客户端连接请求时,主进程接受连接,并创建一个子进程来处理该客户端连接。

    2024年02月07日
    浏览(38)
  • 多线程、协程和多进程并发编程

    37.1 如何通俗理解线程和进程? 进程:进程就是正在执⾏的程序。 线程:是程序执⾏的⼀条路径, ⼀个进程中可以包含多条线程。 通俗理解:例如你打开抖⾳,就是打开⼀个进程,在抖⾳⾥⾯和朋友聊天就是开启了⼀条线程。 再举⼀个例⼦: 在某⻝堂打饭的时候,此⻝堂安

    2024年02月02日
    浏览(99)
  • 有关多线程环境下的Volatile、lock、Interlocked和Synchronized们

    📢欢迎点赞 :👍 收藏 ⭐留言 📝 如有错误敬请指正,赐人玫瑰,手留余香! 📢本文作者:由webmote 原创 📢作者格言:新的征程,我们面对的不仅仅是技术还有人心,人心不可测,海水不可量,唯有技术,才是深沉黑夜中的一座闪烁的灯塔 ! 多线程下的变量访问,就如同

    2024年02月08日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包