死锁Lock锁等待唤醒机制线程组线程池定时器单例设计模式_DAY24

Posted AI菌

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了死锁Lock锁等待唤醒机制线程组线程池定时器单例设计模式_DAY24相关的知识,希望对你有一定的参考价值。

1:线程(理解)

    (1)死锁

       概念:

           同步中,多个线程使用多把锁之间存在等待的现象。

       原因分析:

         a.线程1将锁1锁住,线程2将锁2锁住,而线程1要继续执行锁2中的代码,线程2要继续执行锁1中的代码,

           但是此时,两个锁均处于锁死状态。最终导致两线程相互等待,进入无限等待状态。

          b.有同步代码块的嵌套动作。

        解决方法:

          不要写同步代码块嵌套。

       例子:cn.itcast2.DeadLockThread;  cn.itcast2.demo

技术分享
package cn.itcast2;

public class DeadLockThread extends Thread {

    boolean flag;//定义标记,用来指定要执行的代码
    public DeadLockThread(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        if(flag) {//flag赋值为true时,执行的代码
            synchronized (Demo.LOCK1) {
                System.out.println("if中锁1");
                /*try {      //加上睡眠时间,就不正常了,有可能会出现死锁现象
                    sleep(100l);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }*/
                synchronized (Demo.LOCK2) {
                    System.out.println("if中锁2");
                }
            }
        } else {//flag赋值为false时,执行的代码
            try { //加上睡眠时间,等线程1执行完,就正常了,不会出现死锁现象
                    sleep(100l);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            synchronized (Demo.LOCK2) {
                System.out.println("else中锁2");
                synchronized (Demo.LOCK1) {
                    System.out.println("else中锁1");
                }
            }
        }
    }
}
View Code
技术分享
package cn.itcast2;
/*
 * 线程死锁:
 *         线程锁相互等待
 */
public class Demo {

    public static final Object LOCK1 = new Object();
    public static final Object LOCK2 = new Object();
    
    public static void main(String[] args) {
        
        DeadLockThread dlt = new DeadLockThread(true);
        DeadLockThread dlt2 = new DeadLockThread(false);
        
        dlt.start();
        dlt2.start();
    }
    
}
View Code

 

    (2)JDK5的新特性:Lock

       它把什么时候获取锁,什么时候释放锁的时间给明确了。

       例子:cn.itcast.demo3  cn.itcast.MyTicket

技术分享
package cn.itcast;

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

/*
 * Lock锁:
 *         就是实现同步的另外的一种锁。另外的一种同步操作。
 *         Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。
 * 
 *         子类对象:ReentrantLock
 * 
 *         public void lock() 上锁
 *         public void unlock()  解锁
 */
public class Demo3 {

    public static final Object MY_LOCK = new Object();
    public static final Lock MY_LOCK2 = new ReentrantLock();
    
    public static void main(String[] args) {
        
        MyTicket mt = new MyTicket();
        
        Thread thread = new Thread(mt,"唐嫣");
        Thread thread2 = new Thread(mt,"柳岩");
        Thread thread3 = new Thread(mt,"高圆圆");
        
        thread.start();
        thread2.start();
        thread3.start();
    }

}
View Code
技术分享
package cn.itcast;

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

public class MyTicket implements Runnable {

    private int number = 100;
    Lock lock = new ReentrantLock();
    
    @Override
    public void run() {

        //卖票的动作
        while(true) {
            
//            synchronized(Demo3.MY_LOCK){
                lock.lock();
//                Demo3.MY_LOCK2.lock();
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                
                if(number>0) {
                    System.out.println(Thread.currentThread().getName()+":"+number--);
                }
                lock.unlock();
//                Demo3.MY_LOCK2.unlock();
//            }
        }
        
    }

}
View Code

 

    (3)线程间的通信(生产者与消费者问题)

       不同种类的线程针对同一个资源的操作。生产者线程生产与消费者线程消费,操作同一数据。

       例子:cn.itcast3.*

技术分享
package cn.itcast3;

public class CRunnable implements Runnable {
    Person p;

    public CRunnable(Person p) {
        this.p = p;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (p) {
                //c
                System.out.println(p);
            }
        }
    }

}
View Code
技术分享
package cn.itcast3;
/*
 * 生产者消费者问题:
 *         
 *         共享数据:一个Person对象
 *         生产者:为该对象属性赋值
 *         消费者:获取对象属性值
 * 
 * 采取第二种方式完成。
 */
public class Demo {

