JavaSE学习总结第24天_多线程2
24.01 JDK5之后的Lock锁的概述和使用
虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
public interface Lock:Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition 对象。
例:
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();
}
}
}
}
24.02 死锁问题概述和使用
同步弊端:效率低,如果出现了同步嵌套,就容易产生死锁问题
死锁问题:是指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待现象
同步代码块的嵌套案例
public class DieLock extends Thread
{
private boolean flag; // 创建两把锁对象
public static final Object objA = new Object();
public static final Object objB = new Object(); public DieLock(boolean flag)
{
this.flag = flag;
} @Override
public void run()
{
if (flag)
{
synchronized (objA)
{
System.out.println("if objA");
synchronized (objB)
{
System.out.println("if objB");
}
}
}
else
{
synchronized (objB)
{
System.out.println("else objB");
synchronized (objA)
{
System.out.println("else objA");
}
}
}
}
}
理想状态下运行结果:
if objA
if objB
else objB
else objA
死锁运行结果:
if objA
else objB
24.03 生产者消费者问题代码1
线程间通讯:多个线程在处理同一资源,但是任务却不同
例:
public class Practice
{
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); //启动线程
t2.start();
t1.start();
}
}
问题:t1线程是用来设置学生的信息,而t2线程是用来获取学生的信息,理想状态下t1先运行t2后运行没有问题,但如果t2先运行则获取的结果是null---0
24.04 生产者消费者题代码2并解决线程安全问题
问题:加入了循环和判断,给出不同的值,这个时候产生了新的问题
1:同一个数据出现多次
2:姓名和年龄不匹配
原因:
1:同一个数据出现多次,因为CPU的一点点时间片的执行权,就足够执行很多次
2:姓名和年龄不匹配,线程运行的随机性
解决方案:加锁
1:不同种类的线程都要加锁
2:不同种类的线程加的锁必须是同一把
例:
public class SetThread implements Runnable
{
private Student s;
private int i = 0;
public SetThread(Student s)
{
this.s = s;
} @Override
public void run()
{
while(true)
{
synchronized (s)
{
if(i % 2 == 0)
{
s.name = "旺财";
s.age = 7;
}
else
{
s.name = "小强强强";
s.age = 3;
}
i++;
}
}
} } public class GetThread implements Runnable
{
private Student s; public GetThread(Student s)
{
this.s = s;
} @Override
public void run()
{
while(true)
{
synchronized (s)
{
System.out.println(s.name + "---" + s.age);
}
}
}
}
24.05 生产者消费者之等待唤醒机制思路
思路:
生产者:先看是否有数据,有就等待,没有就生产,生产完之后通知消费者消费
消费者:先看是否有数据,有就消费,没有就等待,等待时通知生产者生产
Java提供了等待唤醒机制
24.06 生产者消费者之等待唤醒机制代码实现
通过Java提供的等待唤醒机制来实现一次一个输出
等待唤醒:
Object类中提供了三个方法:
wait():等待
notify():唤醒单个线程
notifyAll():唤醒所有线程
为什么操作线程的方法wait notify notifyAll定义在Object类中?
因为这些方法是监视器的方法,监视器其实就是锁。锁可以是任意的对象,任意的对象调用的方法一定定义在Object类中。
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();//等待立即释放锁
} catch (InterruptedException e)
{
e.printStackTrace();
}
} if (x % 2 == 0)
{
s.name = "小明";
s.age = 17;
}
else
{
s.name = "旺财";
s.age = 3;
}
x++; //修改标记
s.flag = true;
//唤醒线程
s.notify();
}
}
}
} 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(); //等待立即释放锁
}
catch (InterruptedException e)
{
e.printStackTrace();
}
} System.out.println(s.name + "---" + s.age);
//修改标记
s.flag = false;
//唤醒线程
s.notify();
}
}
}
}
24.07 线程的状态转换图及常见执行情况
24.08 线程组的概述和使用
Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。
方法:public final ThreadGroup getThreadGroup()
返回该线程所属的线程组。如果该线程已经终止(停止运行),该方法则返回 null。
构造方法:public Thread(ThreadGroup group,Runnable target,String name)
分配新的 Thread 对象,以便将 target 作为其运行对象,将指定的 name 作为其名称,并作为 group 所引用的线程组的一员。
默认情况下,所有的线程都属于主线程组。
例:
public class Practice
{
public static void main(String[] args)
{
method2();
}
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();//main
String name2 = tg2.getName();//main
System.out.println(name1);
System.out.println(name2);
// 线程默认情况下属于main线程组,所有的线程都属于同一个组
System.out.println(Thread.currentThread().getThreadGroup().getName());//main
}
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);
}
}
24.09 生产者消费者之等待唤醒机制代码优化
public class Student
{
private String name;
private int age;
// 默认情况是没有数据,如果是true,说明有数据
private boolean flag; 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 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("xiaoming", 17);
}
else
{
s.set("旺财", 7);
}
x++;
}
}
}
--------------------------------------------------------------------
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 Practice
{
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();
}
}
24.10 线程池的概述和使用
程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。
线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池
JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法
1.public static ExecutorService newCachedThreadPool():创建一个具有缓存功能的线程池
创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。
2.public static ExecutorService newFixedThreadPool(int nThreads):创建一个可重用的,具有固定线程数的线程池
创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。
3.public static ExecutorService newSingleThreadExecutor():创建一个只有单线程的线程池,相当于上个方法的参数是1
创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。
这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。
接口 ExecutorService提供了如下方法
1.Future<?> submit(Runnable task)
提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。该 Future 的 get 方法在成功完成时将会返回 null。
2.<T> Future<T> submit(Callable<T> task)
提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future。
例:
public class Practice
{
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();
}
}
24.11 多线程方式3的代码实现
//这里指定的泛型其实是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;
}
} public class Practice
{
public static void main(String[] args)
{
//创建线程池对象
ExecutorService pool = Executors.newFixedThreadPool(2); //可以执行Runnable对象或者Callable对象代表的线程
pool.submit(new MyCallable());
pool.submit(new MyCallable()); //结束
pool.shutdown();
}
}
好处:可以有返回值、可以抛出异常
弊端:代码比较复杂,所以一般不用
24.12 多线程方式3的求和案例
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;
}
} public class Practice
{
public static void main(String[] args) throws InterruptedException, ExecutionException
{
//创建线程池对象
ExecutorService pool = Executors.newFixedThreadPool(2); //可以执行Runnable对象或者Callable对象代表的线程
Future<Integer> f1 = pool.submit(new MyCallable(6));
Future<Integer> f2 = pool.submit(new MyCallable(100)); Integer i1 = f1.get();
Integer i2 = f2.get();
System.out.println(i1);
System.out.println(i2);
//结束
pool.shutdown();
}
}
24.13 匿名内部类的方式实现多线程程序
public class Practice
{
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();
}
}
24.14 定时器的概述和使用
定时器是一个应用十分广泛的线程工具,可用于调度多个定时任务以后台线程的方式执行。在Java中,可以通过Timer和TimerTask类来实现定义调度的功能
类 Timer:一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。
构造方法:
public Timer()
创建一个新计时器。相关的线程不作为守护程序运行。
方法:
1.public void schedule(TimerTask task,long delay)
安排在指定延迟后执行指定的任务。
2.public void schedule(TimerTask task,long delay,long period)
安排指定的任务从指定的延迟后开始进行重复的固定延迟执行。
类 TimerTask:由 Timer 安排为一次执行或重复执行的任务。
1.public abstract void run()
此计时器任务要执行的操作。
2.public boolean cancel()
取消此计时器任务。如果任务安排为一次执行且还未运行,或者尚未安排,则永远不会运行。
开发中:Quartz是一个完全由java编写的开源调度框架。
例:
public class Practice
{
public static void main(String[] args)
{
// 创建定时器对象
Timer t = new Timer();
//结束任务
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("任务执行");
t.cancel();
}
}
24.15 定时任务的多次执行代码体现
public class Practice
{
public static void main(String[] args)
{
// 创建定时器对象
Timer t = new Timer();
t.schedule(new MyTask(), 3000, 2000);
}
}
//做一个任务
class MyTask extends TimerTask
{
@Override
public void run()
{
System.out.println("任务执行");
}
}
24.16 定时删除指定的带内容目录
public class Practice
{
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);
}
}
//做一个任务
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());
}
}
}
24.17 多线程常见的面试题
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:线程的生命周期图
新建 -- 就绪 -- 运行 -- 死亡
新建 -- 就绪 -- 运行 -- 阻塞 -- 就绪 -- 运行 -- 死亡
画图解释
24.18 面向对象的常见设计原则
面向对象思想的设计原则:
1.单一职责原则:
其实就是开发人员经常说的”高内聚,低耦合”
也就是说,每个类应该只有一个职责,对外只能提供一种功能,而引起类变化的原因应该只有一个。在设计模式中,所有的设计模式都遵循这一原则。
2.开闭原则
核心思想是:一个对象对扩展开放,对修改关闭。
其实开闭原则的意思就是:对类的改动是通过增加代码进行的,而不是修改现有代码。
3.里氏替换原则
核心思想:在任何父类出现的地方都可以用它的子类来替代。
其实就是说:同一个继承体系中的对象应该有共同的行为特征。
4.依赖注入原则
核心思想:要依赖于抽象,不要依赖于具体实现。
其实就是说:在应用程序中,所有的类如果使用或依赖于其他的类,则应该依赖这些其他类的抽象类,而不是这些其他类的具体类。为了实现这一原则,就要求我们在编程的时候针对抽象类或者接口编程,而不是针对具体实现编程。
5.接口分离原则
核心思想:不应该强迫程序依赖它们不需要使用的方法。
其实就是说:一个接口不需要提供太多的行为,一个接口应该只提供一种对外的功能,不应该把所有的操作都封装到一个接口中。
6.迪米特原则
核心思想:一个对象应当对其他对象尽可能少的了解
其实就是说:降低各个对象之间的耦合,提高系统的可维护性。在模块之间应该只通过接口编程,而不理会模块的内部工作原理,它可以使各个模块耦合度降到最低,促进软件的复用
24.19 设计模式的概述和分类
设计模式概述:设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。
设计模式不是一种方法和技术,而是一种思想
设计模式和具体的语言无关,学习设计模式就是要建立面向对象的思想,尽可能的面向接口编程,低耦合,高内聚,使设计的程序可复用,学习设计模式能够促进对面向对象思想的理解,反之亦然。它们相辅相成
设计模式的几个要素:
名字必须有一个简单,有意义的名字
问题描述在何时使用模式
解决方案描述设计的组成部分以及如何解决问题
效果描述模式的效果以及优缺点
设计模式的分类
创建型模式对象的创建
结构型模式对象的组成(结构)
行为型模式对象的行为
创建型模式:简单工厂模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式、单例模式。(6个)
结构型模式:外观模式、适配器模式、代理模式、装饰模式、桥接模式、组合模式、享元模式。(7个)
行为型模式:模版方法模式、观察者模式、状态模式、职责链模式、命令模式、访问者模式、策略模式、备忘录模式、迭代器模式、解释器模式。(10个)
24.20 简单工厂模式概述和使用
简单工厂模式概述:又叫静态工厂方法模式,它定义一个具体的工厂类负责创建一些类的实例
优点:客户端不需要在负责对象的创建,从而明确了各个类的职责
缺点:这个静态工厂类负责所有对象的创建,如果有新的对象增加,或者某些对象的创建方式不同,就需要不断的修改工厂类,不利于后期的维护
例:
public class Practice
{
public static void main(String[] args)
{
// // 具体类调用
// Dog d = new Dog();
// d.eat();
// Cat c = new Cat();
// c.eat();
// System.out.println("------------"); // //使用工厂创建对象
// Dog dd = AnimalFactory.createDog();
// Cat cc = AnimalFactory.createCat();
// dd.eat();
// cc.eat();
// System.out.println("------------"); // 工厂改进后
Animal a = AnimalFactory.createAnimal("dog");
a.eat();
a = AnimalFactory.createAnimal("cat");
a.eat(); // NullPointerException
a = AnimalFactory.createAnimal("pig");
if (a != null)
{
a.eat();
}
else
{
System.out.println("没有这种动物");
}
}
}
abstract class Animal
{
public abstract void eat();
} class Dog extends Animal
{
@Override
public void eat()
{
System.out.println("狗吃肉");
}
}
class Cat extends Animal
{ @Override
public void eat()
{
System.out.println("猫吃鱼");
}
} //工厂类
class AnimalFactory
{
private AnimalFactory()
{} // public static Dog createDog() {
// return new Dog();
// }
//
// public static Cat createCat() {
// return new Cat();
// } public static Animal createAnimal(String type)
{
if ("dog".equals(type))
{
return new Dog();
}
else if ("cat".equals(type))
{
return new Cat();
}
else
{
return null;
}
}
}
24.21 工厂方法模式的概述和使用
工厂方法模式概述:工厂方法模式中抽象工厂类负责定义创建对象的接口,具体对象的创建工作由继承抽象工厂的具体类实现。
优点:客户端不需要在负责对象的创建,从而明确了各个类的职责,如果有新的对象增加,只需要增加一个具体的类和具体的工厂类即可,不影响已有的代码,后期维护容易,增强了系统的扩展性
缺点:需要额外的编写代码,增加了工作量
例:
public class Practice
{
public static void main(String[] args)
{
// 创建狗
Factory f = new DogFactory();
Animal a = f.createAnimal();
a.eat();
System.out.println("-------"); //创建猫
f = new CatFactory();
a = f.createAnimal();
a.eat();
}
}
//动物类
abstract class Animal
{
public abstract void eat();
}
//狗类
class Dog extends Animal
{
@Override
public void eat()
{
System.out.println("狗吃肉");
}
}
//猫类
class Cat extends Animal
{ @Override
public void eat()
{
System.out.println("猫吃鱼");
}
}
//工厂
interface Factory
{
public abstract Animal createAnimal();
}
//创建Cat的工厂
class CatFactory implements Factory
{
@Override
public Animal createAnimal()
{
return new Cat();
} }
//创建Dog的工厂
class DogFactory implements Factory
{
@Override
public Animal createAnimal()
{
return new Dog();
}
}
24.22 单例模式之饿汉式
单例设计模式概述:单例模式就是要确保类在内存中只有一个对象,该实例必须自动创建,并且对外提供。
优点:在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。
缺点:没有抽象层,因此扩展很难。职责过重,在一定程序上违背了单一职责
保证对象唯一性的条件:
1.不允许其他程序用new创建该类对象
2.在该类创建一个本类实例
3.对外提供一个方法让其他程序可以获取该对象
保证对象唯一性的步骤:
1.私有化该类的构造函数
2.通过new在本类中创建一个本类的对象
3.定义一个公有的方法,将创建的对象返回
例:
public class Student
{
// 构造私有
private Student()
{} // 静态方法只能访问静态成员变量,加静态
// 为了不让外界直接访问修改这个值,加private
private static Student s = new Student(); // 提供公共的访问方式
// 为了保证外界能够直接使用该方法,加静态
public static Student getStudent()
{
return s;
}
}
24.23 单例模式之懒汉式
例:
class Teacher
{
private Teacher()
{} private static Teacher t = null; public static Teacher getTeacher()
{
if (t == null) //该语句在被多线程操作时存在安全隐患
{
t = new Teacher();
}
return t;
}
}
上面的单例模式在被多线程操作时是有问题的
解决单例模式延迟加载涉及的多线程安全问题
方案1:使用同步方法(多线程操作时每次都需要判断锁,效率低)
class Teacher
{
private Teacher()
{} private static Teacher t = null;
//同步方法
public synchronized static Teacher getTeacher()
{
if (t == null)
{
t = new Teacher();
}
return t;
}
}
方案2:使用同步代码块
class Teacher
{
private Teacher()
{} private static Teacher t = null; public static Teacher getTeacher()
{
if (t == null) //提高效率
{
//同步代码块
synchronized (t) //解决安全问题
{
if(t == null)
t = new Teacher();
}
}
return t;
}
}
24.24 单例模式的Java代码体现Runtime类
Runtime类概述:每个Java应用程序都有一个Runtime类实例,使应用程序能够与其运行的环境相连接。
可以通过 getRuntime 方法获取当前运行时。应用程序不能创建自己的 Runtime 类实例。(单例设计模式饿汉式)
Runtime类使用
public Process exec(String command)throws IOException
在单独的进程中执行指定的字符串命令。
例:
Runtime r = Runtime.getRuntime();
r.exec("notepad");//打开记事本
JavaSE学习总结第24天_多线程2的更多相关文章
- JavaSE学习总结第23天_多线程1
23.01 多线程程序的引入 如果一个程序只有一个执行流程,所以这样的程序就是单线程程序. 如果一个程序有多条执行流程,那么,该程序就是多线程程序. 23.02 进程概述及多进程的意义 要想说 ...
- JavaSE学习总结第08天_面向对象3
08.01 工具类中使用静态 例: class ArrayTool { //将构造方法私有,外界不能创建该类的对象 private ArrayTool(){} //遍历数组 public stat ...
- JavaSE学习总结第09天_面向对象4
09.01 final关键字引入 例: class Fu { public final void show() { System.out.println("访问底层资源"); ...
- JavaSE学习总结第07天_面向对象2
07.01 成员变量和局部变量的区别 1.在类中的位置不同 成员变量 类中方法外 局部变量 方法内或者方法声明上 2.在内存中的位置不同 成员变量 堆内存 局部变量 栈内存 3 ...
- JavaSE学习总结第10天_面向对象5
10.01 运动员和教练案例分析 教练和运动员案例:运动员分乒乓球运动员和篮球运动员,教练分乒乓球教练和篮球教练.为了出国交流,跟乒乓球相关的人员都需要学习英语. 分析: 10.02 运动员和教练 ...
- JavaSE学习总结第11天_开发工具 & API常用对象1
11.01 常见开发工具介绍 1:操作系统自带的记事本软件 2:高级记事本软件例:Editplus,Notepad++,UltraEdit 3:集成开发环境 IDE(Integrated Deve ...
- JavaSE学习总结第27天_反射 & 设计模式 & JDK5、7、8新特性
27.01 反射_类的加载概述和加载时机 类的加载:当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化. 加载:就是指将class文件读 ...
- JavaSE学习总结第26天_网络编程
26.01 网络编程概述 网络编程:就是用来实现网络互连的不同计算机上运行的程序间可以进行数据交换. 26.02 网络模型概述和图解 计算机网络之间以何种规则进行通信,就是网络模型研究问题. ...
- JavaSE学习总结第15天_集合框架1
15.01 对象数组的概述和使用 public class Student { // 成员变量 private String name; private int age; // 构造方法 publ ...
随机推荐
- SQL Server ansi_null_default | ansi_null_dflt_on
先说一下这两个变量是一个意思,只是它们的作用范围不同 alter database dbTest set ansi_null_default on; -- 这个的作用域是整个SQL Server ...
- C#实现 ffmpeg视频转码、播放
近来公司项目要求实现全景相机的视频截取,但是截取的视频需求转码上传.经过研究采用ffmpeg转码,奉上一个详细介绍的博文: 最简单的基于FFMPEG的转码程序 主要是转码的操作过程,能够实现了从相机获 ...
- TCP的核心系列 — SACK和DSACK的实现(一)
TCP的实现中,SACK和DSACK是比较重要的一部分. SACK和DSACK的处理部分由Ilpo Järvinen (ilpo.jarvinen@helsinki.fi) 维护. tcp_ack() ...
- TP的SDK的调用
1,SDK简介 本SDK是基于ThinkPHP开发类库扩展,因此只能在ThinkPHP平台下使用(ThinkPHP版本要求2.0以上).DEMO中用到了控制器分层,因此运行DEMO需使用ThinkPH ...
- java final 关键字醍醐灌顶
醍醐灌顶: final 关键字,它可以修饰数据 .方法.类. 可能有些同学傻傻分不清出,这里可以快速弄懂final; final 实例域: 可以将实例域定义为final,构建对象时必须初始化这样的域, ...
- 微型 ORM 的第一篇 DapperLambda发布
引言:因为接触过多个ORM,但使用的时候都遇到了各自的一些不够理想的地方,从最早开始开始公司自己分装的,到后面用EF,以及Dapper和DapperExtensions 到现在用的FluentDat ...
- 浅谈Struts2(三)
一.Struts2收集client的参数 核心思路: <form method="post" action="XXXX"> <input ty ...
- 面试题之java 编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串。 要求不能出现截半的情况
题目:10. 编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串. 但是要保证汉字不被截半个,如"我ABC"4,应该截为"我AB",输 ...
- mysql修改用户名和密码
修改用户名 mysql> use mysql; 选择数据库Database changedmysql> update user set user="dns" wher ...
- php 计算多维数组中所有值的总和
php 内置函数 array_sum() 函数返回数组中所有值的总和,只能返回一维数组的总和: 计算多维数组所有值的和就要自定义函数了: function get_sum($array) { $num ...