多线程-扩展篇(Lock锁,线程通信,线程池,定时器..)

1:多线程

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

          Lock:

               void lock(): 获取锁。

               void unlock():释放锁。

          ReentrantLock是Lock的实现类. 

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();

            }

        }

    }



}

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)死锁问题的描述和代码体现

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

public class MyLock {

    // 创建两把锁对象

    public static final Object objA = new Object();

    public static final Object objB = new Object();

}

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");

                }

            }

        }

    }

}
/

 * 举例:

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

 *         正常情况:

 *             中国人:筷子两支

 *             美国人:刀和叉

 *         现在:

 *             中国人:筷子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();

    }

}

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

        代码:

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

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

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

                wait()

                notify()

                notifyAll() (多生产多消费)

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

           最终版:

          

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();

    }

}



public class GetThread implements Runnable {

    private Student s;



    public GetThread(Student s) {

        this.s = s;

    }



    @Override

    public void run() {

        while (true) {

            s.get();

        }

    }

}



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++;

        }

    }

}

/*

 * 分析:

 *         资源类: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)线程组

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

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


/*

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

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

 */

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)线程池

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


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

 *         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)多线程实现的第三种方案:线程池


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();

    }

}



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

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

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;

    }



}

(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)定时器

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

     依赖Timer和TimerTask这两个类:

     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 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();

    }



}



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,爆炸了");

    }

}




/*

 * 需求:在指定的时间删除我们的指定目录(使用项目路径下的demo)

 */



class DeleteFolder extends TimerTask {



    @Override

    public void run() {

        File srcFolder = new File("demo");

        deleteFolder(srcFolder);

    }



    // 递归删除目录

    public void deleteFolder(File srcFolder) {

        File[] fileArray = srcFolder.listFiles();

        if (fileArray != null) {

            for (File file : fileArray) {

                if (file.isDirectory()) {

                    deleteFolder(file);

                } else {

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

                }

            }

            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);

    }

}
发布了114 篇原创文章 · 获赞 52 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/Smile_Sunny521/article/details/89704719