javaSE第二十四天

第二十四天    363

1:多线程(理解)    363

(1)JDK5以后的Lock    363

A:定义    363

B:方法:    364

C:具体应用(以售票程序为例)    364

1,. SellTicket类    364

2,. SellTicketDemo测试类    365

(2)死锁问题的描述和代码体现    365

1. DieLockDemo测试类    365

2. DieLock类(该类继承自Thread    366

3. MyLock(锁对象类)    367

(3)生产者和消费者多线程体现(线程间通信问题)    367

A:最基本的版本,只有一个数据。    367

1. Student资源类    367

2. SetThread(生产者类)    368

3. GetThread(消费者类)    368

4. StudentDemo测试类    369

B:改进版本,给出了不同的数据,并加入了同步机制    369

1. Student资源类    369

2. SetThread(生产者类)    370

3. GetThread(消费者)    370

4. StudentDemo(测试类)    371

C:等待唤醒机制改进该程序,让数据能够实现依次的出现    372

1. Student(资源类)    372

2. SetThread(生产者类)    373

3. GetThread(消费者类)    374

4. StudentDemo(测试类)    374

D:等待唤醒机制的代码优化。把数据及操作都写在了资源类中    376

1. Student(资源类,把数据及操作都写在资源类中)    376

2. SetThread(生产者类)    377

3. GetThread(消费者类)    378

4. StudentDemo(测试类)    378

(4)线程组    380

1. MyRunnable(线程类)    380

2. ThreadGroupDemo(测试类)    380

(5)线程池    382

1. MyRunnable(线程类)    382

2. ExecutorsDemo(线程池类)    382

(6)多线程实现的第三种方案    383

(7)匿名内部类方法实现多线程    386

(8)定时器的介绍    387

1. TimerDemo类(任务完成之后,可以终止定时器)    387

2. TimerDemo2类(任务完成之后,不终止定时器)    388

3. 案例:定时删除某个目录下的所有文件    389

(9)多线程的面试题    390

2:设计模式(理解)    391

(1)面试对象的常见设计原则    391

(2)设计模式概述和分类    391

(3)改进的设计模式    391

A:简单工厂模式    391

B:工厂方法模式    393

C:单例模式(掌握)    395

a:饿汉式:    395

b:懒汉式:    396

(4)Runtime    398

 

 

 

第二十四天

1:多线程(理解)
    (1)JDK5以后的Lock

        A:定义

JDK5以后的针对线程的锁定操作和释放操作(Lock是一个接口,具体实例由其实现类ReentrantLock

        B:方法:

            实现类ReenTrantlock的方法:

            void lock()加锁

            void unlock():解锁

        C:具体应用(以售票程序为例)

            1,. SellTicket

import java.util.concurrent.locks.Lock;

import java.util.concurrent.locks.ReentrantLock;

 

public class SellTicket implements Runnable {

 

    // 定义票

    private int tickets = 100;

 

    // 定义锁对象

    private Lock lock = new ReentrantLock();

 

    @Override

    public void run() {

        while (true) {

            try {

                // 加锁

                lock.lock();

                if (tickets > 0) {

                    try {

                        //为了模拟真实环境,对线程进行了延迟

                        Thread.sleep(100);

                    } catch (InterruptedException e) {

                        e.printStackTrace();

                    }

                    System.out.println(Thread.currentThread().getName()

                            + "正在出售第" + (tickets--) + "张票");

                }

            } finally {

                // 释放锁

                lock.unlock();

            }

        }

    }

 

}

 

 

            2,. SellTicketDemo测试类

/*

* 虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,

* 为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock

*

* Lock

*         void lock(): 获取锁。

*         void unlock():释放锁。

* ReentrantLockLock的实现类.

* 通过ReentrantLock类来创建锁对象

*/

public class SellTicketDemo {

    public static void main(String[] args) {

        // 创建资源对象

        SellTicket st = new SellTicket();

 

        // 创建三个窗口

        Thread t1 = new Thread(st, "窗口1");

        Thread t2 = new Thread(st, "窗口2");

        Thread t3 = new Thread(st, "窗口3");

 

        // 启动线程

        t1.start();

        t2.start();

        t3.start();

    }

}

 

 

    (2)死锁问题的描述和代码体现

        1. DieLockDemo测试类

/*

* 同步的弊端:

*         A:效率低

*         B:容易产生死锁

*

* 死锁:

*         两个或两个以上的线程在争夺资源的过程中,发生的一种相互等待的现象。

*

* 举例:

*         中国人,美国人吃饭案例。

*         正常情况:

*             中国人:筷子两支

*             美国人:刀和叉

*         现在:

*             中国人:筷子1支,刀一把

*             美国人:筷子1支,叉一把

*/

public class DieLockDemo {

    public static void main(String[] args) {

        DieLock dl1 = new DieLock(true);

        DieLock dl2 = new DieLock(false);

 

        dl1.start();

        dl2.start();

    }

}

 

 

        2. DieLock类(该类继承自Thread

/**

* 死锁演示

* @author asus1

*

*/

public class DieLock extends Thread {

 

    private boolean flag;

 

    public DieLock(boolean flag) {

        this.flag = flag;

    }

 

    @Override

    public void run() {

        if (flag) {

            synchronized (MyLock.objA) {

                System.out.println("if objA");

                synchronized (MyLock.objB) {

                    System.out.println("if objB");

                }

            }

        } else {

            synchronized (MyLock.objB) {

                System.out.println("else objB");

                synchronized (MyLock.objA) {

                    System.out.println("else objA");

                }

            }

        }

    }

}

 

        3. MyLock(锁对象类)

/**

* 死锁演示

* @author asus1

*

*/

public class MyLock {

    // 创建两把锁对象

    public static final Object objA = new Object();

    public static final Object objB = new Object();

}

 

    (3)生产者和消费者多线程体现(线程间通信问题)

        以学生作为资源来实现的

        

        资源类:Student

        设置数据类:SetThread(生产者)

        获取数据类:GetThread(消费者)

        测试类:StudentDemo

        

        代码:

            A:最基本的版本,只有一个数据。

                1. Student资源类

/*

* Student:资源类

*/

public class Student {

    public String name;

    public int age;

}

 

                2. SetThread(生产者类)

/*

* 生产者类:

*/

public class SetThread implements Runnable {

    //保证每个线程是对同一个对象进行操作

    private Student s;

 

    public SetThread(Student s) {

        this.s = s;

    }

 

    @Override

    public void run() {

        // Student s = new Student();

        s.name = "林青霞";

        s.age = 27;

    }

}

 

                3. GetThread(消费者类)

/*

* 消费者类

*/

public class GetThread implements Runnable {

    //保证每个线程是对同一个对象进行操作

    private Student s;

 

    public GetThread(Student s) {

        this.s = s;

    }

 

    @Override

    public void run() {

        // Student s = new Student();

        System.out.println(s.name + "---" + s.age);

    }

}

 

 

                4. StudentDemo测试类

/*

* 分析:

*         资源类:Student    

*         设置学生数据:SetThread(生产者)

*         获取学生数据:GetThread(消费者)

*         测试类:StudentDemo

*

* 问题1:按照思路写代码,发现数据每次都是:null---0

* 原因:我们在每个线程中都创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个

* 如何实现呢?

*         在外界把这个数据创建出来,通过构造方法传递给其他的类。

*

*/

public class StudentDemo {

    public static void main(String[] args) {

        //创建资源

        Student s = new Student();

        

        //设置和获取的类

        SetThread st = new SetThread(s);

        GetThread gt = new GetThread(s);

 

        //线程类

        Thread t1 = new Thread(st);

        Thread t2 = new Thread(gt);

 

        //启动线程

        t1.start();

        t2.start();

    }

}

 

 

            B:改进版本,给出了不同的数据,并加入了同步机制

                1. Student资源类

/*

* 资源类:

*/

public class Student {

    String name;

    int age;

}

 

 

                2. SetThread(生产者类)

/*

* 生产者类:

*/

public class SetThread implements Runnable {

 

    private Student s;

    private int x = 0;

 

    public SetThread(Student s) {

        this.s = s;

    }

 

    @Override

    public void run() {

        while (true) {

            //加入了同步代码块,要保证多个线程是同意把锁

            synchronized (s) {

                if (x % 2 == 0) {

                    s.name = "林青霞";//刚走到这里,就被别人抢到了执行权

                    s.age = 27;

                } else {

                    s.name = "刘意"; //刚走到这里,就被别人抢到了执行权

                    s.age = 30;

                }

                x++;

            }

        }

    }

}

 

 

                3. GetThread(消费者)

/*

* 消费者类:

*/

public class GetThread implements Runnable {

    private Student s;

 

    public GetThread(Student s) {

        this.s = s;

    }

 

    @Override

    public void run() {

        while (true) {

            //加入了同步代码块

            synchronized (s) {

                System.out.println(s.name + "---" + s.age);

            }

        }

    }

}

 

 

                4. StudentDemo(测试类)

/*

* 分析:

*         资源类:Student    

*         设置学生数据:SetThread(生产者)

*         获取学生数据:GetThread(消费者)

*         测试类:StudentDemo

*

* 问题1:按照思路写代码,发现数据每次都是:null---0

* 原因:我们在每个线程中都创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个

* 如何实现呢?

*         在外界把这个数据创建出来,通过构造方法传递给其他的类。

*

* 问题2:为了数据的效果好一些,我加入了循环和判断,给出不同的值,这个时候产生了新的问题

*         A:同一个数据出现多次

*         B:姓名和年龄不匹配

* 原因:

*         A:同一个数据出现多次

*             CPU的一点点时间片的执行权,就足够你执行很多次。

*         B:姓名和年龄不匹配

*             线程运行的随机性

* 线程安全问题:

*         A:是否是多线程环境        

*         B:是否有共享数据        

*         C:是否有多条语句操作共享数据    

* 解决方案:

*         加锁。

*         注意:

*             A:不同种类的线程都要加锁。

*             B:不同种类的线程加的锁必须是同一把。

*/

public class StudentDemo {

    public static void main(String[] args) {

        //创建资源

        Student s = new Student();

        

        //设置和获取的类

        SetThread st = new SetThread(s);

        GetThread gt = new GetThread(s);

 

        //线程类

        Thread t1 = new Thread(st);

        Thread t2 = new Thread(gt);

 

        //启动线程

        t1.start();

        t2.start();

    }

}

 

 

            C:等待唤醒机制改进该程序,让数据能够实现依次的出现

                Object类的方法:

wait():让线程等待,并释放锁

                notify():唤醒线程,并加锁

                notifyAll() (多生产多消费)

                1. Student(资源类)

/*

* 资源类:

*/

public class Student {

    String name;

    int age;

    boolean flag; // 默认情况是没有数据,如果是true,说明有数据

}

 

 

                2. SetThread(生产者类)

/*

* 生产者类:加入了等待-唤醒机制

*/

public class SetThread implements Runnable {

 

    private Student s;

    private int x = 0;

 

    public SetThread(Student s) {

        this.s = s;

    }

 

    @Override

    public void run() {

        while (true) {

            synchronized (s) {

                //判断有没有

                if(s.flag){

                    try {

                        s.wait(); //t1等着,释放锁

                    } catch (InterruptedException e) {

                        e.printStackTrace();

                    }

                }

                

                if (x % 2 == 0) {

                    s.name = "林青霞";

                    s.age = 27;

                } else {

                    s.name = "刘意";

                    s.age = 30;

                }

                x++; //x=1

                

                //修改标记

                s.flag = true;

                //唤醒线程

                s.notify(); //唤醒t2,唤醒并不表示你立马可以执行,必须还得抢CPU的执行权。

            }

            //t1有,或者t2

        }

    }

}

 

                3. GetThread(消费者类)

/*

* 消费者类:加入了等待-唤醒机制

*/

public class GetThread implements Runnable {

    private Student s;

 

    public GetThread(Student s) {

        this.s = s;

    }

 

    @Override

    public void run() {

        while (true) {

            synchronized (s) {

                if(!s.flag){

                    try {

                        s.wait(); //t2就等待了。立即释放锁。将来醒过来的时候,是从这里醒过来的时候

                    } catch (InterruptedException e) {

                        e.printStackTrace();

                    }

                }

                

                System.out.println(s.name + "---" + s.age);

                //林青霞---27

                //刘意---30

                

                //修改标记

                s.flag = false;

                //唤醒线程

                s.notify(); //唤醒t1

            }

        }

    }

}

 

                4. StudentDemo(测试类)

/*

* 分析:

*         资源类:Student    

*         设置学生数据:SetThread(生产者)

*         获取学生数据:GetThread(消费者)

*         测试类:StudentDemo

*

* 问题1:按照思路写代码,发现数据每次都是:null---0

* 原因:我们在每个线程中都创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个

* 如何实现呢?

*         在外界把这个数据创建出来,通过构造方法传递给其他的类。

*

* 问题2:为了数据的效果好一些,我加入了循环和判断,给出不同的值,这个时候产生了新的问题

*         A:同一个数据出现多次

*         B:姓名和年龄不匹配

* 原因:

*         A:同一个数据出现多次

*             CPU的一点点时间片的执行权,就足够你执行很多次。

*         B:姓名和年龄不匹配

*             线程运行的随机性

* 线程安全问题:

*         A:是否是多线程环境        

*         B:是否有共享数据        

*         C:是否有多条语句操作共享数据    

* 解决方案:

*         加锁。

*         注意:

*             A:不同种类的线程都要加锁。

*             B:不同种类的线程加的锁必须是同一把。

*

* 问题3:虽然数据安全了,但是呢,一次一大片不好看,我就想依次的一次一个输出。

* 如何实现呢?

*         通过Java提供的等待唤醒机制解决。

*

* 等待唤醒:

*         Object类中提供了三个方法:

*             wait():等待

*             notify():唤醒单个线程

*             notifyAll():唤醒所有线程

*         为什么这些方法不定义在Thread类中呢?

*             这些方法的调用必须通过锁对象调用,而我们刚才使用的锁对象是任意锁对象。

*             所以,这些方法必须定义在Object类中。

*/

public class StudentDemo {

    public static void main(String[] args) {

        //创建资源

        Student s = new Student();

        

        //设置和获取的类

        SetThread st = new SetThread(s);

        GetThread gt = new GetThread(s);

 

        //线程类

        Thread t1 = new Thread(st);

        Thread t2 = new Thread(gt);

 

        //启动线程

        t1.start();

        t2.start();

    }

}

 

 

 

 

            D:等待唤醒机制的代码优化。把数据及操作都写在了资源类中

            1. Student(资源类,把数据及操作都写在资源类中)

/*

* 资源类:把数据及操作都写在资源类上面

*/

public class Student {

    private String name;

    private int age;

    private boolean flag; // 默认情况是没有数据,如果是true,说明有数据

 

    //使用同步方法解决线程安全问题

    public synchronized void set(String name, int age) {

        // 如果有数据,就等待

        if (this.flag) {

            try {

                this.wait();

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

        }

 

        // 设置数据

        this.name = name;

        this.age = age;

 

        // 修改标记

        this.flag = true;

        this.notify();

    }

 

    //使用同步方法解决线程安全问题

    public synchronized void get() {

        // 如果没有数据,就等待

        if (!this.flag) {

            try {

                this.wait();

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

        }

 

        // 获取数据

        System.out.println(this.name + "---" + this.age);

 

        // 修改标记

        this.flag = false;

        this.notify();

    }

}

 

 

            2. SetThread(生产者类)

/*

* 生产者类:

*/

public class SetThread implements Runnable {

 

    private Student s;

    private int x = 0;

 

    public SetThread(Student s) {

        this.s = s;

    }

 

    @Override

    public void run() {

        while (true) {

            if (x % 2 == 0) {

                s.set("林青霞", 27);

            } else {

                s.set("刘意", 30);

            }

            x++;

        }

    }

}

 

 

            3. GetThread(消费者类)

/*

* 消费者类:

*/

public class GetThread implements Runnable {

    private Student s;

 

    public GetThread(Student s) {

        this.s = s;

    }

 

    @Override

    public void run() {

        while (true) {

            s.get();

        }

    }

}

 

 

            4. StudentDemo(测试类)

 

/*

* 分析:

*         资源类:Student    

*         设置学生数据:SetThread(生产者)

*         获取学生数据:GetThread(消费者)

*         测试类:StudentDemo

*

* 问题1:按照思路写代码,发现数据每次都是:null---0

* 原因:我们在每个线程中都创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个

* 如何实现呢?

*         在外界把这个数据创建出来,通过构造方法传递给其他的类。

*

* 问题2:为了数据的效果好一些,我加入了循环和判断,给出不同的值,这个时候产生了新的问题

*         A:同一个数据出现多次

*         B:姓名和年龄不匹配

* 原因:

*         A:同一个数据出现多次

*             CPU的一点点时间片的执行权,就足够你执行很多次。

*         B:姓名和年龄不匹配

*             线程运行的随机性

* 线程安全问题:

*         A:是否是多线程环境        

*         B:是否有共享数据        

*         C:是否有多条语句操作共享数据    

* 解决方案:

*         加锁。

*         注意:

*             A:不同种类的线程都要加锁。

*             B:不同种类的线程加的锁必须是同一把。

*

* 问题3:虽然数据安全了,但是呢,一次一大片不好看,我就想依次的一次一个输出。

* 如何实现呢?

*         通过Java提供的等待唤醒机制解决。

*

* 等待唤醒:

*         Object类中提供了三个方法:

*             wait():等待

*             notify():唤醒单个线程

*             notifyAll():唤醒所有线程

*         为什么这些方法不定义在Thread类中呢?

*             这些方法的调用必须通过锁对象调用,而我们刚才使用的锁对象是任意锁对象。

*             所以,这些方法必须定义在Object类中。

*

* 最终版代码中:

*         Student的成员变量给私有的了。

*         把设置和获取的操作给封装成了功能,并加了同步。

*         设置或者获取的线程里面只需要调用方法即可。

*/

public class StudentDemo {

    public static void main(String[] args) {

        //创建资源

        Student s = new Student();

        

        //设置和获取的类

        SetThread st = new SetThread(s);

        GetThread gt = new GetThread(s);

 

        //线程类

        Thread t1 = new Thread(st);

        Thread t2 = new Thread(gt);

 

        //启动线程

        t1.start();

        t2.start();

    }

}

 

 

 

 

 

    (4)线程组

        具体案例介绍

            1. MyRunnable(线程类)

/*

* 实现Runnable接口的线程类

*/

public class MyRunnable implements Runnable {

 

    @Override

    public void run() {

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

            //打印当前线程的名称及数字

            System.out.println(Thread.currentThread().getName() + ":" + x);

        }

    }

 

}

 

            2. ThreadGroupDemo(测试类)

/*

* 线程组: 把多个线程组合到一起。

* 它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。

*

* 线程组类:ThreadGroup

*         构造方法:public ThreadGroup(String name):构造一个带名称的线程组

*         普通方法:public final String getName():返回此线程组的名称。

*     注意:默认情况下,所有的线程都属于同一个组,就是main

* Thread类:

*         构造方法:

*         public Thread(ThreadGroup group,

Runnable target,

String name):创建一个属于某个线程组的且带名称的线程

普通方法

public final ThreadGroup getThreadGroup():返回该线程所属的线程组

 

*/

public class ThreadGroupDemo {

    public static void main(String[] args) {

        // method1();

 

        // 我们如何修改线程所在的组呢?

        // 创建一个线程组

        // 创建其他线程的时候,把其他线程的组指定为我们自己新建线程组

        method2();

 

        // t1.start();

        // t2.start();

    }

 

    private static void method2() {

        // ThreadGroup(String name)

        ThreadGroup tg = new ThreadGroup("这是一个新的组");

 

        MyRunnable my = new MyRunnable();

        // Thread(ThreadGroup group, Runnable target, String name)

        Thread t1 = new Thread(tg, my, "林青霞");

        Thread t2 = new Thread(tg, my, "刘意");

        

        System.out.println(t1.getThreadGroup().getName());

        System.out.println(t2.getThreadGroup().getName());

        

        //通过组名称设置后台线程,表示该组的线程都是后台线程

        tg.setDaemon(true);

    }

 

    private static void method1() {

        MyRunnable my = new MyRunnable();

        Thread t1 = new Thread(my, "林青霞");

        Thread t2 = new Thread(my, "刘意");

        // 我不知道他们属于那个线程组,我想知道,怎么办

        // 线程类里面的方法:public final ThreadGroup getThreadGroup()

        ThreadGroup tg1 = t1.getThreadGroup();

        ThreadGroup tg2 = t2.getThreadGroup();

        // 线程组里面的方法:public final String getName()

        String name1 = tg1.getName();

        String name2 = tg2.getName();

        System.out.println(name1);

        System.out.println(name2);

        // 通过结果我们知道了:线程默认情况下属于main线程组

        // 通过下面的测试,你应该能够看到,默任情况下,所有的线程都属于同一个组

        System.out.println(Thread.currentThread().getThreadGroup().getName());

    }

}

 

 

 

    (5)线程池

        案例代码体现:

        1. MyRunnable(线程类)

/*

* 实现Runnable接口的线程类

*/

public class MyRunnable implements Runnable {

 

    @Override

    public void run() {

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

            //打印当前线程的名称及数字

            System.out.println(Thread.currentThread().getName() + ":" + x);

        }

    }

 

}

 

        2. ExecutorsDemo(线程池类)

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

 

/*

* 线程池的好处:线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。

*

* 如何实现线程的代码呢?

*         A:创建一个线程池对象,控制要创建几个线程对象。

*             public static ExecutorService newFixedThreadPool(int nThreads)

*         B:这种线程池的线程可以执行:

*             可以执行Runnable对象或者Callable对象代表的线程

*             做一个类实现Runnable接口。

*         C:调用如下方法即可

*             Future<?> submit(Runnable task)

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

*        D:我就要结束,可以吗?

*            可以。

*/

public class ExecutorsDemo {

    public static void main(String[] args) {

        // 创建一个线程池对象,控制要创建几个线程对象。

        // public static ExecutorService newFixedThreadPool(int nThreads)

        ExecutorService pool = Executors.newFixedThreadPool(2);

 

        // 可以执行Runnable对象或者Callable对象代表的线程

        pool.submit(new MyRunnable());

        pool.submit(new MyRunnable());

 

        //结束线程池

        pool.shutdown();

    }

}

 

 

    (6)多线程实现的第三种方案

        多线程实现第三种方式介绍

        1. MyCallable

import java.util.concurrent.Callable;

 

//Callable:是带泛型的接口。

//这里指定的泛型其实是call()方法的返回值类型。

public class MyCallable implements Callable {

 

    @Override

    public Object call() throws Exception {

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

            System.out.println(Thread.currentThread().getName() + ":" + x);

        }

        return null;

    }

 

}

 

        2. CallableDemo

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

 

/*

* 多线程实现的方式3

*     A:创建一个线程池对象,控制要创建几个线程对象。

*             public static ExecutorService newFixedThreadPool(int nThreads)

*         B:这种线程池的线程可以执行:

*             可以执行Runnable对象或者Callable对象代表的线程

*             做一个类实现Runnable接口。

*         C:调用如下方法即可

*             Future<?> submit(Runnable task)

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

*        D:我就要结束,可以吗?

*            可以。

*/

public class CallableDemo {

    public static void main(String[] args) {

        //创建线程池对象

        ExecutorService pool = Executors.newFixedThreadPool(2);

        

        //可以执行Runnable对象或者Callable对象代表的线程

        pool.submit(new MyCallable());

        pool.submit(new MyCallable());

        

        //结束

        pool.shutdown();

    }

}

 

        多线程案例:求和

        1. MyCallable

import java.util.concurrent.Callable;

 

/*

* 线程求和案例

*/

public class MyCallable implements Callable<Integer> {

 

    private int number;

 

    public MyCallable(int number) {

        this.number = number;

    }

 

    @Override

    public Integer call() throws Exception {

        int sum = 0;

        for (int x = 1; x <= number; x++) {

            sum += x;

        }

        return sum;

    }

 

}

 

        2. CallableDemo

import java.util.concurrent.ExecutionException;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.Future;

 

/*

* 多线程实现的方式3

*     A:创建一个线程池对象,控制要创建几个线程对象。

*             public static ExecutorService newFixedThreadPool(int nThreads)

*         B:这种线程池的线程可以执行:

*             可以执行Runnable对象或者Callable对象代表的线程

*             做一个类实现Runnable接口。

*         C:调用如下方法即可

*             Future<?> submit(Runnable task)

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

*        D:我就要结束,可以吗?

*            可以。

*/

public class CallableDemo {

    public static void main(String[] args) throws InterruptedException, ExecutionException {

        // 创建线程池对象

        ExecutorService pool = Executors.newFixedThreadPool(2);

 

        // 可以执行Runnable对象或者Callable对象代表的线程

        Future<Integer> f1 = pool.submit(new MyCallable(100));

        Future<Integer> f2 = pool.submit(new MyCallable(200));

 

        // V get()

        Integer i1 = f1.get();

        Integer i2 = f2.get();

 

        System.out.println(i1);

        System.out.println(i2);

 

        // 结束

        pool.shutdown();

    }

}

 

 

    (7)匿名内部类方法实现多线程

    

/*

* 匿名内部类的格式:

*         new 类名或者接口名() {

*             重写方法;

*         };

*         本质:是该类或者接口的子类对象。

*/

public class ThreadDemo {

    public static void main(String[] args) {

        // 继承Thread类来实现多线程

        new Thread() {

            public void run() {

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

                    System.out.println(Thread.currentThread().getName() + ":"

                            + x);

                }

            }

        }.start();

 

        // 实现Runnable接口来实现多线程

        new Thread(new Runnable() {

            @Override

            public void run() {

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

                    System.out.println(Thread.currentThread().getName() + ":"

                            + x);

                }

            }

        }) {

        }.start();

 

        // 更有难度的,以下的这个代码是不会报错的,可以正常运行,需要能看懂

        new Thread(new Runnable() {

            @Override

            public void run() {

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

                    System.out.println("hello" + ":" + x);

                }

            }

        }) {

            public void run() {

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

                    System.out.println("world" + ":" + x);

                }

            }

        }.start();

    }

}

 

    (8)定时器的介绍

        1. TimerDemo类(任务完成之后,可以终止定时器)

