多线程与并发编程【线程对象锁、死锁及解决方案、线程并发协作、生产者与消费者模式】(四)-全面详解(学习总结---从入门到深化)

简介: 多线程与并发编程【线程对象锁、死锁及解决方案、线程并发协作、生产者与消费者模式】(四)-全面详解(学习总结---从入门到深化)

使用Class作为线程对象锁



语法结构:

synchronized(XX.class){
      //同步代码
 }


synchronized public static void accessVal()


/**
* 定义销售员工类
*/
class Sale{
    private String name;
    public Sale(String name){
this.name = name;
   }
    /**
     * 领取奖金
     */
    synchronized  public static void money(){
            try {
              System.out.println(Thread.currentThread().getName() + " 被领导表扬");
                Thread.sleep(500);
              System.out.println(Thread.currentThread().getName() + " 拿钱");
                Thread.sleep(500);
              System.out.println(Thread.currentThread().getName() + " 对公司表示感谢");
                Thread.sleep(500);
              System.out.println(Thread.currentThread().getName() + " 开开心心的拿钱走人");
           } catch (InterruptedExceptione) {
                e.printStackTrace();
           }
       }
}
class Programmer{
    private String name;
    public Programmer(String name){
         this.name = name;
   }
    /**
     * 打开电脑
     */
    synchronized  public  void computer(){
            try {
              System.out.println(this.name + " 接通电源");
                Thread.sleep(500);
              System.out.println(this.name + " 按开机按键");
                Thread.sleep(500);
              System.out.println(this.name + " 系统启动中");
                Thread.sleep(500);
              System.out.println(this.name + " 系统启动成功");
           } catch (InterruptedExceptione) {
                e.printStackTrace();
           }
   }
    /**
     * 编码
     */
    synchronized public void coding(){
            try {
                    System.out.println(this.name + " 双击Idea");
                Thread.sleep(500);
              System.out.println(this.name + " Idea启动完毕");
                Thread.sleep(500);
              System.out.println(this.name + " 开开心心的写代码");
           } catch (InterruptedExceptione) {
                e.printStackTrace();
           }
       }
    /**
     * 去卫生间
     */
    public void wc(){
        synchronized ("suibian") {
            try {
              System.out.println(this.name + " 打开卫生间门");
                Thread.sleep(500);
              System.out.println(this.name + " 开始排泄");
                Thread.sleep(500);
              System.out.println(this.name + " 冲水");
                Thread.sleep(500);System.out.println(this.name + " 离开卫生间");
           } catch (InterruptedExceptione) {
                e.printStackTrace();
           }
       }
   }
    /**
     * 领取奖金
     */
    public void money(){
        synchronized (Programmer.class) {
            try {
              System.out.println(this.name + " 被领导表扬");
                Thread.sleep(500);
              System.out.println(this.name + " 拿钱");
                Thread.sleep(500);
              System.out.println(this.name + " 对公司表示感谢");
                Thread.sleep(500);
              System.out.println(this.name + " 开开心心的拿钱走人");
           } catch (InterruptedExceptione) {
                e.printStackTrace();
   }
       }
   }
}
/**
* 打开电脑的工作线程
*/
class Working1 extends Thread{
    private  Programmer p;
    public Working1(Programmer p){
        this.p = p;
   }
    @Override
    public void run() {
        this.p.computer();
   }
}
/**
* 编写代码的工作线程
*/
class Working2 extends Thread{
    private  Programmer p;
    public Working2(Programmer p){
        this.p = p;
   }
    @Override
    public void run() {
        this.p.coding();
   }
}
/**
* 去卫生间的线程
*/
class WC extends Thread{
    private  Programmer p;
    public WC(Programmer p){
        this.p = p;
   }
    @Override
    public void run() {
        this.p.wc();
   }
}
/**
* 程序员领取奖金
*/
class ProgrammerMoney extends Thread{
    private  Programmer p;
    public ProgrammerMoney(Programmer p){
        this.p = p;
   }
    @Override
    public void run() {
        this.p.money();
   }
}
/**
* 销售部门领取奖金
*/
class SaleMoney extends  Thread{
    private  Sale p;
    public SaleMoneyThread(Sale p){
        this.p = p;
   }
    @Override
    public void run() {
        this.p.money();
   }
}
public class TestSyncThread {
    public static void main(String[] args)
{
       /* Programmer p = new Programmer("张三");
        Programmer p1 = new Programmer("李四");
        new ProgrammerMoney(p).start();
        new ProgrammerMoney(p1).start();*/
        Sale s = new Sale("张晓丽");
        Sale s1 = new Sale("王晓红");
        new SaleMoney(s).start();
        new SaleMoney(s1).start();
   }
}


