死锁、Lock锁、等待唤醒机制、线程组、线程池、定时器、单例设计模式_DAY24
1:线程(理解)
(1)死锁
概念:
同步中,多个线程使用多把锁之间存在等待的现象。
原因分析:
a.线程1将锁1锁住,线程2将锁2锁住,而线程1要继续执行锁2中的代码,线程2要继续执行锁1中的代码,
但是此时,两个锁均处于锁死状态。最终导致两线程相互等待,进入无限等待状态。
b.有同步代码块的嵌套动作。
解决方法:
不要写同步代码块嵌套。
例子:cn.itcast2.DeadLockThread; cn.itcast2.demo
package cn.itcast2; public class DeadLockThread extends Thread { boolean flag;//定义标记,用来指定要执行的代码
public DeadLockThread(boolean flag) {
this.flag = flag;
} @Override
public void run() {
if(flag) {//flag赋值为true时,执行的代码
synchronized (Demo.LOCK1) {
System.out.println("if中锁1");
/*try { //加上睡眠时间,就不正常了,有可能会出现死锁现象
sleep(100l);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
synchronized (Demo.LOCK2) {
System.out.println("if中锁2");
}
}
} else {//flag赋值为false时,执行的代码
try { //加上睡眠时间,等线程1执行完,就正常了,不会出现死锁现象
sleep(100l);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (Demo.LOCK2) {
System.out.println("else中锁2");
synchronized (Demo.LOCK1) {
System.out.println("else中锁1");
}
}
}
}
}
package cn.itcast2;
/*
* 线程死锁:
* 线程锁相互等待
*/
public class Demo { public static final Object LOCK1 = new Object();
public static final Object LOCK2 = new Object(); public static void main(String[] args) { DeadLockThread dlt = new DeadLockThread(true);
DeadLockThread dlt2 = new DeadLockThread(false); dlt.start();
dlt2.start();
} }
(2)JDK5的新特性:Lock锁
它把什么时候获取锁,什么时候释放锁的时间给明确了。
例子:cn.itcast.demo3 cn.itcast.MyTicket
package cn.itcast; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; /*
* Lock锁:
* 就是实现同步的另外的一种锁。另外的一种同步操作。
* Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。
*
* 子类对象:ReentrantLock
*
* public void lock() 上锁
* public void unlock() 解锁
*/
public class Demo3 { public static final Object MY_LOCK = new Object();
public static final Lock MY_LOCK2 = new ReentrantLock(); public static void main(String[] args) { MyTicket mt = new MyTicket(); Thread thread = new Thread(mt,"唐嫣");
Thread thread2 = new Thread(mt,"柳岩");
Thread thread3 = new Thread(mt,"高圆圆"); thread.start();
thread2.start();
thread3.start();
} }
package cn.itcast; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class MyTicket implements Runnable { private int number = 100;
Lock lock = new ReentrantLock(); @Override
public void run() { //卖票的动作
while(true) { // synchronized(Demo3.MY_LOCK){
lock.lock();
// Demo3.MY_LOCK2.lock();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
} if(number>0) {
System.out.println(Thread.currentThread().getName()+":"+number--);
}
lock.unlock();
// Demo3.MY_LOCK2.unlock();
// }
} } }
(3)线程间的通信(生产者与消费者问题)
不同种类的线程针对同一个资源的操作。生产者线程生产与消费者线程消费,操作同一数据。
例子:cn.itcast3.*
package cn.itcast3; public class CRunnable implements Runnable {
Person p; public CRunnable(Person p) {
this.p = p;
} @Override
public void run() {
while (true) {
synchronized (p) {
//c
System.out.println(p);
}
}
} }
package cn.itcast3;
/*
* 生产者消费者问题:
*
* 共享数据:一个Person对象
* 生产者:为该对象属性赋值
* 消费者:获取对象属性值
*
* 采取第二种方式完成。
*/
public class Demo { public static void main(String[] args) { Person person = new Person();
//生产线程运行目标
PRunnable p = new PRunnable(person);
//消费线程运行目标
CRunnable c = new CRunnable(person);
//创建生产线程
Thread pThread = new Thread(p);
//创建消费线程
Thread cThread = new Thread(c);
//开启消费与生产
pThread.start();
cThread.start();
} }
package cn.itcast3;
/*
* 被共享的对象对应的类
*/
public class Person { private String name;
private int age; public Person() {
super();
} public Person(String name, int age) {
super();
this.name = name;
this.age = age;
} @Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
}
}
package cn.itcast3;
/*
* 生产者线程执行目标
*/
public class PRunnable implements Runnable { Person p;
int x = 0; public PRunnable(Person p) {
this.p = p;
} @Override
public void run() { while(true) {
synchronized (p) {
if (x % 2 == 0) {
p.setName("唐嫣");
p.setAge(28);
} else {
p.setName("刘正风");
//p
p.setAge(29);
}
x++;
}
}
} }
(4)等待唤醒机制
涉及并非是Thread类的方法,而是Object类的两个方法:因为锁可以为共享数据本身可以是任意的对象,在runnable中进行等待唤醒当前所在线程。
等待:
public final void wait() throws InterruptedException
让当前线程进入等待状态,如果线程进入该状态,不唤醒或打断,不会解除等待状态。
进入等待状态时会释放锁。
唤醒:
public final void notify()
唤醒正在等待的线程。
继续等待之后的代码执行。
sleep与wait的区别:
sleep指定时间,wait可指定可不指定。
sleep释放执行权,不释放锁。因为一定可以醒来。
wait释放执行权与锁。
例子:cn.itcast4.*
package cn.itcast4; /*
* 消费者线程执行目标
*/
public class CRunnable implements Runnable {
Person p; public CRunnable(Person p) {
this.p = p;
} // 判断>>生产/消费>>修改标志位>>唤醒
@Override
public void run() {
while (true) {
synchronized (p) {
// 判断是否有数据
if (!p.flag) {// 如果没有数据,消费者等待
try {
p.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} // 消费
// System.out.println(p);
System.out.println(p.getName() + ":" + p.getAge());
// 将数据标志位,置为没有值的false
p.flag = false;
// 唤醒正在等待的其他线程
p.notify();
}
}
} }
package cn.itcast4;
/*
* 生产者消费者问题:
*
* 共享数据:一个Person对象
* 生产者:为该对象属性赋值
* 消费者:获取对象属性值
*
* 采取第二种方式完成。
*/
public class Demo { public static void main(String[] args) { Person person = new Person();
//生产线程运行目标
PRunnable p = new PRunnable(person);
//消费线程运行目标
CRunnable c = new CRunnable(person);
//创建生产线程
Thread pThread = new Thread(p);
//创建消费线程
Thread cThread = new Thread(c);
//开启消费与生产
pThread.start();
cThread.start();
} }
package cn.itcast4;
/*
* 被共享的对象对应的类
*/
public class Person { private String name;
private int age;
//标记是否有数据。true为有数据,需要消费。false没有数据,需要生产。
boolean flag = false; public Person() {
super();
} public Person(String name, int age) {
super();
this.name = name;
this.age = age;
} @Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
}
}
package cn.itcast4; /*
* 生产者线程执行目标
*/
public class PRunnable implements Runnable { Person p;
int x = 0; public PRunnable(Person p) {
this.p = p;
} // 判断>>生产/消费>>修改标志位>>唤醒
@Override
public void run() { while (true) {
synchronized (p) {
// 判断是否有数据
if (p.flag) {// 如有有数据,生产者等待
try {
p.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 生产
if (x % 2 == 0) {
p.setName("唐嫣");
p.setAge(28);
} else {
p.setName("刘正风");
p.setAge(29);
}
x++; // 将数据标志位,置为有值的true
p.flag = true;
// 唤醒正在等待的其他线程
p.notify();
}
}
} }
(5)线程组
多个线程出现时,可以将线程分组,统一处理。
涉及线程组类:ThreadGroup
注意:
线程组可以包含线程或者线程组。
当前线程只能访问所在线程组或者子组。不能访问父组或者兄弟组。
如果没有指定线程组,则属于main线程组。
主要方法:
public final String getName()
public final void setDaemon(boolean?daemon)
无线程添加方法,设置线程组在Thread构造方法中。
例子:cn.itcast5.demo
package cn.itcast5;
/*
* 线程组:
* 多个线程出现时,可以将线程分组,统一处理
* Thread相关线程组的方法:
* public Thread(ThreadGroup group,Runnable target,String name)
* public final ThreadGroup getThreadGroup()
* ThreadGroup:
* public final String getName()
*/
public class Demo { public static void main(String[] args) { ThreadGroup tg = new ThreadGroup("唐嫣的小组");
Thread thread = new Thread(tg, new Runnable() { @Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
}
},"线程1"); Thread thread2 = new Thread(new Runnable() { @Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
}
},"线程1"); System.out.println("thread:"+thread.getThreadGroup().getName()); //thread:唐嫣的小组
System.out.println("thread2:"+thread2.getThreadGroup().getName()); //thread2:main
System.out.println("main:"+Thread.currentThread().getThreadGroup().getName()); //main:main }
}
(6)线程池
将线程放置到同一个线程池中,其中的线程可以反复使用,无需反复创建线程而消耗过多资源。
Executors:线程池创建工厂类
返回线程池方法(还有其他方法) : public static ExecutorService newFixedThreadPool(int?nThreads)
ExecutorService:线程池类
主要方法:
Future<?> submit(Runnable?task)
<T> Future<T> submit(Callable<T>?task)
void shutdown()
注意:
线程池提交方法后,程序并不终止,是因为线程池控制了线程的关闭等。
Callable:相当于有返回值类型的runnable。
Future是将结果抽象的接口。可以将线程执行方法的返回值返回。
例子:cn.itcast5.demo2
package cn.itcast5; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; /*
* 线程池:用来存放线程。
* 一、Executors:线程池工厂类
* 主要方法:
* public static ExecutorService newFixedThreadPool(int nThreads)
*
*二、 ExecutorService接口
* 主要方法:
* 1、Future<?> submit(Runnable task)
* 2、<T> Future<T> submit(Callable<T> task)
* 3、void shutdown()
*/
public class Demo2 { public static void main(String[] args) { //使用线程池工厂创建线程池对象
ExecutorService es = Executors.newFixedThreadPool(2); //使用线程池,第一次提交runnable
es.submit(new Runnable(){ @Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("第一次:"+i);
}
}}); //使用线程池,第二次提交runnable
es.submit(new Runnable(){ @Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("第二次"+i);
}
}}); //不一定要关闭,但是这里关闭了线程池
es.shutdown();
}
}
(7)线程实现的第三种方式
a.使用线程池工厂创建线程池对象 : ExecutorService es = Executors.newFixedThreadPool(2);
b.实现Callable接口,public public class MyCallable implements Callable<String>{},然后重写其中的call()方法,相当于runnable中的run()方法
c.创建Callable类的实例对象 : MyCallable mc = new MyCallable();
d.线程池提交Callable,返回Future结果 : Future<String> submit2 = es.submit(mc);
e.通过Future对象获取call方法的返回结果: System.out.println(submit2.get());
例子:cn.itcast5.MyCallable cn.itcast5.demo3
package cn.itcast5; import java.util.concurrent.Callable; /*
* 定义一个Callable类。
* 实现多线程的第三种方式。
*/
public class MyCallable implements Callable<String>{ //重写call方法,相当于runnable中的run方法
@Override
public String call() throws Exception {
return "经过多线程的学习,我发现,编程真的很有意思!";
} }
package cn.itcast5; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; /*
*
* Future<?> submit(Runnable task)
* <T> Future<T> submit(Callable<T> task)
*
* Future:线程执行返回结果
* V get()
throws InterruptedException,
ExecutionException 返回计算结果
Callable:
*/
public class Demo3 { public static void main(String[] args) throws Exception { //使用线程池工厂创建线程池对象
ExecutorService es = Executors.newFixedThreadPool(2); //使用线程池,提交runnable
Future<?> submit = es.submit(new Runnable(){ @Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}}); Object object = submit.get();
System.out.println(object); //输出结果 null //实现多线程的第三种方式。
//创建Callable类的实例对象
MyCallable mc = new MyCallable();
//线程池提交Callable,返回Future结果
Future<String> submit2 = es.submit(mc);
System.out.println(submit2); //打印的是地址
//通过Future对象获取call方法的返回结果。
System.out.println(submit2.get()); // call()方法返回的内容:经过多线程的学习,我发现,编程真的很有意思!
} }
(8)定时器
定时器:Timer类
构造方法:
public Timer()
主要方法:
public void schedule(TimerTask task, Date time) 指定时间点完成指定任务
public void schedule(TimerTask task,Date firstTime,long period) 指定第一次开始时间和后边重复执行的时间完成指定任务
public void schedule(TimerTask task, long delay) 在指定时间后执行指定任务一次
public void schedule(TimerTask task,long delay, long period) 指定时间后执行指定任务第一次,再在每指定时间后,执行指定任务
定时器任务类:TimerTask (实现了Runnable接口)
public abstract void run() 要重写的真正执行的代码
针对于cancel()方法:
定时器的该方法终止定时器的。
定时器任务的该方法用来取消该定时器任务的任务。
例子:cn.itcast5.demo4 ; cn.itcast5.MyTask
package cn.itcast5; import java.util.Date;
import java.util.Timer; /*
* 定时器:Timer类
* 构造方法:
* public Timer()
* 主要方法:
* public void schedule(TimerTask task, Date time) 指定时间点完成指定任务
* public void schedule(TimerTask task,Date firstTime,long period) 指定第一次开始时间和后边重复执行的时间完成指定任务
* public void schedule(TimerTask task, long delay) 在指定时间后执行指定任务一次
* public void schedule(TimerTask task,long delay, long period) 指定时间后执行指定任务第一次,再在每指定时间后,执行指定任务
* 定时器任务类:TimerTask 实现了Runnable接口
* public abstract void run() 要重写的真正执行的代码
*
* 针对于cancel()方法:
* 定时器的该方法终止定时器的。
* 定时器任务的该方法用来取消该定时器任务的任务。
*/
public class Demo4 { public static void main(String[] args) { //创建定时器对象
Timer timer = new Timer(); //创建定时器任务对象
MyTask myTask = new MyTask(); //调用计划执行方法
//定时器不可以重复schedule任务
// timer.schedule(myTask, new Date(), 3000);
// timer.schedule(myTask, new Date(new Date().getTime()+3000), 5000); // myTask.cancel(); 任务不能先取消再执行。
timer.schedule(myTask, 3000, 5000); // myTask.cancel(); //定时器任务的该方法用来取消该定时器任务的任务,定时器不会终止。
// timer.cancel(); //定时器的该方法终止定时器的。
} }
package cn.itcast5; import java.util.TimerTask;
/*
* 定义搞一个TimerTask实际类
*/
public class MyTask extends TimerTask { @Override
public void run() { System.out.println("地震了!");
} }
(9)最简单的线程程序代码:
new Thread() {
public void run() {
for(int x=0; x<100; x++) {
System.out.println(x);
}
}
}.start();
new Thread(new Runnable(){
public void run() {
for(int x=0; x<100; x++) {
System.out.println(x);
}
}
}).start();
2:单例设计模式(理解 面试)
(1)保证类在内存中只有一个对象。
(2)怎么保证:
A:构造私有
B:自己造一个对象
C:提供公共访问方式
(3)两种方式:
A:懒汉式(面试)
public class Student {
private Student(){}
private static Student s = null;
public synchronized static Student getStudent() {
if(s == null) {
s = new Student();
}
return s;
}
}
B:饿汉式(开发)
public class Student {
private Student(){}
private static Student s = new Student();
public static Student getStudent() {
return s;
}
}
(4)JDK的一个类本身也是单例模式的。
Runtime
(5)例子:cn.itcast5.Demo5 cn.itcast5.SinglePerson2 cn.itcast5.SinglePerson
package cn.itcast5; public class Demo5 { public static void main(String[] args) { // SinglePerson sp = new SinglePerson("唐嫣", 28);
// SinglePerson sp2 = new SinglePerson("唐嫣", 28); //饿汉式对象
SinglePerson sp = SinglePerson.getInstance();
SinglePerson sp2 = SinglePerson.getInstance(); System.out.println(sp==sp2);
//懒汉式对象
SinglePerson2 sp3 = SinglePerson2.getInstance();
SinglePerson2 sp4 = SinglePerson2.getInstance(); System.out.println(sp3==sp4); } }
package cn.itcast5;
/*
* 懒汉式单例
*/
public class SinglePerson2 { private static final Object LOCK = new Object();
private String name;
private int age; //定义一个private,static修饰的成员变量,类型是自己。
private static SinglePerson2 sp; //经构造方法私有化,外界无法创建对象,只能自己创建自己
private SinglePerson2(String name, int age) {
super();
this.name = name;
this.age = age;
}
//定义一个公共的,静态的,返回值类型为自己的的获取实例对象的方法。不考虑多线程
// public static SinglePerson2 getInstance() {
// if(sp==null) {
// System.out.println("没有对象");
// sp = new SinglePerson2("唐嫣", 28);
// }
// return sp;
// }
//定义一个公共的,静态的,返回值类型为自己的的获取实例对象的方法。考虑多线程,使用代码块,不考虑效率
// public static SinglePerson2 getInstance() {
//
// synchronized (SinglePerson2.LOCK) {
// if (sp == null) {
// System.out.println("没有对象");
// sp = new SinglePerson2("唐嫣", 28);
// }
// }
//
// return sp;
// } //定义一个公共的,静态的,返回值类型为自己的的获取实例对象的方法。考虑多线程,使用同步方法,不考虑效率
// public static synchronized SinglePerson2 getInstance() {
//
// if (sp == null) {
// System.out.println("没有对象");
// sp = new SinglePerson2("唐嫣", 28);
// }
//
// return sp;
// } //定义一个公共的,静态的,返回值类型为自己的的获取实例对象的方法。考虑多线程,使用代码块,考虑效率
public static SinglePerson2 getInstance() { if(sp==null) { //如果没有对象,才进行同步操作
synchronized (SinglePerson2.LOCK) {
if (sp == null) { //同步操作过程中,必须判断的条件
System.out.println("没有对象");
sp = new SinglePerson2("唐嫣", 28);
}
}
} return sp;
} @Override
public String toString() {
return "SinglePerson [name=" + name + ", age=" + age + "]";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
package cn.itcast5;
/*
* 饿汉式单例
*/
public class SinglePerson { private String name;
private int age; //定义一个private,static修饰的成员变量,类型是自己。
private static SinglePerson sp = new SinglePerson("唐嫣", 28); //经构造方法私有化,外界无法创建对象,只能自己创建自己
private SinglePerson(String name, int age) {
super();
this.name = name;
this.age = age;
}
//定义一个公共的,静态的,返回值类型为自己的的获取实例对象的方法。
public static SinglePerson getInstance() {
return sp;
} @Override
public String toString() {
return "SinglePerson [name=" + name + ", age=" + age + "]";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
} }
3、习题
(1)、使用定时器完成下列需求:某地区每隔1秒地震一次,当震过5次时,不再地震。
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask; /*
* 使用定时器完成下列需求:让某地区每隔1秒地震一次,当震过5次时,不再地震。(将地震打印五次,停止定时器,要用到定时器任务)
*/
public class Test { public static void main(String[] args) { //定义计时器
Timer timer = new Timer(); //创建定时器任务对象
MyTask mt = new MyTask(5,timer); //计划执行任务
timer.schedule(mt, new Date(), 1000); // //关闭计时器
// timer.cancel(); //不能在此处直接关
} } //定义计时器任务类
class MyTask extends TimerTask { private int times;
private Timer timer; public MyTask(int times, Timer timer) {
super();
this.times = times;
this.timer = timer;
} @Override
public void run() { if(times>0) {
System.out.println("地震了!");
}else {
timer.cancel();
} times--;
} }
死锁、Lock锁、等待唤醒机制、线程组、线程池、定时器、单例设计模式_DAY24的更多相关文章
- 等待唤醒机制----线程池----lambda表达式
1.等待唤醒机制 1.1线程间通信 概念:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同. 比如:线程A用来生成包子的,线程B用来吃包子的,包子可以理解为同一资源,线程A与线程B处理的 ...
- 等待唤醒机制,UDP通信和TCP通信
等待唤醒机制 通过等待唤醒机制使各个线程能有效的利用资源. 等待唤醒机制所涉及到的方法: wait() :等待,将正在执行的线程释放其执行资格 和 执行权,并存储到线程池中. notify():唤醒, ...
- JAVA之旅(十四)——静态同步函数的锁是class对象,多线程的单例设计模式,死锁,线程中的通讯以及通讯所带来的安全隐患,等待唤醒机制
JAVA之旅(十四)--静态同步函数的锁是class对象,多线程的单例设计模式,死锁,线程中的通讯以及通讯所带来的安全隐患,等待唤醒机制 JAVA之旅,一路有你,加油! 一.静态同步函数的锁是clas ...
- java多线程(死锁,lock接口,等待唤醒机制)
一.Lock接口 常用方法 Lock提供了一个更加面对对象的锁,在该锁中提供了更多的操作锁的功能. 使用Lock接口,以及其中的lock()方法和unlock()方法替代同步,对电影院卖票案例中Tic ...
- 00103_死锁、Lock接口、等待唤醒机制
1.死锁 (1)同步锁使用的弊端:当线程任务中出现了多个同步(多个锁)时,如果同步中嵌套了其他的同步.这时容易引发一种现象:程序出现无限等待,这种现象我们称为死锁.这种情况能避免就避免掉: synch ...
- java ->多线程_线程同步、死锁、等待唤醒机制
线程安全 如果有多个线程在同时运行,而这些线程可能会同时运行这段代码.程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的. l 我们通过一个案例,演示线 ...
- 多线程之间的通信(等待唤醒机制、Lock 及其它线程的方法)
一.多线程之间的通信. 就是多个线程在操作同一份数据, 但是操作的方法不同. 如: 对于同一个存储块,其中有两个存储位:name sex, 现有两个线程,一个向其中存放数据,一个打印其中的数据. ...
- “全栈2019”Java多线程第三十二章:显式锁Lock等待唤醒机制详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- Java多线程02(线程安全、线程同步、等待唤醒机制)
Java多线程2(线程安全.线程同步.等待唤醒机制.单例设计模式) 1.线程安全 如果有多个线程在同时运行,而这些线程可能会同时运行这段代码.程序每次运行结果和单线程运行的结果是一样的,而且其他的变量 ...
随机推荐
- nexus 组件下载和上传
一. 重写 super pom 修改 maven 的 settings.xml Configuring Maven to Use a Single Repository Group <setti ...
- 【科普】Web(瓦片)地图的工作原理
[译者按:在看MapBox Guides文档时,看到这篇 How do web maps work?,这篇文档通俗易懂地阐述了Web地图是如何工作的,其实更偏向讲瓦片地图的工作原理,鉴于之前很多人不了 ...
- js基础学习笔记(五)
多种选择(Switch语句) 当有很多种选项的时候,switch比if else使用更方便. 语法: switch(表达式) { case值1: 执行代码块 1 break; case值2: 执行代码 ...
- PO Release Final Closed 灾难恢复
今天不小心 Final Closed了一条Po Release,只能通过后台更新数据恢复了. 更新后可接收可匹配,但不保证更新数据有遗漏,慎用. 更新前备份各表数据 UPDATE PO_LINE_LO ...
- .Net应用程序 参照的组合没有安装在您的系统中。 HRESULT: 0x800736B3
同事打开一个.Net的应用程序链接,一直无法启动.错误信息为: 啓用xx.application 時發生例外狀況. 已偵測到下列失敗訊息:參照的組合沒有安裝在您的系統中. (發生例外狀況於 HRESU ...
- asp.net—策略模式
一.什么是策略模式 定义:定义一系列算法,把一个个算法封装成独立类并实现同一个接口,使得它们之间可以相互替换. 二.怎么使用策略模式 首先模拟一个场景:有一个用户想买车. 可以有多种方式买车: (1 ...
- DropDownList切换选择,服务器控件Repeater未更新
将EnableViewState属性设置为false,这样禁止服务器控件使用视图状态,也就是禁止发送给浏览器HEML中的缓存副本, 每次都会使用新数据. 一旦页面的控件很多,频繁的传递控件状态值对网络 ...
- C#远程桌面连接工具
1.注册控件:regsvr32 C:\Windows\System32\mstscax.dll. 2.添加RDP Control控件到工具箱中.我选择的RDP Client Control (redi ...
- Meteor in Action(一)起步
杜撰的名字,这个系列文章旨在记录工作中开发APP所要学习meteor的过程. 最简单的例子,运行Meteor自带的缺省的Hello world例子. 安装好Meteor后,建立一个空白目录. 然后: ...
- Day6 ,周期末考试试题
Python基础数据类型考试题 考试时间:两个半小时 满分100分(80分以上包含80分及格) 一,基础题. 1,简述变量命名规范(3分) 2,字节和位的关系 ...