import java.util.Timer;

import java.util.TimerTask;

 

/*

* 定时器:可以让我们在指定的时间做某件事情,还可以重复的做某件事情。

* 依赖TimerTimerTask这两个类:

* Timer:定时

*         public Timer()

*         public void schedule(TimerTask task,long delay)

*                             :安排在指定延迟后执行指定的任务。

*         public void schedule(TimerTask task, Date time)

*                             :安排在指定的时间执行指定的任务

 

*         public void schedule(TimerTask task,long delay,long period)

*                          :安排指定的任务在指定的时间开始进行重复的固定延迟执行。

*         public void cancel():终止此计时器,丢弃所有当前已安排的任务。

*                              这不会干扰当前正在执行的任务(如果存在)。

* TimerTask:任务类

*             子类继承该方法,并重写里面的run()方法

*/

public class TimerDemo {

    public static void main(String[] args) {

        // 创建定时器对象

        Timer t = new Timer();

        // 3秒后执行爆炸任务

        // t.schedule(new MyTask(), 3000);

        //结束任务

        t.schedule(new MyTask(t), 3000);

    }

}

 

// 做一个任务

class MyTask extends TimerTask {

 

    private Timer t;

    

    public MyTask(){}

    

    public MyTask(Timer t){

        this.t = t;

    }

