码迷,mamicode.com
首页 > 编程语言 > 详细

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

时间:2017-10-08 18:11:02      阅读:198      评论:0      收藏:0      [点我收藏+]

标签:cut   调用   except   pool   display   锁定   解决   pen   logs   

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

标签:cut   调用   except   pool   display   锁定   解决   pen   logs   

原文地址:http://www.cnblogs.com/hezhiyao/p/7637974.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!