——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方法)