    //要做的任务

    @Override

    public void run() {

        System.out.println("beng,爆炸了");

        //任务执行完成之后,终止此定时器

        t.cancel();

    }

}

 

        2. TimerDemo2类(任务完成之后,不终止定时器)

import java.util.Timer;

import java.util.TimerTask;

 

/*

* 定时器:可以让我们在指定的时间做某件事情,还可以重复的做某件事情。

* 依赖TimerTimerTask这两个类:

* Timer:定时

*         public Timer()

*         public void schedule(TimerTask task,long delay)

*         public void schedule(TimerTask task,long delay,long period)

*         public void cancel()

* TimerTask:任务

*/

public class TimerDemo2 {

    public static void main(String[] args) {

        // 创建定时器对象

        Timer t = new Timer();

        // 3秒后执行爆炸任务第一次,如果不成功,每隔2秒再继续炸

        t.schedule(new MyTask2(), 3000, 2000);

        //该程序不会自动停止

    }

}

 

// 做一个任务

class MyTask2 extends TimerTask {

    @Override

    public void run() {

        System.out.println("beng,爆炸了");

    }

}

 

        3. 案例:定时删除某个目录下的所有文件

import java.io.File;

import java.text.ParseException;

import java.text.SimpleDateFormat;

import java.util.Date;

