【黑马程序员】多线程,设计模式——Java复习笔记

——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-

多线程

(1)多线程:一个应用程序有多条执行路径
    进程:正在执行的应用程序
    线程:进程的执行单元,执行路径
    单线程:一个应用程序只有一条执行路径
    多线程:一个应用程序有多条执行路径

    多进程的意义?
        提高CPU的使用率

    多线程的意义?
        提高应用程序的使用率  线程多的程序容易获得更多资源

    并发:物理上同时发生,指在某一个时间点同时运行多个程序
    并行:逻辑上同时发生,指在某一个时间内同时运行多个程序

(2)Java程序的运行原理及JVM的启动是多线程的吗?
    A:Java命令去启动JVM,JVM会启动一个进程,该进程会启动一个主线程。
    B:JVM的启动是多线程的,因为它最低有两个线程启动了,主线程和垃圾回收线程。

(3)多线程的实现方案
    A:继承Thread类,重写run方法,创建对象,启动线程start

    B(常用):实现Runnable接口,重写run方法,创建对象,创建Thread类的对象,并把上一步的对象作为构造参数传递

    C:实现Callable接口,依赖线程池

    run方法包含那些被线程执行的代码,不在run方法里的不执行,run方法并不启动线程,需要start方法启动,start方法会自动调用run.若想创建多个线程,就创建多个对象分别调用start方法  

    可通过getName setName来获取设置线程名称,也可通过构造方法设置线程名称  
    还可获取当前正在执行的线程名称                                          Thread.currentThread().getName()

    B方式的优点:
        避免Java单继承带来的局限性
        适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码/数据有效分离,较好的体现了面向对象思想

(4)线程的调度和优先级问题
    A:线程的调度
        a:分时调度 所有线程轮流使用cpu  平均分配时间
        b:抢占式调度 (Java采用) 高优先级有更高几率先使用cpu,优先级相同的就随机选择一个 

    B:获取和设置线程优先级 getPriority()
        a:默认是5
        b:范围是1-10 越大越高

(5)线程的控制(常见方法)
    A:休眠线程 public static void sleep(long) 线程暂停xx毫秒,不释放锁

    B:加入线程 public final void join() 等待该线程终止

    D:后台线程 public final void setDaemon(boolean) 将该线程标记为守护线程或用户线程,当正在运行的线程都是守护线程时,JVM退出.该方法必须在启动线程前调用

    E:终止线程 public final void stop() 已过时 
            public void interrupt()这个中断会将线程状态终止,并抛出InterruptedException

(6)线程的生命周期
    新建,就绪,运行,阻塞 ,死亡

(7)电影院卖票程序的实现
    A:继承Thread类
    B:实现Runnable接口
    为了更符合真实的场景,加入了休眠100毫秒。
/**
 * Created by mo on 15/11/18.
 * 电影院100张票,3个窗口同时卖,请设计程序实现
 */
public class SaleTicker2 {
    public static void main(String[] args) {
        SaleTickets1 s = new SaleTickets1();

        Thread t1 = new Thread(s, "no.1");
        Thread t2 = new Thread(s, "no.2");
        Thread t3 = new Thread(s, "no.3");

        t1.start();
        t2.start();
        t3.start();
    }
}

class SaleTickets1 implements Runnable {
    private static int num = 1000;
    private Object s = new Object();

    @Override
    public void run() {


        while (true) {
            synchronized (s) {
                if (num > 0) {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        System.out.println("停止售票");
                    }
                    num--;
                    System.out.println(Thread.currentThread().getName() + "窗口卖出一张,剩余" + num + "张" + "by同步方法");
                } else {
                    break;
                }
            }
        }


    }

}
(8)多线程安全问题的原因(也是我们以后判断一个程序是否有线程安全问题的依据)
    A:是否有多线程环境
    B:是否有共享数据
    C:是否有多条语句操作共享数据

(9)同步解决线程安全问题
    A:同步代码块
        某线程执行时,其他线程不能执行该代码块,但可以执行非同步的代码块.前提是这些线程用同一个锁对象.

        synchronized(锁对象) {
            需要被同步的代码;
        }           
        这里的锁对象可以是任意对象。

    B:同步方法
        把同步加在方法上。方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行         
        这里的锁对象是this

    C:静态同步方法
        把同步加在方法上。           
        这里的锁对象是当前类的字节码文件对象(反射再讲字节码文件对象)即SellTickets.class