使用自定义对象作为线程对象锁



语法结构:

synchronized(自定义对象){
      //同步代码
}


/**
* 定义销售员工类
*/
class Sale{
    private String name;
    public Sale(String name){
        this.name = name;
   }
    /**
     * 领取奖金
     */
    synchronized  public static void money(){
            try {
                System.out.println(Thread.currentThread(). getName() + " 被领导表扬");
                Thread.sleep(500);
              System.out.println(Thread.currentThread().getName() + " 拿钱");
                Thread.sleep(500);
              System.out.println(Thread.currentThread().getName() + " 对公司表示感谢");
                Thread.sleep(500);
              System.out.println(Thread.currentThread().getName() + " 开开心心的拿钱走人");
           } catch (InterruptedExceptione) {
                e.printStackTrace();
           }
       }
}
class Programmer{
    private String name;
    public Programmer(String name){
        this.name = name;
   }
    /**
     * 打开电脑
     */
    synchronized  public  void computer(){
            try {
                   System.out.println(this.name + " 接通电源");
                   Thread.sleep(500);
                   System.out.println(this.name + " 按开机按键");
                   Thread.sleep(500);
                   System.out.println(this.name + " 系统启动中");
                Thread.sleep(500);
              System.out.println(this.name + " 系统启动成功");
           } catch (InterruptedException e) {
                e.printStackTrace();
           }
   }
    /**
     * 编码
     */
    synchronized public void coding(){
            try {
              System.out.println(this.name + " 双击Idea");
                Thread.sleep(500);
              System.out.println(this.name + " Idea启动完毕");
                Thread.sleep(500);
              System.out.println(this.name + " 开开心心的写代码");
           } catch (InterruptedException e) {
                e.printStackTrace();
           }
       }
    /**
     * 去卫生间
     */
    public void wc(){
        synchronized ("suibian") {
            try {
               System.out.println(this.name + " 打开卫生间门");
                Thread.sleep(500);
               System.out.println(this.name + " 开始排泄");
                Thread.sleep(500);
               System.out.println(this.name + " 冲水");
                Thread.sleep(500);
               System.out.println(this.name + " 离开卫生间");
           } catch (InterruptedException e) {
                e.printStackTrace();
           }
       }
 }
    /**
     * 领取奖金
     */
    public void money(){
        synchronized (Programmer.class) {
            try {
                 System.out.println(this.name + " 被领导表扬");
                Thread.sleep(500);
                 System.out.println(this.name + " 拿钱");
                Thread.sleep(500);
                System.out.println(this.name + " 对公司表示感谢");
                Thread.sleep(500);
                System.out.println(this.name + " 开开心心的拿钱走人");
           } catch (InterruptedException e) {
                e.printStackTrace();
           }
       }
   }
}
class Manager{
    private String name;
    public Manager(String name){
        this.name = name;
}
    public String getName(){
        return this.name;
   }
    /**
     * 敬酒
     */
    public void cheers(String mName,String eName){
            try {
                System.out.println(mName + " 来到 " + eName + " 面前");
                Thread.sleep(500);
                System.out.println(eName + " 拿起酒杯");
                Thread.sleep(500);
                System.out.println(mName + " 和 " + eName + " 干杯");
           } catch (InterruptedException e) {
                e.printStackTrace();
           }
   }
}
/**
* 打开电脑的工作线程
*/
class Working1 extends Thread{
    private  Programmer p;
    public Working1(Programmer p){
        this.p = p;
   }
  @Override
    public void run() {
        this.p.computer();
   }
}
/**
* 编写代码的工作线程
*/
class Working2 extends Thread{
    private  Programmer p;
    public Working2(Programmer p){
        this.p = p;
   }
    @Override
    public void run() {
        this.p.coding();
   }
}
/**
* 去卫生间的线程
*/
class WC extends Thread{
    private  Programmer p;
    public WC(Programmer p){
        this.p = p;
   }
    @Override
    public void run() {
        this.p.wc();
   }
}
/**
* 程序员领取奖金
*/
class ProgrammerMoney extends Thread{
    private  Programmer p;
    public ProgrammerMoney(Programmer p){
        this.p = p;
   }
    @Override
    public void run() {
        this.p.money();
   }
}
/**
* 销售部门领取奖金
*/
class SaleMoneyThread extends  Thread{
    private  Sale p;
    public SaleMoneyThread(Sale p){
        this.p = p;
   }
    @Override
    public void run() {
        this.p.money();
   }
}
/**
* 敬酒线程类
*/
class CheersThread extends Thread{
    private Manager manager;
    private String name;
    public CheersThread(String name,Manager manager){
        this.name = name;
        this.manager = manager;
   }
    @Override
    public void run() {
        synchronized (this.manager) {
this.manager.cheers(this.manager.getName() , name);
       }
   }
}
public class TestSyncThread {
    public static void main(String[] args)
{
        Manager manager = new Manager("张三丰");
        new CheersThread("张三",manager).start();
        new CheersThread("李四",manager).start();
   }
}