import java.util.Timer;

import java.util.TimerTask;

 

/*

* 需求:在指定的时间删除我们的指定目录(你可以指定c盘,但是我不建议,我使用项目路径下的demo)

*/

 

class DeleteFolder extends TimerTask {

 

    //定时任务

    @Override

    public void run() {

        File srcFolder = new File("demo");

        deleteFolder(srcFolder);

    }

 

    // 递归删除目录

    public void deleteFolder(File srcFolder) {

        //获取源目录下的所有File对象的数组

        File[] fileArray = srcFolder.listFiles();

        

        //判断该File数组是否为null

        if (fileArray != null) {

            //不为null,循环遍历 该数组

            for (File file : fileArray) {

                

                //判断该file对象是否为目录

                if (file.isDirectory()) {

                    //如果为目录,则进行递归

                    deleteFolder(file);

                } else {

                    //不为目录,则打印被删除文件的名称并同时删除文件

                    System.out.println(file.getName() + ":" + file.delete());

                }

            }

            //如果File数组为null,则打印被删除文件的名称并同时删除文件

            System.out.println(srcFolder.getName() + ":" + srcFolder.delete());

        }

    }

}

 

//测试类

public class TimerTest {

    public static void main(String[] args) throws ParseException {

        //创建定时器对象

        Timer t = new Timer();

 

        String s = "2014-11-27 15:45:00";

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        Date d = sdf.parse(s);

        //在指定的时间,删除某个目录下的所有文件

        t.schedule(new DeleteFolder(), d);

    }

}

 

 

    (9)多线程的面试题