    public static void main(String[] args) {

        Person person = new Person();
        //生产线程运行目标
        PRunnable p = new PRunnable(person);
        //消费线程运行目标
        CRunnable c = new CRunnable(person);
        //创建生产线程
        Thread pThread = new Thread(p);
        //创建消费线程
        Thread cThread = new Thread(c);
        //开启消费与生产
        pThread.start();
        cThread.start();
    }

}
View Code
技术分享
package cn.itcast3;
/*
 * 被共享的对象对应的类
 */
public class Person {

    private String name;
    private int age;

    public Person() {
        super();
    }

    public Person(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
View Code
技术分享
package cn.itcast3;
/*
 * 生产者线程执行目标
 */
public class PRunnable implements Runnable {
    
    Person p;
    int x = 0;
    
    public PRunnable(Person p) {
        this.p = p;
    }

    @Override
    public void run() {

        while(true) {
            synchronized (p) {
                if (x % 2 == 0) {
                    p.setName("唐嫣");
                    p.setAge(28);
                } else {
                    p.setName("刘正风");
                    //p
                    p.setAge(29);
                }
                x++;
            }
        }
    }

}
View Code

 

    (4)等待唤醒机制

       涉及并非是Thread类的方法,而是Object类的两个方法:因为锁可以为共享数据本身可以是任意的对象,在runnable中进行等待唤醒当前所在线程。

       等待:

           public final void wait() throws InterruptedException

           让当前线程进入等待状态,如果线程进入该状态,不唤醒或打断,不会解除等待状态。

           进入等待状态时会释放锁。

       唤醒:

           public final void notify()

           唤醒正在等待的线程。

           继续等待之后的代码执行。

       sleepwait的区别:

           sleep指定时间,wait可指定可不指定。

           sleep释放执行权,不释放锁。因为一定可以醒来。

           wait释放执行权与锁。

       例子:cn.itcast4.*

技术分享
package cn.itcast4;

/*
 * 消费者线程执行目标
 */
public class CRunnable implements Runnable {
    Person p;

    public CRunnable(Person p) {
        this.p = p;
    }

    // 判断>>生产/消费>>修改标志位>>唤醒
    @Override
    public void run() {
        while (true) {
            synchronized (p) {
                // 判断是否有数据
                if (!p.flag) {// 如果没有数据,消费者等待
                    try {
                        p.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                // 消费
                // System.out.println(p);
                System.out.println(p.getName() + ":" + p.getAge());
                // 将数据标志位,置为没有值的false
                p.flag = false;
                // 唤醒正在等待的其他线程
                p.notify();
            }
        }
    }

}
View Code
技术分享
package cn.itcast4;
/*
 * 生产者消费者问题:
 *         
 *         共享数据:一个Person对象
 *         生产者:为该对象属性赋值
 *         消费者:获取对象属性值
 * 
 * 采取第二种方式完成。
 */
public class Demo {

    public static void main(String[] args) {

        Person person = new Person();
        //生产线程运行目标
        PRunnable p = new PRunnable(person);
        //消费线程运行目标
        CRunnable c = new CRunnable(person);
        //创建生产线程
        Thread pThread = new Thread(p);
        //创建消费线程
        Thread cThread = new Thread(c);
        //开启消费与生产
        pThread.start();
        cThread.start();
    }

}
View Code
技术分享
package cn.itcast4;
/*
 * 被共享的对象对应的类
 */
public class Person {

    private String name;
    private int age;
    //标记是否有数据。true为有数据,需要消费。false没有数据,需要生产。
    boolean flag = false;

    public Person() {
        super();
    }

    public Person(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
View Code
技术分享
package cn.itcast4;

/*
 * 生产者线程执行目标
 */
public class PRunnable implements Runnable {

    Person p;
    int x = 0;

    public PRunnable(Person p) {
        this.p = p;
    }

    // 判断>>生产/消费>>修改标志位>>唤醒
    @Override
    public void run() {

        while (true) {
            synchronized (p) {
                // 判断是否有数据
                if (p.flag) {// 如有有数据,生产者等待
                    try {
                        p.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 生产
                if (x % 2 == 0) {
                    p.setName("唐嫣");
                    p.setAge(28);
                } else {
                    p.setName("刘正风");
                    p.setAge(29);
                }
                x++;

                // 将数据标志位,置为有值的true
                p.flag = true;
                // 唤醒正在等待的其他线程
                p.notify();
            }
        }
    }

}
View Code

 

     (5)线程组

       多个线程出现时,可以将线程分组,统一处理。

       涉及线程组类:ThreadGroup

       注意:

           线程组可以包含线程或者线程组。

           当前线程只能访问所在线程组或者子组。不能访问父组或者兄弟组。

           如果没有指定线程组,则属于main线程组。

       主要方法:

           public final String getName()

           public final void setDaemon(boolean?daemon)

           无线程添加方法,设置线程组在Thread构造方法中。

       例子:cn.itcast5.demo

技术分享
package cn.itcast5;
/*
 * 线程组:
 *         多个线程出现时,可以将线程分组,统一处理
 *         Thread相关线程组的方法:
 *             public Thread(ThreadGroup group,Runnable target,String name)
 *             public final ThreadGroup getThreadGroup()
 *         ThreadGroup:
 *             public final String getName()
 */
public class Demo {

    public static void main(String[] args) {
        
        ThreadGroup tg = new ThreadGroup("唐嫣的小组");
        Thread thread = new Thread(tg, new Runnable() {
            
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(i);
                }
            }
        },"线程1");
        
        Thread thread2 = new Thread(new Runnable() {
            
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(i);
                }
            }
        },"线程1");
        
        System.out.println("thread:"+thread.getThreadGroup().getName()); //thread:唐嫣的小组
        System.out.println("thread2:"+thread2.getThreadGroup().getName()); //thread2:main
        System.out.println("main:"+Thread.currentThread().getThreadGroup().getName()); //main:main
        
    }
}
View Code

 

     (6)线程池

      将线程放置到同一个线程池中,其中的线程可以反复使用,无需反复创建线程而消耗过多资源。

      Executors:线程池创建工厂类

        返回线程池方法(还有其他方法) public static ExecutorService newFixedThreadPool(int?nThreads)

      ExecutorService:线程池类

         主要方法:

             Future<?> submit(Runnable?task)

             <T> Future<T> submit(Callable<T>?task)

             void shutdown()

        注意:

            线程池提交方法后,程序并不终止,是因为线程池控制了线程的关闭等。

            Callable:相当于有返回值类型的runnable

            Future是将结果抽象的接口。可以将线程执行方法的返回值返回。

      例子:cn.itcast5.demo2

技术分享
package cn.itcast5;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/*
 * 线程池:用来存放线程。
 * 一、Executors:线程池工厂类
 * 主要方法:
 * public static ExecutorService newFixedThreadPool(int nThreads)
 * 
 *二、 ExecutorService接口
 * 主要方法:
 * 1、Future<?> submit(Runnable task)
 * 2、<T> Future<T> submit(Callable<T> task)
 * 3、void shutdown()
 */
public class Demo2 {

    public static void main(String[] args) {
        
        //使用线程池工厂创建线程池对象
        ExecutorService es = Executors.newFixedThreadPool(2);
        
        //使用线程池,第一次提交runnable
        es.submit(new Runnable(){

            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println("第一次:"+i);
                }
            }});

        //使用线程池,第二次提交runnable
        es.submit(new Runnable(){

            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println("第二次"+i);
                }
            }});
        
        //不一定要关闭,但是这里关闭了线程池
        es.shutdown();
    }
}
View Code

 

     (7)线程实现的第三种方式

      a.使用线程池工厂创建线程池对象  : ExecutorService es = Executors.newFixedThreadPool(2);

      b.实现Callable接口,public public class MyCallable implements Callable<String>{},然后重写其中的call()方法,相当于runnable中的run()方法

      c.创建Callable类的实例对象  : MyCallable mc = new MyCallable();

      d.线程池提交Callable,返回Future结果 :  Future<String> submit2 = es.submit(mc);

      e.通过Future对象获取call方法的返回结果: System.out.println(submit2.get());

      例子:cn.itcast5.MyCallable   cn.itcast5.demo3

