前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JAVASE中的多线程小结,多生产多消费案例.

JAVASE中的多线程小结,多生产多消费案例.

原创
作者头像
帅的一麻皮
修改2020-06-24 14:53:46
6840
修改2020-06-24 14:53:46
举报
文章被收录于专栏:前端与Java学习前端与Java学习

如何创建线程:

1.继承Thread类

1.1定义一个类继承Thread类

因为Thread类是描述线程事务,然后具备线程应该有的功能

代码语言:javascript
复制
 Thread t= new Thread();
 t.start();
 //如果这样做,start方法调用的是Thread类中的run方法,而这个线程中的run方法没有做什么事情
 //更重要的是这个run方法中并没有定义我们需要让线程执行的代码

1.2重写run方法

1.3创建子类对象,就是创建线程对象

1.4调用start方法,开启线程并且执行,同时会告诉jvm去调用run方法

2.实现Runnable接口(避免了单继承的局限性,所以比较常用)

2.1定义一个类,实现Runnable接口,重写run方法

2.2创建实现类对象

2.3创建Thread类对象,并且把实现类对象作为参数传递到Thread对象中

因为线程任务已经被封装到Runnable接口中的run方法中,而这个run方法属于Runable接口的子类对象,所以要将这个子类对象作为参数传递给Thread类的构造方法,这样,线程对象创建时就可以明确要运行线程的任务。

2.4调用线程类的start方法

3.区别小结:

线程对象调用run方法和调用start方法的区别:

调用run方法不开启线程,仅仅是对象调用方法

调用start方法开启线程,并且让jvm调用run方法在开启的线程中执行

栈区域内存的分配:

多线程执行时,在栈内存中,其实每一个执行线程都有一片自己所属的栈内存空间进行方法的压栈和弹栈。当执行线程的任务结束了,线程自动在栈内存中释放了,当所有的执行线程都结束了,进程就结束了。

Thread.currentThread().getName():获取当前执行线程的名称,当线程多个时数字顺延

两种方式的不同:

线程分为两部分:一部分叫线程对象,一部分叫线程任务

直接继承Thread类方式:线程对象和线程任务耦合在一起,创建Thread类的子类对象, 既是线程又是线程任务

实现接口的方式:将线程任务单独分离出来封装成对象

synchronize同步方法和同步代码块的区别:

同步方法使用的锁固定是this,同步代码块使用的锁可以是任意对象,如果在一个线程任务中需要写上两个以上的同步那么能够使用的只能够是同步代码块。

静态同步方法:

使用的锁不是this,因为静态和对象没有关系,而这个锁对象是字节码文件对象,类名.class

同步的弊端:

当线程任务(run)中出现多个同步(多个锁)时,如果同步中嵌套了其他的同步,这时候容易引发死锁。

多线程案例(多生产者-多消费者模式):