1:多线程有几种实现方案,分别是哪几种?

    两种。

    

    继承Thread

    实现Runnable接口

    

    扩展一种:实现Callable接口。这个得和线程池结合。

 

2:同步有几种方式,分别是什么?

    两种。

    

    同步代码块

    同步方法

 

3:启动一个线程是run()还是start()?它们的区别?

    start();

    

    run():封装了被线程执行的代码,直接调用仅仅是普通方法的调用

    start():启动线程,并由JVM自动调用run()方法

 

4:sleep()和wait()方法的区别

    sleep():必须指时间;不释放锁。

    wait():可以不指定时间,也可以指定时间;释放锁。

 

5:为什么wait(),notify(),notifyAll()等方法都定义在Object类中

    因为这些方法的调用是依赖于锁对象的,而同步代码块的锁对象是任意锁。

    而Object代表任意的对象,所以,定义在这里面。

 

6:线程的生命周期图

    新建 -- 就绪 -- 运行 -- 死亡

    新建 -- 就绪 -- 运行 -- 阻塞 -- 就绪 -- 运行 -- 死亡

    建议:画图解释。

 

 

2:设计模式(理解)
    (1)面试对象的常见设计原则

        单一

        开闭

        里氏

        依赖注入

        接口

        迪米特

    (2)设计模式概述和分类

        A:经验的总结

        B:三类

            创建型

            结构型

            行为型

    (3)改进的设计模式

        A:简单工厂模式

            1. Animal类(顶层抽象类)