技术分享
package cn.itcast5;

import java.util.concurrent.Callable;

/*
 * 定义一个Callable类。
 * 实现多线程的第三种方式。
 */
public class MyCallable implements Callable<String>{

    //重写call方法,相当于runnable中的run方法
    @Override
    public String call() throws Exception {
        return "经过多线程的学习,我发现,编程真的很有意思!";
    }

}
View Code
技术分享
package cn.itcast5;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/*
 *  
 * Future<?> submit(Runnable task)
 * <T> Future<T> submit(Callable<T> task)
 * 
 * Future:线程执行返回结果
 * V get()
      throws InterruptedException,
             ExecutionException   返回计算结果
   Callable:
 */
public class Demo3 {

    public static void main(String[] args) throws Exception {
        
        //使用线程池工厂创建线程池对象
        ExecutorService es = Executors.newFixedThreadPool(2);
        
        //使用线程池,提交runnable
        Future<?> submit = es.submit(new Runnable(){

            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println(i);
                }
            }});
        
        Object object = submit.get();
        System.out.println(object);  //输出结果 null
        
        //实现多线程的第三种方式。
        //创建Callable类的实例对象
        MyCallable mc = new MyCallable();
        //线程池提交Callable,返回Future结果
        Future<String> submit2 = es.submit(mc);
        System.out.println(submit2); //打印的是地址
        //通过Future对象获取call方法的返回结果。
        System.out.println(submit2.get()); // call()方法返回的内容:经过多线程的学习,我发现,编程真的很有意思!
    }

}
View Code

 

     (8)定时器

        定时器:Timer

           构造方法:

              public Timer()

           主要方法:

              public void schedule(TimerTask task, Date time) 指定时间点完成指定任务

              public void schedule(TimerTask task,Date firstTime,long period) 指定第一次开始时间和后边重复执行的时间完成指定任务

              public void schedule(TimerTask task, long delay)  在指定时间后执行指定任务一次

              public void schedule(TimerTask task,long delay, long period)  指定时间后执行指定任务第一次,再在每指定时间后,执行指定任务

       定时器任务类:TimerTask  (实现了Runnable接口)

           public abstract void run()  要重写的真正执行的代码

       针对于cancel()方法:

           定时器的该方法终止定时器的。

           定时器任务的该方法用来取消该定时器任务的任务。

       例子:cn.itcast5.demo4 ; cn.itcast5.MyTask

