zoukankan      html  css  js  c++  java
  • Day 2:线程与进程系列问题(二)

    补充:

    线程的创建方式二:

      1.自定义一个实现Runnable接口的类

      2.实现Runnable接口中的run方法把自定义线程的任务写在run方法中

      3.创建实现Runnable接口的对象

      4.创建Thread类的对象,并且把 3 中的对象作为实参传递(Thread thread = new Thread(Runnable target,"名字");)

      5.调用Thread对象的start方法,开启一个线程

    课后习题:一个银行账户5000块,两夫妻一个拿着 存折,一个拿着卡,开始取钱比赛,每次只能取100块,要求不准出现线程安全问题。用线程创建方法二实现

    public class Demo1 implements Runnable {
        
        static int money = 5000;
        static    Object lock = new Object();
    
        public Demo1(String string) {
            
        }
    
        public void run() {
            while(true) {
                synchronized (lock) {
                    if(money>0) {
                        System.out.println(Thread.currentThread().getName()+"取走100元,还剩"+(money-100)+"元!");
                        money-=100;
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }else {
                        System.out.println("银行卡没有存款了!");
                        break;
                    }    
                }
            }
        }
        
        public static void main(String[] args) {
            Demo1 men = new Demo1("男士");
            Demo1 women = new Demo1("女士");
            
            Thread a = new Thread(men,"男士");
            Thread b = new Thread(men,"女士");
            
            a.start();
            b.start();
        }
    
    }

    Q:为什么要把实现Runnable接口的类的对象作为实参传给Thread对象?作用是什么?

    A:作用是把实现Runnable接口的类的对象的run方法作为线程的任务代码去执行,也就是把实现Runnable接口的类的对象的作为任务代码传给Thread对象的run方法执行。

    线程安全性问题解决方法

    方式二:同步函数  :  同步函数就是使用synchronized修饰一个函数。(大多数情况锁不住)

     同步函数要注意的事项 : 同步函数的锁对象是固定的,不能由你来指定的。 

    优先使用: 同步代码块。
                   1. 同步代码块的锁对象可以由我们随意指定,方便控制。同步函数的锁对象是固定 的,不能由我们来指定。
                  2. 同步代码块可以很方便控制需要被同步代码的范围,同步函数必须是整个函数 的所有代码都被同步了。
       
     

    新内容:

    死锁

    java中同步机制解决了线程安全问题,但是也同时引发死锁现象。

    死锁现象出现的原因:

     1. 存在两个或者两个以上的线程。
     2. 存在两个或者两个以上的共享资源。
     
    解决方案: 没有方案。只能避免发生。

    //演示代码(摘录):
    class DeadLock extends Thread{
        
        public DeadLock(String name){
            super(name);
        }
        
        public void run() {
            if("张三".equals(Thread.currentThread().getName())){
                synchronized ("遥控器") {
                    System.out.println("张三拿到了遥控器,准备 去拿电池!!");
                    synchronized ("电池") {
                        System.out.println("张三拿到了遥控器与电池了,开着空调爽歪歪的吹着...");
                    }
                }
            }else if("狗娃".equals(Thread.currentThread().getName())){
                synchronized ("电池") { 
                    System.out.println("狗娃拿到了电池,准备去拿遥控器!!");
                    synchronized ("遥控器") {
                        System.out.println("狗娃拿到了遥控器与电池了,开着空调爽歪歪的吹着...");
                    }
                }    
            }    
        }
    }
    
    public class Demo2 {
        public static void main(String[] args) {
            DeadLock thread1 = new DeadLock("张三");
            DeadLock thread2 = new DeadLock("狗娃");
            //开启线程
            thread1.start();
            thread2.start();
        }    
    }

    分析:此时开启了两个线程

    当 thread1主动调用run方法在分支结构判断为真的时候获得遥控器资源后(synchronized ("遥控器") 状态为关闭), thread2已经获得了分支结构中下一个的电池资源,但由于遥控器锁和电池锁是共享资源,所以两个线程互相等待对方解锁才能获得资源,然后才能解锁状态,但是两个资源的锁状态一直为关闭又无法进入,所以进入了死锁状态。

    线程的通信

    线程通讯:一个线程完成了自己的任务时,要通知另外一个线程去完成另外一个任务.

    wait():  等待   如果线程执行了wait方法,那么该线程会进入等待的状态,等待状态下的线程必须要被其他线程调用notify方法才能唤醒。

    notify(): 唤醒    唤醒线程池等待线程其中的一个。
    notifyAll() : 唤醒线程池所有等待 线程。

    wait与notify方法要注意的事项:
     1. wait方法与notify方法是属于Object对象 的。
     2. wait方法与notify方法必须要在同步代码块或者是同步函数中才能 使用。
     3. wait方法与notify方法必需要由锁对象调用。

    作业:有一个水池,水池的容量是固定 的500L,一边为进水口,一边为出水口.要求,进水与放水不能同时进行.水池一旦满了不能继续注水,一旦放空了,不可以继续放水. 进水的速度5L/s ,  放水的速度2L/s 

    class watercave{
        
        String name; 
        int Sumwater = 0; 
        boolean flag = false; 
        
    }
    
    class in extends Thread{
        watercave p;
    
        public in(watercave p) {
            this.p = p;
        }
        
        public void run() {
            while(true) {
                synchronized (p) {
                if(p.flag==false) {
                    if(p.Sumwater<500) {
                        p.Sumwater+=5;
                        System.out.println("现在有 "+p.Sumwater+"L 水!");
                        p.flag=true;
                        p.notify();
                    }
                }else {
                        try {
                            p.wait();
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
    
    
    
    class out extends Thread{
        watercave p;
    
        public out(watercave p) {
            this.p = p;
        }
    
        public void run() {
            while(true) {
                synchronized (p) {
                    if(p.flag==true) {
                        if(p.Sumwater>0) {
                            p.Sumwater-=2;
                            System.out.println("还剩"+p.Sumwater+"L 水!");
                            p.flag=false;
                            p.notify();
                        }
                    }
                else {
                        try {
                            p.wait();
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }    
                
            }
        }
    }
    
    
    
    
    public class Demo2 {
        public static void main(String[] args) {
            watercave p = new watercave();
            
            in a = new in(p);
            out b= new out(p);
            
            a.start();
            b.start();
        }
    }

    线程的停止

    1. 停止一个线程 我们一般都会通过一个变量去控制的。
    2. 如果需要停止一个处于等待状态下的线程,那么我们需要通过变量配合notify方法或者interrupt()来使用

    //引用代码并分析
    public class Demo6 extends Thread {
        
        boolean flag = true;
        
        public Demo6(String name){
            super(name);
        }
    public synchronized void run() { int i = 0 ; while(flag){ try { this.wait(); //狗娃等待.. } catch (InterruptedException e) { System.out.println("接收到了异常了...."); } System.out.println(Thread.currentThread().getName()+":"+i); i++; } } public static void main(String[] args) { Demo6 d = new Demo6("狗娃"); d.setPriority(10); d.start(); for(int i = 0 ; i<100 ; i++){ System.out.println(Thread.currentThread().getName()+":"+i); //当主线程的i是80的时候停止狗娃线程。 //d.interrupt(); // interrupt()根本就是无法停止一个线程。 if(i==80){ d.flag = false; d.interrupt(); //把线程的等待状态强制清除,被清除状态的线程会接收到一个InterruptedException。 /*synchronized (d) { d.notify(); }*/ } } } }

    当代码中启动两个线程的时候要用synchronized将狗娃线程的执行代码给锁定,这样才不会出现线程争夺资源所出现的,无法停止一个线程的现象。

     后台线程 

    后台线程(守护线程):在一个进程中如果只剩下 了守护线程,那么守护线程也会死亡。

    Q:如何判断线程是不是守护线程?

    A:首先线程默认都不是守护线程,需要设置(*.setDaemon(true);true表示设置为守护线程,false表示设置为不是守护线程)。

       判断方法(.isDaemon();返回值为true就是守护线程,返回值为false就不是守护线程)

    ?:模拟QQ下载更新包

    public class Demo3 extends Thread {
        
        public Demo3(String name){
            super(name);
        }
    
        public void run() {
            for(int i = 1 ; i<=100 ; i++){
                System.out.println("更新包目前下载"+i+"%");
                if(i==100){
                    System.out.println("更新包下载完毕,准备安装..");
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        
        public static void main(String[] args) {
             Demo3 d = new Demo3("后台线程");
             d.setDaemon(true); 
             d.start();
             for(int i = 1 ; i<=100 ; i++){
                 System.out.println(Thread.currentThread().getName()+":"+i);
             }
        }
    }

    效果图

     join方法:一个线程如果执行了join语句,那么就有新的线程加入,执行该语句的线程必须让步给新加入的线程完成任务,然后才能继续执行任务。

    //使用方法
    class  Mon extends Thread{
        public void run() {
            System.out.println("妈妈洗菜");
            System.out.println("妈妈切菜");
            System.out.println("妈妈准备炒菜,发现没有酱油了..");
            //叫儿子去打酱油
            Son s= new Son();
            s.start();
            try {
                s.join();  
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            System.out.println("妈妈继续炒菜");
            System.out.println("全家一起吃饭..");        
        }
    } 
    
    class Son extends Thread{
        
        public void run() {
            System.out.println("儿子下楼..");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("儿子一直往前走");
            System.out.println("儿子打完酱油了");
            System.out.println("上楼,把酱油给老妈");
        }
    }
    
    public class Demo8 {
        
        public static void main(String[] args) {
            Mon m = new Mon();
            m.start();
        
        }
    }

    进程与线程篇终结

  • 相关阅读:
    Vue基础
    Document
    Document
    Document
    Document
    Document
    Document
    Document
    Document
    Document
  • 原文地址:https://www.cnblogs.com/JYDesigner/p/9327150.html
Copyright © 2011-2022 走看看