/*

* 动物类:抽象类

*/

public abstract class Animal {

    //吃的方法

    public abstract void eat();

}

 

            2. Cat

/*

* 猫类:继承动物类

*/

public class Cat extends Animal {

    @Override

    public void eat() {

        System.out.println("猫吃鱼");

    }

 

}

 

            3. Dog

/*

* 狗类:继承动物类

*/

public class Dog extends Animal {

 

    @Override

    public void eat() {

        System.out.println("狗吃肉");

    }

}

 

            4. AnimalFactory类(动物工厂类)

            

/*

* 动物工厂类:专门造动物的

*/

public class AnimalFactory {

    //构造方法私有,不允许创建对象

    private AnimalFactory() {

    }

    //根据传递过来的类型,创建指定的对象,可通过类名直接调用

    public static Animal createAnimal(String type) {

        if ("dog".equals(type)) {

            return new Dog();

        } else if ("cat".equals(type)) {

            return new Cat();

        } else {

            return null;

        }

    }

}

 

            5. AnimalDemo(测试类)

/*

* 测试类

*/

public class AnimalDemo {

    public static void main(String[] args) {

        // 具体类调用

        Dog d = new Dog();

        d.eat();

        Cat c = new Cat();

        c.eat();

        System.out.println("------------");

 

        // 工厂改进后

        Animal a = AnimalFactory.createAnimal("dog");

        a.eat();

        a = AnimalFactory.createAnimal("cat");

        a.eat();

 

        // NullPointerException,在使用对象之前,应该先对对象是否为null进行判断

        a = AnimalFactory.createAnimal("pig");

        if (a != null) {

            a.eat();

        } else {

            System.out.println("对不起,暂时不提供这种动物");

        }

    }

}

 

            

        B:工厂方法模式

            1. Animal(动物类)