技术分享
package cn.itcast5;

import java.util.Date;
import java.util.Timer;

/*
 * 定时器:Timer类
 *         构造方法:
 *             public Timer()
 *         主要方法:
 *             public void schedule(TimerTask task, Date time) 指定时间点完成指定任务
 *             public void schedule(TimerTask task,Date firstTime,long period) 指定第一次开始时间和后边重复执行的时间完成指定任务
 *             public void schedule(TimerTask task, long delay)  在指定时间后执行指定任务一次
 *             public void schedule(TimerTask task,long delay, long period)  指定时间后执行指定任务第一次,再在每指定时间后,执行指定任务
 * 定时器任务类:TimerTask  实现了Runnable接口
 *         public abstract void run()  要重写的真正执行的代码
 * 
 * 针对于cancel()方法:
 *         定时器的该方法终止定时器的。
 *         定时器任务的该方法用来取消该定时器任务的任务。
 */
public class Demo4 {

    public static void main(String[] args) {

        //创建定时器对象
        Timer timer = new Timer();
        
        //创建定时器任务对象
        MyTask myTask = new MyTask();
        
        //调用计划执行方法
        //定时器不可以重复schedule任务
//        timer.schedule(myTask, new Date(), 3000);               
//        timer.schedule(myTask, new Date(new Date().getTime()+3000), 5000);  
         
//        myTask.cancel();  任务不能先取消再执行。
        timer.schedule(myTask, 3000, 5000);
        
//        myTask.cancel();  //定时器任务的该方法用来取消该定时器任务的任务,定时器不会终止。
//        timer.cancel();   //定时器的该方法终止定时器的。
    }

}
View Code
技术分享
package cn.itcast5;

import java.util.TimerTask;
/*
 * 定义搞一个TimerTask实际类
 */
public class MyTask extends TimerTask {

    @Override
    public void run() {

        System.out.println("地震了!");
    }

}
View Code

 

    (9)最简单的线程程序代码:

       new Thread() {

           public void run() {

              for(int x=0; x<100; x++) {

                  System.out.println(x);

              }

           }

       }.start();

 

       new Thread(new Runnable(){

           public void run() {

              for(int x=0; x<100; x++) {

                  System.out.println(x);

              }

           }

       }).start();

 

2:单例设计模式(理解 面试)

    (1)保证类在内存中只有一个对象。

    (2)怎么保证:

       A:构造私有

       B:自己造一个对象

       C:提供公共访问方式

    (3)两种方式:

       A:懒汉式(面试)

           public class Student {

              private Student(){}

 

              private static Student s = null;

 

              public synchronized static Student getStudent() {

                  if(s == null) {

                     s = new Student();

                  }

                  return s;

              }

           }

 

 

       B:饿汉式(开发)

           public class Student {

              private Student(){}

 

              private static Student s = new Student();

 

              public static Student getStudent() {

                  return s;

              }

           }

    (4)JDK的一个类本身也是单例模式的。

       Runtime

 

    (5)例子:cn.itcast5.Demo5  cn.itcast5.SinglePerson2   cn.itcast5.SinglePerson