(10)回顾以前的线程安全的类
    A:StringBuffer
    B:Vector
    C:Hashtable

    D:如何把一个线程不安全的集合类变成一个线程安全的集合类
        用Collections工具类的方法即可。不用Vector 
List<String> list = Collections.synchronizedList(new ArrayList<String>);
(11)JDK5以后的针对线程的锁定操作和释放操作
    Lock锁 实现类ReentrantLock
        lock()      unlock() 两个方法实现上锁和解锁

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

(12)生产者和消费者多线程体现(线程间通信问题)
    为解决此问题,Java引入了等待唤醒机制

    Object类中提供了三个方法,wait notify notifyAll   
    为什么这三个方法定义在Obj类中呢?因为这些方法的调用必须通过锁对象,而锁对象是任意对象但都是Obj的子类,so..

    wait进入等待状态,可以指定时间,也可不指定.立即释放锁.
    notify唤醒等待的线程,如果有多个等待线程,则随机选择一个唤醒,若被唤醒线程抢到了cpu就从wait处开始执行

(13)线程组
    ThreadGroup表示线程组,可以对一批线程进行分类管理
    默认情况下线程都属于main线程组

    修改线程组的方法:创建一个线程组,创建其他线程的时候,把其他线程的组制定为我们自己新建线程组

(14)线程池
    启动一个新线程比较耗费资源,因为涉及到与系统的交互,而使用xian'cheng线程池可以很好的提高性能,尤其当程序中要创建大量生存期很短的线程时,更要考虑使用线程池.
    线程池里的线程用完后不会死亡,而是回收成为空闲状态,等待再次利用

static ExecutorService  newFixedThreadPool(int nThreads)创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。

static ExecutorService  newCachedThreadPool() 创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。

static ExecutorService  newSingleThreadExecutor() 创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。

这些方法的返回值是ExecutorService对象,该对象表示一个线程池

Timer

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

/**
 * Created by mo on 15/11/19.
 *
 * 计时器Timer
 * Java提供的工具,线程用其安排以后在后台线程中执行的任务,可安排任务执行一次或定期重复执行
 *
 * 构造方法摘要
 * Timer()      创建一个新计时器。
 * Timer(boolean isDaemon)      创建一个新计时器,可以指定其相关的线程作为守护程序运行。
 * Timer(String name)       创建一个新计时器,其相关的线程具有指定的名称。
 * Timer(String name, boolean isDaemon)     创建一个新计时器,其相关的线程具有指定的名称,并且可以指定作为守护程序运行。
 *
 * 方法摘要
 * void cancel()        终止此计时器,丢弃所有当前已安排的任务。
 * int  purge()     从此计时器的任务队列中移除所有已取消的任务。
 * void schedule(TimerTask task, Date time)     安排在指定的时间执行指定的任务。
 * void schedule(TimerTask task, Date firstTime, long period)       安排指定的任务在指定的时间开始进行重复的固定延迟执行。
 * void schedule(TimerTask task, long delay)        安排在指定延迟后执行指定的任务。
 * void schedule(TimerTask task, long delay, long period)       安排指定的任务从指定的延迟后开始进行重复的固定延迟执行。
 * void scheduleAtFixedRate(TimerTask task, Date firstTime, long period)        安排指定的任务在指定的时间开始进行重复的固定速率执行。
 * void scheduleAtFixedRate(TimerTask task, long delay, long period)        安排指定的任务在指定的延迟后开始进行重复的固定速率执行。
 *
 * TimerTask
 * 抽象类 任务类
 * run()方法  此任务要执行的操作
 */
public class TimerDemo {
    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new myTask(timer),3000);//传入执行任务,3秒后执行
    }
}

class myTask extends TimerTask{
    @Override
    public void run() {
        System.out.println("hello");
        timer.cancel();//hello完就停
    }
    private Timer timer;

    public myTask() {
    }

    public myTask(Timer timer) {
        this.timer = timer;
    }
}
练习:在指定时间删除指定目录
import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