/*

* 动物类:抽象 类

*/

public abstract class Animal {

    public abstract void eat();

}

 

            2. Cat类(猫类)

/*

* 猫类:继承自动物类

*/

public class Cat extends Animal {

 

    @Override

    public void eat() {

        System.out.println("猫吃鱼");

    }

 

}

 

            3. Dog(狗类)

/*

* 狗类:继承自动物类

*/

public class Dog extends Animal {

 

    @Override

    public void eat() {

        System.out.println("狗吃肉");

    }

 

}

 

            4. Factory(接口)

/*

* Factory:工厂接口

*/

public interface Factory {

    //定义了创建动物的方法

    public abstract Animal createAnimal();

}

 

            5. CatFactory(猫工厂)

/*

* CatFactory:猫工厂,实现工厂接口,专门用来生成猫

*/

public class CatFactory implements Factory {

 

    @Override

    public Animal createAnimal() {

        return new Cat();

    }

 

}

 

            6. DogFactory(狗工厂)

/*

* 狗工厂:实现了工厂接口,专门用来造狗

*/

public class DogFactory implements Factory {

 

    @Override

    public Animal createAnimal() {

        return new Dog();

    }

 

}

 

            7. AnimalDemo测试类

/*

* 测试类

*/

public class AnimalDemo {