死锁及解决方案


死锁的概念


“死锁”指的是: 多个线程各自占有一些共享资源,并且互相等待其他线程占有的资 源才能进行,而导致两个或者多个线程都在等待对方释放资源,都 停止执行的情形。


某一个同步块需要同时拥有“两个以上对象的锁”时,就可能会发 生“死锁”的问题。比如,“化妆线程”需要同时拥有“镜子对象”、 “口红对象”才能运行同步块。那么,实际运行时,“小丫的化妆 线程”拥有了“镜子对象”,“大丫的化妆线程”拥有了“口红对象”, 都在互相等待对方释放资源,才能化妆。这样,两个线程就形 成了互相等待,无法继续运行的“死锁状态”。


死锁案例演示

/**
* 口红类
*/
   class Lipstick{
     }
/**
* 镜子类
*/
   class Mirror{
   }
/**
* 化妆线程类
*/
class Makeup extends Thread{
    private int flag; //flag=0:拿着口红。 flag!=0:拿着镜子
    private String girlName;
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();
    public Makeup(int flag,String girlName){
        this.flag  = flag;
        this.girlName = girlName;
   }
    @Override
    public void run() {
        this.doMakeup();
   }
    /**
     * 开始化妆
     */
    public void doMakeup(){
        if(flag == 0){
            synchronized (lipstick){
              System.out.println(this.girlName+" 拿着口红");
                try {
                    Thread.sleep(1000);
               } catch (InterruptedException e) {
                    e.printStackTrace();
               }
                synchronized (mirror){
               System.out.println(this.girlName+" 拿着镜子");
               }
           }
       }else{
            synchronized (mirror){
              System.out.println(this.girlName+" 拿着镜子");
                try {
                    Thread.sleep(2000);
               } catch(InterruptedException e) {
                    e.printStackTrace();
               }
                synchronized (lipstick){
                  System.out.println(this.girlName+" 拿着口红");
               }
           }
       }
   }
}
public class DeadLockThread {
    public static void main(String[] args) {
        new Makeup(0,"大丫").start();
        new Makeup(1,"小丫").start();
   }
}


死锁问题的解决


死锁是由于 “同步块需要同时持有多个对象锁造成”的,要解决这个 问题,思路很简单,就是:同一个代码块,不要同时持有两个对象 锁。

/**
* 口红类
*/
    class Lipstick{
     }