/**
 * Created by mo on 15/11/19.
 *
 * 需求:在指定时间删除指定目录
 *
 * 分析:
 * schedule(TimerTask task, Date date)安排任务
 * Task任务调用删除目录方法
 * 编写删除目录方法
 */
public class TimerTest {
    public static void main(String[] args) {

        System.out.println("开始定时器任务,当前时间"+ new Date());
        deleteFolderInTime("/Users/mo/FileTest","2015-11-19 23:56:50");

    }

    /**
     * @version 1.0
     * @author mo
     *
     * 这是一个可以爱指定时间删除指定文件/文件夹的方法
     *
     * @param folderpath 要删除的文件/文件夹路径
     * @param time 指定删除任务开始的时间,字符串类型 格式为 yyyy-MM-dd HH:mm:ss
     */
    private static void deleteFolderInTime(String folderpath, String time) {
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            date = sdf.parse(time);
        } catch (ParseException e) {
            System.out.println("解析时间时出现意外错误");
        }
        Timer timer  = new Timer();
        timer.schedule(new myTask2(timer,folderpath),date);

    }
}

class myTask2 extends TimerTask{
    private Timer timer;
    private File file;

    @Override
    public void run() {
        System.out.println("开始删除,当前时间"+new Date());
        deleteFolder(file);
        System.out.println("全部删除完毕,当前时间"+ new Date());
        timer.cancel();
    }

    private void deleteFolder(File file) {
        File[] arr = file.listFiles();
        try{
            for (File files:arr){
                if (files.isDirectory()){
                    deleteFolder(files);
                }else {
                    files.delete();
                }
            }

            System.out.println(file+"-----删除完毕");
        }catch (NullPointerException e){
            System.out.println("目录不存在");
        }
    }

    public myTask2() {
    }

    public myTask2(Timer timer, String folderpath) {
        this.timer = timer;
        file = new File(folderpath);
    }
}

设计模式

(1)面试对象的常见设计原则
    单一职责:每个类应该只有一个职责,对外只提供一种功能,而引起类变化的原因应该只有一个.所有的设计模式都遵循此原则

    开闭:一个对象对扩展开放,对修改关闭.即对类的改动是通过增加代码而不是修改现有代码.如何做到?写出来的代码要借助抽象和多态,把可能变化的内容抽象出来,从而使抽象的部分是相对稳定的,而具体的实现则是可以改变和扩展的

    里氏替换:在任何父类出现的地方都可以用他的子类替代.即要保证同一继承体系中的对象应该有共同的行为特征

    依赖注入:要依赖于抽象,不要依赖于具体实现.写代码时进行针对抽象类或接口,而不是针对具体实现编程

    接口分离:不应该强迫程序依赖它们不需要使用的方法.即一个接口不用提供太多行为,只需提供一种对外功能就行了,不应该吧所有操作都封装到一个接口中

    迪米特:一个对象对其他对象尽可能少的了解.模块之间应该只通过接口编程,而不用理会模块内部的工作原理,使各模块耦合度降到最低

(2)设计模式概述和分类
    A:是经验的总结
    B:三类
        创建型
        结构型
        行为型

(3)改进的设计模式
    A:简单工厂模式

    B:工厂方法模式:抽象工厂类负责创建对象的接口,具体对象的创建由抽象工厂的具体类实现.
        例: abstract Animal,interface Factory,Dog extends Animal,Cat extends Animal,DogFactory implements Factory,CatFactory implements Factory

    C:单例模式:保证类在内存中只有一个对象        
        a:饿汉式(类一加载就创建对象)
            把无参构造方法私有化,在成员位置创建一个私有静态对象,再写个静态公共方法提供访问

        b:懒汉式(用的时候再创建对象)
            创建静态成员对象时不创建而是=null; 然后在公共访问方法里判断对象是否为空,为空就创建对象.其他都同饿汉式

        开发中用饿汉式,因为不会出问题.懒汉式会出现线程安全问题,即可能多个线程都通过公共方法重复的创建对象.所以懒汉式的公共方法要用synchronized修饰

(4)Runtime
    JDK提供的一个应用了单例模式的类。
    还可以调用dos命令。(exec方法)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值