    public static void main(String[] args) {

        // 需求:我要买只狗

        Factory f = new DogFactory();

        Animal a = f.createAnimal();

        a.eat();

        System.out.println("-------");

        

        //需求:我要买只猫

        f = new CatFactory();

        //使用特定的工厂专门造狗,造猫

        a = f.createAnimal();

        a.eat();

    }

}

 

        C:单例模式(掌握)

            a:饿汉式:

            说明:饿汉式:提前造好一个对象(开发中常用,因为不容易出现问题)

            1.

/*

* 单例模式:饿汉式:提前造好一个对象

*/

public class Student {

    // 构造私有

    private Student() {

    }

 

    // 自己造一个

    // 静态方法只能访问静态成员变量,加静态

    // 为了不让外界直接访问修改这个值,加private

    private static Student s = new Student();

 

    // 提供公共的访问方式

    // 为了保证外界能够直接使用该方法,加静态

    public static Student getStudent() {

        return s;

    }

}

 

 

            2. StudentDemo测试类

/*

* 单例模式:保证类在内存中只有一个对象。

*

* 如何保证类在内存中只有一个对象呢?

*         A:把构造方法私有

*         B:在成员位置自己创建一个对象

*         C:通过一个公共的方法提供访问

*/

public class StudentDemo {

    public static void main(String[] args) {

        //通过单例模式得到对象

        Student s1 = Student.getStudent();

        Student s2 = Student.getStudent();

        System.out.println(s1 == s2);

 

        System.out.println(s1); // nullcn.itcast_03.Student@175078b

        System.out.println(s2);// nullcn.itcast_03.Student@175078b

    }

}

 

 

            b:懒汉式:

            说明:需要用的时候,才去创建对象。(面试常面)

            1. Teacher

/*

* 单例模式:

*         饿汉式:类一加载就创建对象

*         懒汉式:用的时候,才去创建对象

*

* 面试题:单例模式的思想是什么?请写一个代码体现。

*

*         开发:饿汉式(是不会出问题的单例模式)

*         面试:懒汉式(可能会出问题的单例模式)

*             A:懒加载(延迟加载)    

*             B:线程安全问题

*                 a:是否多线程环境    

*                 b:是否有共享数据    

*                 c:是否有多条语句操作共享数据     

*/

public class Teacher {

    //构造方法私有化

    private Teacher() {

    }

 

    //定义一个对象

    private static Teacher t = null;

 

    //加入同步关键字解决线程安全问题

    public synchronized static Teacher getTeacher() {

        // t1,t2,t3

        //如果对象为null,就创建,否则不创建

        if (t == null) {

            //t1,t2,t3

            t = new Teacher();

        }

        return t;

    }

}

 

 

            2. TeacherDemo

/*

* 测试类

*/

public class TeacherDemo {

    public static void main(String[] args) {

        Teacher t1 = Teacher.getTeacher();

        Teacher t2 = Teacher.getTeacher();

        System.out.println(t1 == t2);

        System.out.println(t1); // cn.itcast_03.Teacher@175078b

        System.out.println(t2);// cn.itcast_03.Teacher@175078b

    }

}

 

 

 

    (4)Runtime

        JDK提供的一个单例模式应用的类。

        还可以调用dos命令。

        

import java.io.IOException;

 

/*

* Runtime:每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。

* exec(String command)

*/

public class RuntimeDemo {

    public static void main(String[] args) throws IOException {

        Runtime r = Runtime.getRuntime();

//        r.exec("winmine");

        //打开记事本

        // r.exec("notepad");

        //打开计算器

        // r.exec("calc");

        //关机(时间是以秒计算的)

//        r.exec("shutdown -s -t 10000");

        //取消shutdown的命令

        r.exec("shutdown -a");

    }

}

 

/*Runtime类使用了单例模式

* class Runtime {

*         private Runtime() {}

*         private static Runtime currentRuntime = new Runtime();

*         public static Runtime getRuntime() {

*     return currentRuntime;

*     }

* }

*/

 

 

posted @ 2016-09-15 12:46  runningto  阅读(215)  评论(0编辑  收藏  举报