/**
* 镜子类
*/
    class Mirror{
    }
/**
* 化妆线程类
*/
class Makeup extends Thread{
    private int flag; //flag=0:拿着口红。 flag!=0:拿着镜子
    private String girlName;
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();
    public void setFlag(int flag) {
        this.flag = flag;
      }
    public void setGirlName(String girlName)
      {
        this.girlName = girlName;
      }
    @Override
    public void run() {
        this.doMakeup();
     }
    /**
     * 开始化妆
     */
    public void doMakeup(){
        if(flag == 0){
            synchronized (lipstick){
              System.out.println(this.girlName+" 拿着口红");
                try {
                    Thread.sleep(1000);
               } catch(InterruptedException e) {
                    e.printStackTrace();
               }
           }
            synchronized (mirror){
             System.out.println(this.girlName+" 拿着镜子");
           }
       }else{
            synchronized (mirror){
              System.out.println(this.girlName+" 拿着镜子");
                try {
                    Thread.sleep(2000);
               } catch(InterruptedException e) {
                    e.printStackTrace();
               }
           }
            synchronized (lipstick){
              System.out.println(this.girlName+" 拿着口红");
           }
       }
   }
}
public class DeadLockThread {
    public static void main(String[] args) {
        Makeup makeup = new Makeup();
        makeup.setFlag(0);
        makeup.setGirlName("大丫");
        Makeup makeup1 = new Makeup();
        makeup1.setFlag(1);
        makeup1.setGirlName("小丫");
        makeup.start();
        makeup1.start();
   }
}


死锁问题的解决


死锁是由于 “同步块需要同时持有多个对象锁造成”的,要解决这个 问题,思路很简单,就是:同一个代码块,不要同时持有两个对象 锁。

/**
* 口红类
*/
     class Lipstick{
     }
/**
* 镜子类
*/
    class Mirror{
    }
/**
* 化妆线程类
*/
class Makeup extends Thread{
    private int flag; //flag = 0 :拿着口红,flag != 0 :拿着镜子
  private String girlName;
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();
    public Makeup(int flag,String girlName){
        this.flag = flag;
        this.girlName = girlName;
   }
    @Override
    public void run() {
        this.doMakeup();
   }
    /**
     * 开始化妆
     */
    public void doMakeup(){
        if(this.flag == 0){
            synchronized (lipstick){
               System.out.println(this.girlName+" 拿着口红");
                try {
                    Thread.sleep(1000);
               } catch(InterruptedException e) {
                    e.printStackTrace();
               }
           }
 synchronized (mirror){
              System.out.println(this.girlName+" 拿着镜子");
           }
       }else{
            synchronized (mirror){
              System.out.println(this.girlName+" 拿着镜子");
                try {
                    Thread.sleep(2000);
               } catch(InterruptedException e) {
                    e.printStackTrace();
               }
           }
            synchronized (lipstick){
              System.out.println(this.girlName+" 拿着口红");
           }
       }
   }
}
public class DeadLockThread {
    public static void main(String[] args) {
  new Makeup(0,"小丫").start();
        new Makeup(1,"大丫").start();
   }
}


线程并发协作(生产者/消费者模式)



多线程环境下,我们经常需要多个线程的并发和协作。这个时候, 就需要了解一个重要的多线程并发协作模型“生产者/消费者模式”。


角色介绍


  什么是生产者?

     生产者指的是负责生产数据的模块(这里模块可能是:方法、对 象、线程、进程)。


 什么是消费者?

   消费者指的是负责处理数据的模块(这里模块可能是:方法、对 象、线程、进程)。


 什么是缓冲区?

   消费者不能直接使用生产者的数据,它们之间有个“缓冲区”。生 产者将生产好的数据放入“缓冲区”,消费者从“缓冲区”拿要处理 的数据。


缓冲区是实现并发的核心,缓冲区的设置有两个好处:


1 实现线程的并发协作

