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

day24--多线程案例源码

时间:2015-07-11 10:32:03      阅读:133      评论:0      收藏:0      [点我收藏+]

标签:

JDK5新特性:解决线程同步问题,因为同步代码块,同步方法,与同步静态方法的同步锁不明显,JDK5特意新增加了一个新的子类:Lock
//具体实现类
package cn.itcast_01;

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

}
//测试类
package cn.itcast_01;
/*
 * 虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,
 * 为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock。
 *
 * Lock:
 *         void lock(): 获取锁。
 *         void unlock():释放锁。  
 * ReentrantLock是Lock的实现类.
 */
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();
    }
}

---------------------------------------------------------------------------------------------------------------------------
死锁问题的出现:
//创建不同的两把锁
package cn.itcast_02;

public class MyLock {
    // 创建两把锁对象
    public static final Object objA = new Object();
    public static final Object objB = new Object();
}

//创建线程实现类
package cn.itcast_02;

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


//测试类
package cn.itcast_02;

/*
 * 同步的弊端:
 *         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();
    }
}


---------------------------------------------------------------------------------------------------------------------------
线程组
//测试线程类
package cn.itcast_06;

public class MyRunnable implements Runnable {

    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            System.out.println(Thread.currentThread().getName() + ":" + x);
        }
    }
}

//测试具体类
package cn.itcast_06;
/*
 * 线程组: 把多个线程组合到一起。
 * 它可以对一批线程进行分类管理,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());
    }
}

---------------------------------------------------------------------------------------------------------------------------
消费者生产者问题最终版
//消费者
package cn.itcast_07;

public class GetThread implements Runnable {
    private Student s;

    public GetThread(Student s) {
        this.s = s;
    }

    @Override
    public void run() {
        while (true) {
            s.get();
        }
    }
}

//生产者
package cn.itcast_07;

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

//具体调用的共享类(以共享数据的类作为锁,共同维护同一个对象,注意是对象)
package cn.itcast_07;

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


//测试类
package cn.itcast_07;

/*
 * 分析:
 *         资源类: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();
    }
}
---------------------------------------------------------------------------------------------------------------------------
消费者生产者问题上一个版本,把方法分到各自的线程的run里面去
//消费者
package cn.itcast_05;

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
            }
        }
    }
}

//生产者
package cn.itcast_05;

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有
        }
    }
}

//具体调用的共享类(以共享数据的类作为锁,共同维护同一个对象,注意是对象)
package cn.itcast_05;

public class Student {
    String name;
    int age;
    boolean flag; // 默认情况是没有数据,如果是true,说明有数据
}

//测试类
package cn.itcast_05;

/*
 * 分析:
 *         资源类: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();
    }
}

---------------------------------------------------------------------------------------------------------------------------
线程池
package cn.itcast_08;

public class MyRunnable implements Runnable {

    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            System.out.println(Thread.currentThread().getName() + ":" + x);
        }
    }

}

测试类:
package cn.itcast_08;

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

---------------------------------------------------------------------------------------------------------------------------
创建线程的第三种方法,依赖于Executors线程池的线程实现方法:
package cn.itcast_09;

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

}

//测试类
package cn.itcast_09;

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

---------------------------------------------------------------------------------------------------------------------------
Callable接口带返回值求和
package cn.itcast_10;

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

}

/*
 * 多线程实现的方式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();
    }
}

---------------------------------------------------------------------------------------------------------------------------
匿名内部类实现多线程:
package cn.itcast_11;

/*
 * 匿名内部类的格式:
 *         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();
    }
}

---------------------------------------------------------------------------------------------------------------------------
定时器:
/*
 * 定时器:可以让我们在指定的时间做某件事情,还可以重复的做某件事情。
 * 依赖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();
    }

}

---------------------------------------------------------------------------------------------------------------------------
定时器2:
package cn.itcast_12;

import java.util.Timer;
import java.util.TimerTask;

/*
 * 定时器:可以让我们在指定的时间做某件事情,还可以重复的做某件事情。
 * 依赖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 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,爆炸了");
    }
}

---------------------------------------------------------------------------------------------------------------------------------
定时器练习,定时删除文件
package cn.itcast_12;

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[] 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);
    }
}

day24--多线程案例源码

标签:

原文地址:http://www.cnblogs.com/canceler/p/4638093.html

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