多线程(线程通信)

1:多线程(理解)
(1)JDK5以后的针对线程的锁定操作和释放操作
Lock锁

/*
 * 虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,
 * 为了更清晰的表达如何加锁和释放锁,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();
    }
}
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)死锁问题的描述和代码体现
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");
                }
            }
        }
    }
}
/*
 * 同步的弊端:
 *      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();
    }
}
(3)生产者和消费者多线程体现(线程间通信问题)
    以学生作为资源来实现的

    资源类:Student
    设置数据类:SetThread(生产者)
    获取数据类:GetThread(消费者)
    测试类:StudentDemo

    代码:
        A:最基本的版本,只有一个数据。
        B:改进版本,给出了不同的数据,并加入了同步机制
        C:等待唤醒机制改进该程序,让数据能够实现依次的出现
            wait()
            notify()
            notifyAll() (多生产多消费)
        D:等待唤醒机制的代码优化。把数据及操作都写在了资源类中
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++;
        }
    }
}

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

/*
 * 分析:
 *      资源类: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)线程组
(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)多线程实现的第三种方案
/*
 * 多线程实现的方式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:是带泛型的接口。
//这里指定的泛型其实是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;
    }

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

/*
 * 线程求和案例
 */
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;
    }

}
/*
 * 匿名内部类的格式:
 *      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 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,爆炸了");
    }
}
/*
 * 需求:在指定的时间删除我们的指定目录(你可以指定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);
    }
}

这里写图片描述

这里写图片描述
(7)多线程的面试题
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:简单工厂模式
B:工厂方法模式
C:单例模式(掌握)
a:饿汉式
b:懒汉式

public class Student {
    // 构造私有
    private Student() {
    }

    // 自己造一个
    // 静态方法只能访问静态成员变量,加静态
    // 为了不让外界直接访问修改这个值,加private
    private static Student s = new Student();

    // 提供公共的访问方式
    // 为了保证外界能够直接使用该方法,加静态
    public static Student getStudent() {
        return s;
    }
}
/*
 * 单例模式:
 *      饿汉式:类一加载就创建对象
 *      懒汉式:用的时候,才去创建对象
 * 
 * 面试题:单例模式的思想是什么?请写一个代码体现。
 * 
 *      开发:饿汉式(是不会出问题的单例模式)
 *      面试:懒汉式(可能会出问题的单例模式)
 *          A:懒加载(延迟加载) 
 *          B:线程安全问题
 *              a:是否多线程环境   是
 *              b:是否有共享数据   是
 *              c:是否有多条语句操作共享数据     是
 */
public class Teacher {
    private Teacher() {
    }

    private static Teacher t = null;

    public synchronized static Teacher getTeacher() {
        // t1,t2,t3
        if (t == null) {
            //t1,t2,t3
            t = new Teacher();
        }
        return t;
    }
}
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命令。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值