有了缓冲区以后,生产者线程只需要往缓冲区里面放置数据,而 不需要管消费者消费的情况;同样,消费者只需要从缓冲区拿数 据处理即可,也不需要管生产者生产的情况。 这样,就从逻辑 上实现了“生产者线程”和“消费者线程”的分离,解除了生产者与 消费者之间的耦合。


2 解决忙闲不均,提高效率

生产者生产数据慢时,缓冲区仍有数据,不影响消费者消费;消 费者处理数据慢时,生产者仍然可以继续往缓冲区里面放置数据 。


实现生产者与消费者模式


创建缓冲区

/**
* 定义馒头类
*/
class ManTou{
    private int id;
    public ManTou(int id){
        this.id = id;
   }
  public int getId(){
        return this.id;
   }
}
/**
* 定义缓冲区类
*/
class SyncStack{
    //定义存放馒头的盒子
    private ManTou[] mt = new ManTou[10];
    //定义操作盒子的索引
    private int index;
    /**
     * 放馒头
     */
    public synchronized void push(ManTou manTou){
        //判断盒子是否已满
        while(this.index == this.mt.length){
            try {
                /**
                 * 语法:wait(),该方法必须要在 synchronized块中调用。
                 * wait执行后,线程会将持有的对象锁释放,并进入阻塞状态,
                 * 其他需要该对象锁的线程就可以继续运行了。
                 */
                this.wait();
               } catch (InterruptedException e){
                e.printStackTrace();
           }
       }
        //唤醒取馒头的线程
        /**
         * 语法:该方法必须要在synchronized块中调用。
         * 该方法会唤醒处于等待状态队列中的一个线程。
         */
        this.notify();
        this.mt[this.index] = manTou;
        this.index++;
   }
    /**
     * 取馒头
     */
    public synchronized ManTou pop(){
        while(this.index == 0){
            try {
                /**
                 * 语法:wait(),该方法必须要在synchronized块中调用。
                 * wait执行后,线程会将持有的对象锁释放,并进入阻塞状态,
                 * 其他需要该对象锁的线程就可以继续运行了。
                 */
                this.wait();
           } catch (InterruptedException e)
           {
                e.printStackTrace();
           }
       }
        this.notify();
        this.index--;
        return this.mt[this.index];
   }
}
public class TestProduceThread {
    public static void main(String[] args) {
   }
}


创建生产者消费者线程

/**
* 定义馒头类
*/
class ManTou{
    private int id;
    public ManTou(int id){
        this.id = id;
   }
    public int getId(){
        return this.id;
   }
}
/**
* 定义缓冲区类
*/
class SyncStack{
    //定义存放馒头的盒子
    private ManTou[] mt = new ManTou[10];
    //定义操作盒子的索引
    private int index;
    /**
     * 放馒头
     */
    public synchronized void push(ManTou manTou){
        //判断盒子是否已满
        while(this.index == this.mt.length)
          {
            try {
                /**
                 * 语法:wait(),该方法必须要在 synchronized块中调用。
                 * wait执行后,线程会将持有的对象锁释放,并进入阻塞状态,
                 * 其他需要该对象锁的线程就可以继续运行了。
                 */
                this.wait();
           } catch (InterruptedException e) {
                e.printStackTrace();
          }
       }
        //唤醒取馒头的线程
        /**
         * 语法:该方法必须要在synchronized块中调用。
         * 该方法会唤醒处于等待状态队列中的一个线程。
         */
        this.notify();
        this.mt[this.index] = manTou;
        this.index++;
   }
    /**
     * 取馒头
     */
    public synchronized ManTou pop(){
        while(this.index == 0){
            try {
                /**
                 * 语法:wait(),该方法必须要在synchronized块中调用。
                 * wait执行后,线程会将持有的对象锁释放,并进入阻塞状态,
                 * 其他需要该对象锁的线程就可以继续运行了。
                 */
                this.wait();
           } catch (InterruptedException e) {
                e.printStackTrace();
           }
       }
        this.notify();
        this.index--;
        return this.mt[this.index];
   }
}
/**
* 定义生产者线程类
*/
class ShengChan extends Thread{
    private SyncStack ss;
    public ShengChan(SyncStack ss){
        this.ss = ss;
   }
    @Override
    public void run() {
       for(int i=0;i<10;i++){
           System.out.println("生产馒头:"+i);
           ManTou manTou = new ManTou(i);
           this.ss.push(manTou);
       }
   }
}
/**
* 定义消费者线程类
*/
class XiaoFei extends Thread{
    private SyncStack ss;
    public XiaoFei(SyncStack ss){
        this.ss = ss;
   }
    @Override
    public void run() {
        for(int i=0;i<10;i++){
           ManTou manTou = this.ss.pop();
            System.out.println("消费馒头:"+i);
       }
   }
}
public class ProduceThread {
    public static void main(String[] args)
{
        SyncStack ss = new SyncStack();
        new ShengChan(ss).start();
        new XiaoFei(ss).start();
   }
}