代码语言:java
复制
public class ThreadDemo1{
    //多生产与多消费  ---多线程案例
    /*
    生产一个资源,消费一个资源
    当有资源时,则消费,无资源时,则生产
    
    定义标记来标记当前是否有资源,如果标记为true,则代表有资源,生产者先告诉消费者来消费
    然后再进去到等待状态,如果标记为false,则代表没有资源,消费者来告诉生产者生产资源,然后再去等待
    
    问题1:被唤醒的线程没有继续判断标记,所以会造成一个生产者生产多个一个消费者消费多个,
    我们只需要吧if改为while进行多次判断即可解决。
    问题2:出现死锁即全部等待状态,因为没有办法指定唤醒哪个线程所以我们只能把所有的线程全部唤醒notifyAll。
    
    提升效率:Lock实现提供了比使用synchronized方法和语句可获得的更广泛的锁定操作,此实现允许更加灵活的结构,可以具有
    差别很大的属性。
    */
    public static void main(String[] args){
        //创建资源对象
        Resource r=new Resource();
        //创建线程任务
        Producer p=new Producer(r);
        Consumer c=new Consumer(r);
        //创建线程
        Thread t1 = new Thread(p);//负责生产
        Thread t2 = new Thread(c);//负责消费
        Thread t3 = new Thread(p);//负责生产
        Thread t4 = new Thread(c);//负责消费
        //开启线程
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}
//描述资源
class Resource{
    //属性:名字 编号
    private String name;
    private int count = 1;
    //定义锁对象
    private Lock lock = new ReentrantLock();
    private Condition p = lock.newCondition();//生产者
    private Condition c = lock.newCondition();//消费者
    //定义标记
    private boolean flag=false;
    //方法:对资源名字赋值set   获取资源get
    public void set(String name){
        //获取锁
        lock.lock();
        try{
            while(flag){//当为true则代表有资源,生产者等待
                try{
                    //wait();
                    /*
                    之前的wait的锁对象是this,现在的锁已经是lock了,所以需要方法来作用到新锁上,将之前原有的
                    所有方法封装在Condition对象中,想要获取方法,首先获取Condition对象
                    
                    希望本方唤醒对方中的一个,一个锁,但是操作线程状态的方法不止一个,可以在一个锁上挂多个监视
                    器对象
                    */
                    c.await();
                }catch(){
                
                }
            }
            this.name=name+count;
            count++;
            System.out.println(Thread.currentThread().getName()+"生产者"+this.name);
            //将标记改为true
            flag=true;
            //叫醒消费者的任意一个
            //notifyAll();
            c.signalAll();
        }finally{
            lock.unlock();
        }
    }
    public void get(){
        lock.lock();
        try{
            while(!flag){//flag为false则代表没资源,消费者等待
                try{
                    //wait();
                    p.await();
                }catch(){
                
                }
            }
            System.out.println(Thread.currentThread().getName()+"消费者"+this.name);
            //将标记改为false
            flag=false;
            //叫醒生产者的任意一个
            //notifyAll();
            p.signalAll();
        }finally{
            lock.unlock();
        }
    }
}

//描述生产者
class Producer implements Runnable{
    private Resource r;
    public Producer(Resource r){
        this.r=r;
    }
    
    public void run(){
        //生产资源
        while(true){
            r.set("寿司");
        }
        
    }
}

//描述消费者
class Consumer implements Runnable{
    private Resource r;
    public Consumer(Resource r){
       this.r=r;
    }
    public void run(){
       //消费资源
       while(true){
           r.get("寿司")
       }
     }
}

等待唤醒机制:

wait():等待,让线程释放执行资格以及执行权,将线程临时存到线程池中

notify():唤醒线程池中任意一个等待的线程

notifyAll():会唤醒线程池中所有的等待线程

注意:这些方法必须要定义在同步中,因为必须要标识wait notify 等方法所属的锁,同一锁上的notify只能够唤醒该锁上被wait的线程

sleep()和wait()区别:

相同点:可以让线程处于冻结状态。

不同点

  • 1.sleep必须要指定时间,wait没有指定时间,也可以指定时间
  • 2.sleep时间到,线程处于临时阻塞状态或者运行状态,wait没有指定时间,必须要通过notify或者notifyAll唤醒
  • 3.sleep不一定非要定义在同步中,但是wait方法必须定义在同步中
  • 4.如果两个方法都定在同步中,那么线程执行到sleep,不会释放锁,而wait会释放锁

线程如何停止?

stop方法过时了,有其他解决方案,就是让线程任务执行完,run方法结束,run方法通常都定义了循环,只要控制住循环就可以了,目标线程应该定期检查该变量,并且如果该变量指示它要停止运行,则从其运行方法依次返回。

守护(后台)线程:当进程中所有前台线程都结束了,那么无论后台线程处于什么状态,都会结束,从而进程就结束了

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
作者已关闭评论
0 条评论
热度
最新
推荐阅读
目录
  • 如何创建线程:
    • 1.继承Thread类
      • 2.实现Runnable接口(避免了单继承的局限性,所以比较常用)
        • 3.区别小结:
          • synchronize同步方法和同步代码块的区别:
          • 静态同步方法:
          • 同步的弊端:
          • 多线程案例(多生产者-多消费者模式):
          • 等待唤醒机制:
          • sleep()和wait()区别:
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档