技术分享
package cn.itcast5;

public class Demo5 {

    public static void main(String[] args) {

//        SinglePerson sp = new SinglePerson("唐嫣", 28);
//        SinglePerson sp2 = new SinglePerson("唐嫣", 28);
        
        //饿汉式对象
        SinglePerson sp = SinglePerson.getInstance();
        SinglePerson sp2 = SinglePerson.getInstance();
        
        System.out.println(sp==sp2);
        //懒汉式对象
        SinglePerson2 sp3 = SinglePerson2.getInstance();
        SinglePerson2 sp4 = SinglePerson2.getInstance();
        
        System.out.println(sp3==sp4);
        
    }

}
View Code
技术分享
package cn.itcast5;
/*
 * 懒汉式单例
 */
public class SinglePerson2 {

    private static final Object LOCK = new Object();
    private String name;
    private int age;
    
    //定义一个private,static修饰的成员变量,类型是自己。
    private static SinglePerson2 sp;
    
    //经构造方法私有化,外界无法创建对象,只能自己创建自己
    private SinglePerson2(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
    //定义一个公共的,静态的,返回值类型为自己的的获取实例对象的方法。不考虑多线程
//    public static SinglePerson2 getInstance() {
//        if(sp==null) {
//            System.out.println("没有对象");
//            sp = new SinglePerson2("唐嫣", 28);
//        }
//        return sp;
//    }
    //定义一个公共的,静态的,返回值类型为自己的的获取实例对象的方法。考虑多线程,使用代码块,不考虑效率
//    public static SinglePerson2 getInstance() {
//        
//        synchronized (SinglePerson2.LOCK) {
//            if (sp == null) {
//                System.out.println("没有对象");
//                sp = new SinglePerson2("唐嫣", 28);
//            }
//        }
//        
//        return sp;
//    }

    //定义一个公共的,静态的,返回值类型为自己的的获取实例对象的方法。考虑多线程,使用同步方法,不考虑效率
//    public static synchronized SinglePerson2 getInstance() {
//        
//            if (sp == null) {
//                System.out.println("没有对象");
//                sp = new SinglePerson2("唐嫣", 28);
//            }
//        
//        return sp;
//    }
    
    //定义一个公共的,静态的,返回值类型为自己的的获取实例对象的方法。考虑多线程,使用代码块,考虑效率
    public static SinglePerson2 getInstance() {
        
        if(sp==null) {  //如果没有对象,才进行同步操作
            synchronized (SinglePerson2.LOCK) {
                if (sp == null) { //同步操作过程中,必须判断的条件
                    System.out.println("没有对象");
                    sp = new SinglePerson2("唐嫣", 28);
                }
            }
        }
        
        return sp;
    }
    
    @Override
    public String toString() {
        return "SinglePerson [name=" + name + ", age=" + age + "]";
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}
View Code
技术分享
package cn.itcast5;
/*
 * 饿汉式单例
 */
public class SinglePerson {

    private String name;
    private int age;
    
    //定义一个private,static修饰的成员变量,类型是自己。
    private static SinglePerson sp = new SinglePerson("唐嫣", 28);
    
    //经构造方法私有化,外界无法创建对象,只能自己创建自己
    private SinglePerson(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
    //定义一个公共的,静态的,返回值类型为自己的的获取实例对象的方法。
    public static SinglePerson getInstance() {
        return sp;
    }
    
    @Override
    public String toString() {
        return "SinglePerson [name=" + name + ", age=" + age + "]";
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    
    
}
View Code

 

以上是关于死锁Lock锁等待唤醒机制线程组线程池定时器单例设计模式_DAY24的主要内容,如果未能解决你的问题,请参考以下文章

大数据进阶26-Lock死锁线程间通信线程组线程池,以及定时器

线程同步

Java——线程锁,死锁,等待唤醒机制

JAVA基础再回首(二十五)——Lock锁的使用死锁问题多线程生产者和消费者线程池匿名内部类使用多线程定时器面试题

JAVA学习之等待与唤醒案例线程池Lambda表达式

内置锁synchronized下的等待通知机制