线程并发协作总结


线程并发协作(也叫线程通信


  生产者消费者模式:


1 生产者和消费者共享同一个资源,并且生产者和消费者之间相互 依赖,互为条件。

2 对于生产者,没有生产产品之前,消费者要进入等待状态。而生 产了产品之后,又需要马上通知消费者消费。

3 对于消费者,在消费之后,要通知生产者已经消费结束,需要继 续生产新产品以供消费。

4 在生产者消费者问题中,仅有synchronized是不够的。 synchronized可阻止并发更新同一个共享资源,实现了同步但 是synchronized不能用来实现不同线程之间的消息传递(通 信)。

5 那线程是通过哪些方法来进行消息传递(通信)的呢?见如下总 结:

6 以上方法均是java.lang.Object类的方法;

都只能在同步方法或者同步代码块中使用,否则会抛出异常。


OldLu建议 在实际开发中,尤其是“架构设计”中,会大量使用这个模式。 对于初学者了解即可,如果晋升到中高级开发人员,这就是必 须掌握的内容。


目录
相关文章
|
18天前
|
存储 Java 数据库连接
java多线程之线程通信
java多线程之线程通信
|
29天前
|
存储 缓存 NoSQL
Redis单线程已经很快了6.0引入多线程
Redis单线程已经很快了6.0引入多线程
31 3
|
1月前
|
消息中间件 安全 Linux
线程同步与IPC:单进程多线程环境下的选择与权衡
线程同步与IPC:单进程多线程环境下的选择与权衡
58 0
|
3天前
|
安全 算法 Java
JavaSE&多线程&线程池
JavaSE&多线程&线程池
17 7
|
3天前
|
并行计算 算法 安全
Java从入门到精通:2.1.3深入学习Java核心技术——掌握Java多线程编程
Java从入门到精通:2.1.3深入学习Java核心技术——掌握Java多线程编程
|
3天前
|
存储 缓存 NoSQL
为什么Redis使用单线程 性能会优于多线程?
在计算机领域,性能一直都是一个关键的话题。无论是应用开发还是系统优化,我们都需要关注如何在有限的资源下,实现最大程度的性能提升。Redis,作为一款高性能的开源内存数据库,因其出色的单线程性能而备受瞩目。那么,为什么Redis使用单线程性能会优于多线程呢?
15 1
|
25天前
|
安全 Java 容器
Java并发编程:实现高效、线程安全的多线程应用
综上所述,Java并发编程需要注意线程安全、可见性、性能等方面的问题。合理使用线程池、同步机制、并发容器等工具,可以实现高效且线程安全的多线程应用。
14 1
|
26天前
|
JavaScript 前端开发
JS 单线程还是多线程,如何显示异步操作
JS 单线程还是多线程,如何显示异步操作
22 2
|
1月前
|
Java 调度 C#
C#学习系列相关之多线程(一)----常用多线程方法总结
C#学习系列相关之多线程(一)----常用多线程方法总结
|
1月前
|
安全 编译器 C#
C#学习相关系列之多线程---lock线程锁的用法
C#学习相关系列之多线程---lock线程锁的用法

相关实验场景

更多