Java多线程编程——进阶篇二
一、线程的交互
a、线程交互的基础知识
唤醒在此对象监视器上等待的单个线程(notify()方法调用的时候,锁并没有被释放)。
void notifyAll()
唤醒在此对象监视器上等待的所有线程。
void wait()
导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法(wait()方法释放当前锁)。
导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量。
void wait(long timeout, int nanos)
导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量。
- 必须从同步环境内调用wait()、notify()、notifyAll()方法。
- 线程不能调用对象上的wait或notify的方法,除非它拥有那个对象的锁。
- wait()、notify()、notifyAll()都是Object的实例方法。与每个对象具有锁一样,每个对象可以有一个线程列表,他们等待来自该信号(通知)。线程通过执行对象上的wait()方法获得这个等待列表。从那时候起,它不再执行任何其他指令,直到调用对象的notify()方法为止。如果多个线程在同一个对象上等待,则将只选择一个线程(不保证以何种顺序)继续执行。如果没有线程等待,则不采取任何特殊操作。
* 计算输出其他线程锁计算的数据
*
*
*/
public class ThreadA {
public static void main(String[] args) {
ThreadB b = new ThreadB();
//启动计算线程
b.start();
//主线程拥有b对象上的锁。线程为了调用wait()或notify()方法,该线程必须是那个对象锁的拥有者
synchronized (b) {
try {
System.out.println("等待对象b完成计算...");
//当前主线程等待
b.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("b对象计算的总和是:" + b.total);
}
}
} /**
* 计算1+2+3 ... +100的和
*
*
*/
public class ThreadB extends Thread {
int total; public void run() {
synchronized (this) {
for (int i = 0; i < 101; i++) {
total += i;
}
//(完成计算了)唤醒在此对象监视器上等待的单个线程,在本例中线主线程被唤醒
notify();
}
}
}
执行结果:
等待对象b完成计算...
b对象计算的总和是:5050
千万注意:
b、多个线程在等待一个对象锁时候使用notifyAll()
* 计算线程
*
*
*/
public class Calculator extends Thread {
int total;
public void run() {
synchronized (this) {
for (int i = 0; i < 101; i++) {
total += i;
}
}
//通知在此对象上等待的所有线程
notifyAll();
}
}
* 获取计算结果并输出
*
*
*/
public class ReaderResult extends Thread {
Calculator c;
public ReaderResult(Calculator c) {
this.c = c;
}
public void run() {
synchronized (c) {
try {
System.out.println(Thread.currentThread() + "等待计算结果。。。");
c.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + "计算结果为:" + c.total);
}
}
public static void main(String[] args) {
Calculator calculator = new Calculator();
//启动三个线程,分别获取计算结果
new ReaderResult(calculator).start();
new ReaderResult(calculator).start();
new ReaderResult(calculator).start();
//启动计算线程
calculator.start();
}
}
Thread[Thread-2,5,main]等待计算结果...
Thread[Thread-3,5,main]等待计算结果...
Exception in thread "Thread-0" java.lang.IllegalMonitorStateException: current thread not owner
at java.lang.Object.notifyAll(Native Method)
at threadtest.Calculator.run(Calculator.java:18)
Thread[Thread-1,5,main]计算结果为:5050
Thread[Thread-2,5,main]计算结果为:5050
Thread[Thread-3,5,main]计算结果为:5050
二、线程的调度
a、休眠
/**
* Java线程:线程的调度-休眠
*
**/
public class Test {
public static void main(String[] args) {
Thread t1 = new MyThread1();
Thread t2 = new Thread(new MyRunnable());
t1.start();
t2.start();
}
} class MyThread1 extends Thread {
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("线程1第" + i + "次执行!");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} class MyRunnable implements Runnable {
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("线程2第" + i + "次执行!");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果:
线程2第0次执行!
线程1第0次执行!
线程1第1次执行!
线程2第1次执行!
线程1第2次执行!
线程2第2次执行!
从上面的运行结果可以看出,无法精准保证线程的执行次序。
b、优先级
/**
* Java线程:线程的调度-优先级
*
* */
public class Test {
public static void main(String[] args) {
Thread t1 = new MyThread1();
Thread t2 = new Thread(new MyRunnable());
t1.setPriority(10);
t2.setPriority(1); t2.start();
t1.start();
}
} class MyThread1 extends Thread {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("线程1第" + i + "次执行!");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} class MyRunnable implements Runnable {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("线程2第" + i + "次执行!");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果:
线程1第0次执行!
线程2第0次执行!
线程2第1次执行!
线程1第1次执行!
线程2第2次执行!
线程1第2次执行!
线程1第3次执行!
线程2第3次执行!
线程2第4次执行!
线程1第4次执行!
线程1第5次执行!
线程2第5次执行!
线程1第6次执行!
线程2第6次执行!
线程1第7次执行!
线程2第7次执行!
线程1第8次执行!
线程2第8次执行!
线程1第9次执行!
线程2第9次执行!
由上面的运行结果可以看出,虽然线程1的优先级较高,在真正这行的时候并没有绝对的优势,只是其获得执行的概率较大。
c、让步
/**
* Java线程:线程的调度-让步
*
*
*/
public class Test {
public static void main(String[] args) {
Thread t1 = new MyThread1();
Thread t2 = new Thread(new MyRunnable()); t2.start();
t1.start();
}
} class MyThread1 extends Thread {
public void run() {
for (int i = ; i < ; i++) {
System.out.println("线程1第" + i + "次执行!");
}
}
} class MyRunnable implements Runnable {
public void run() {
for (int i = ; i < ; i++) {
System.out.println("线程2第" + i + "次执行!");
Thread.yield();
}
}
}
运行结果:
线程2第0次执行!
线程2第1次执行!
线程2第2次执行!
线程2第3次执行!
线程1第0次执行!
线程1第1次执行!
线程1第2次执行!
线程1第3次执行!
线程1第4次执行!
线程1第5次执行!
线程1第6次执行!
线程1第7次执行!
线程1第8次执行!
线程1第9次执行!
线程2第4次执行!
线程2第5次执行!
线程2第6次执行!
线程2第7次执行!
线程2第8次执行!
线程2第9次执行!
从上面的执行结果可以看出,让步的线程最后执行完毕。
d、合并
线程的合并的含义就是将几个并行线程的线程合并为一个单线程执行,应用场景是当一个线程必须等待另一个线程执行完毕才能执行时可以使用join方法。
join为非静态方法,定义如下:
void join()
等待该线程终止。
void join(long millis)
等待该线程终止的时间最长为 millis 毫秒。
void join(long millis, int nanos)
等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。
/**
* Java线程:线程的调度-合并
*
* */
public class Test {
public static void main(String[] args) {
Thread t1 = new MyThread1();
t1.start(); for (int i = 0; i < 20; i++) {
System.out.println("主线程第" + i + "次执行!");
if (i > 2) try {
//t1线程合并到主线程中,主线程停止执行过程,转而执行t1线程,直到t1执行完毕后继续。
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} class MyThread1 extends Thread {
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("线程1第" + i + "次执行!");
}
}
}
运行结果:
主线程第0次执行!
主线程第1次执行!
主线程第2次执行!
线程1第0次执行!
主线程第3次执行!
线程1第1次执行!
线程1第2次执行!
线程1第3次执行!
线程1第4次执行!
线程1第5次执行!
线程1第6次执行!
线程1第7次执行!
线程1第8次执行!
线程1第9次执行!
主线程第4次执行!
主线程第5次执行!
主线程第6次执行!
主线程第7次执行!
主线程第8次执行!
主线程第9次执行!
主线程第10次执行!
主线程第11次执行!
主线程第12次执行!
主线程第13次执行!
主线程第14次执行!
主线程第15次执行!
主线程第16次执行!
主线程第17次执行!
主线程第18次执行!
主线程第19次执行!
从上面的运行结果中可以看出,使用join方法添加到主线程后,主线程停止运行,等待子线程运行结束后,主线程继续运行至结束。
e、守护线程
public final void setDaemon(boolean on)将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。
该方法必须在启动线程前调用。 该方法首先调用该线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException(在当前线程中)。 参数:
on - 如果为 true,则将该线程标记为守护线程。
抛出:
IllegalThreadStateException - 如果该线程处于活动状态。
SecurityException - 如果当前线程无法修改该线程。
另请参见:
isDaemon(), checkAccess()
/**
* Java线程:线程的调度-守护线程
*
*
*/
public class Test {
public static void main(String[] args) {
Thread t1 = new MyCommon();
Thread t2 = new Thread(new MyDaemon());
t2.setDaemon(true); //设置为守护线程 t2.start();
t1.start();
}
} class MyCommon extends Thread {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("线程1第" + i + "次执行!");
try {
Thread.sleep(7);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} class MyDaemon implements Runnable {
public void run() {
for (long i = 0; i < 9999999L; i++) {
System.out.println("后台线程第" + i + "次执行!");
try {
Thread.sleep(7);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果:
后台线程第0次执行!
线程1第0次执行!
线程1第1次执行!
后台线程第1次执行!
后台线程第2次执行!
线程1第2次执行!
线程1第3次执行!
后台线程第3次执行!
线程1第4次执行!
后台线程第4次执行!
后台线程第5次执行!
后台线程第6次执行!
后台线程第7次执行!
f、同步方法
- 把竞争访问的资源标识为private;
- 同步哪些修改变量的代码,使用synchronized关键字同步方法或代码。当然这不是唯一控制并发安全的途径。
package MultiThread; /**
* 此处模拟的为信用卡账户
* */
public class PutTakeMoney { /**
* @author donghe
*
*/
static class Account{
private String Id;
private int remain;
public Account(){ }
public Account(String id){
this.setId(id);
this.remain=100;
}
public Account(String id, int initRemain ){
this.setId(id);
this.remain=initRemain;
} public int getRemain(){
return this.remain;
}
public void setRemain(int r){
this.remain=r;
} /**
* 存款的方法
*
*/
public synchronized void putMoney(int money){
try{
Thread.sleep(10L);
this.remain+=money;
System.out.println(Thread.currentThread().getName()+"存款后,余额为:"+this.remain);
Thread.sleep(10L);
}catch(InterruptedException ine){
ine.printStackTrace();
}
} /**
* 取款的方法
*
*/
public synchronized void takeMoney(int money){
try{
Thread.sleep(10L);
this.remain-=money;
System.out.println(Thread.currentThread().getName()+"取款后,余额为:"+this.remain);
Thread.sleep(10L);
}catch(InterruptedException ine){
ine.printStackTrace();
}
}
public String getId() {
return Id;
}
public void setId(String id) {
Id = id;
}
} static class putThread implements Runnable{
private Account acc;
private int put; public putThread(Account acc,int money){
this.acc=acc;
this.put=money; }
public void run(){
acc.putMoney(put);
}
} static class takeThread extends Thread{
private Account acc;
private int take;
public takeThread(String threadName,Account acc, int money){
super(threadName);
this.acc=acc;
this.take=money; }
public void run(){
acc.takeMoney(take);
}
} public static void main(String[] args){
final Account acc=new Account("刘德华");
Thread thread1=new Thread(new putThread(acc, 30), "线程A");
Thread thread2=new takeThread("线程B", acc, 50);
Thread thread3=new Thread(new putThread(acc, 60), "线程C");
Thread thread4=new takeThread("线程D", acc, 70);
Thread thread5=new takeThread("线程E", acc, 20);
Thread thread6=new Thread(new putThread(acc, 10), "线程F");
Thread thread7=new Thread(new putThread(acc, 30), "线程G");
thread1.start();
thread2.start();
thread3.start();
thread4.start();
thread5.start();
thread6.start();
thread7.start(); } }
运行后的结果:
线程A存款后,余额为:
线程G存款后,余额为:
线程F存款后,余额为:
线程E取款后,余额为:
线程D取款后,余额为:
线程C存款后,余额为:
线程B取款后,余额为:
反面教材,不同步的情况,也就是去掉putMoney(int money)和takeMoney(int money)方法的synchronized修饰符,然后运行程序,结果如下:
线程B取款后,余额为:
线程E取款后,余额为:-
线程F存款后,余额为:
线程A存款后,余额为:
线程D取款后,余额为:
线程G存款后,余额为:
线程C存款后,余额为:
void notify()
唤醒在此对象监视器上等待的单个线程。
void notifyAll()
唤醒在此对象监视器上等待的所有线程。
void wait()
导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。
void wait(long timeout)
导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量。
void wait(long timeout, int nanos)
导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量。
结合以上方法,处理多线程同步与互斥问题非常重要,著名的生产者-消费者例子就是一个经典的例子,任何语言多线程必学的例子。
g、同步块
static class Account{
private String Id;
private int remain;
public Account(){ }
public Account(String id){
this.setId(id);
this.remain=100;
}
public Account(String id, int initRemain ){
this.setId(id);
this.remain=initRemain;
} public int getRemain(){
return this.remain;
}
public void setRemain(int r){
this.remain=r;
} /**
* 存款的方法
*
*/
public void putMoney(int money){
try{
Thread.sleep(10L);
synchronized (this) {
this.remain+=money;
System.out.println(Thread.currentThread().getName()+"存款后,余额为:"+this.remain);
}
Thread.sleep(10L);
}catch(InterruptedException ine){
ine.printStackTrace();
}
} /**
* 取款的方法
*
*/
public void takeMoney(int money){
try{
Thread.sleep(10L);
synchronized(this){
this.remain-=money;
System.out.println(Thread.currentThread().getName()+"取款后,余额为:"+this.remain);
}
Thread.sleep(10L);
}catch(InterruptedException ine){
ine.printStackTrace();
}
}
public String getId() {
return Id;
}
public void setId(String id) {
Id = id;
}
}
三、并发协作
a、经典的生产者消费者模型
- 生产者仅仅在仓储未满时候生产,仓满则停止生产。
- 消费者仅仅在仓储有产品时候才能消费,仓空则等待。
- 当消费者发现仓储没产品可消费时候会通知生产者生产。
- 生产者在生产出可消费产品时候,应该通知等待的消费者去消费。
package MultiThread; /**
* 此处模拟的为信用卡账户
* */
public class ProducerConsumerModel2 { /**
* 并发协作:生产消费者模型
* @author donghe
*
*/
static class Warehouse{
private int max_size=100; //最大库存量
private int currentNum; //当前库存数
public Warehouse(){ }
public Warehouse(int capcity){
this.currentNum=capcity;
} public int getMax_size() {
return max_size;
}
public void setMax_size(int max_size) {
this.max_size = max_size;
}
public int getCurrentNum() {
return currentNum;
}
public void setCurrentNum(int currentNum) {
this.currentNum = currentNum;
}
/**
* 生产指定数量的产品
*
*/
public synchronized void produce(int needNum){
while(this.currentNum+needNum>this.max_size){
System.out.println("要生产的产品数量"+needNum+"超过仓库剩余库存容量"+(this.max_size-this.currentNum)+",生产任务暂停执行!"); try {
//当然生产线程等待
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//满足生产条件,则进行生产,这里仅仅更改了仓库中的库存量
this.currentNum+=needNum;
System.out.println("生产了"+needNum+"个产品,生产后当前仓库产品总数为"+this.currentNum);
//唤醒在此对象监视器上等待的所有线程
this.notifyAll();
} /**
* 消费执行数量的产品
*
*/
public synchronized void consume(int needNum){ while(this.currentNum<needNum){
System.out.println("要消费的产品数量"+needNum+"超过仓库当前总的产品数"+this.currentNum+",消费任务暂停执行!");
//当然生产线程等待
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} //满足生产条件,则进行生产,这里仅仅更改了仓库中的库存量
this.currentNum-=needNum;
System.out.println("消费了"+needNum+"个产品,消费后当前仓库的产品总数为"+this.currentNum);
//唤醒在此对象监视器上等待的所有线程
this.notifyAll();
} }
static class Producer extends Thread{
private Warehouse wh;
private int put; public Producer(String threadName,Warehouse warehouse,int needNum){
super(threadName);
this.wh=warehouse;
this.put=needNum; }
public void run(){
wh.produce(put);
}
} static class Consumer extends Thread{
private Warehouse wh;
private int take;
public Consumer(String threadName,Warehouse warehouse, int needNum){
super(threadName);
this.wh=warehouse;
this.take=needNum; }
public void run(){
wh.consume(take);
}
} public static void main(String[] args){
//仓库中的初始产品数
final Warehouse wh=new Warehouse(50);
Thread p1=new Producer("生产线程1", wh, 60);
Thread c1=new Consumer("消费线程1", wh, 20);
Thread c2=new Consumer("消费线程2", wh, 80);
Thread c3=new Consumer("消费线程3", wh, 60);
Thread p2=new Producer("生产线程2", wh, 30);
Thread p3=new Producer("生产线程3", wh, 10);
Thread p4=new Producer("生产线程4", wh, 50); p1.start();
c1.start();
c2.start();
c3.start();
p2.start();
p3.start();
p4.start(); } }
运行结果:
要消费的产品数量80超过仓库当前总的产品数50,消费任务暂停执行!
生产了50个产品,生产后当前仓库产品总数为100
消费了80个产品,消费后当前仓库的产品总数为20
要消费的产品数量60超过仓库当前总的产品数20,消费任务暂停执行!
生产了60个产品,生产后当前仓库产品总数为80
消费了60个产品,消费后当前仓库的产品总数为20
消费了20个产品,消费后当前仓库的产品总数为0
生产了30个产品,生产后当前仓库产品总数为30
生产了10个产品,生产后当前仓库产品总数为40

b、死锁
package MultiThread; /**
* 并发协作-死锁
*
*
*/
public class Test {
public static void main(String[] args) {
DeadlockRisk dead = new DeadlockRisk();
MyThread t1 = new MyThread(dead, 1, 2);
MyThread t2 = new MyThread(dead, 3, 4);
MyThread t3 = new MyThread(dead, 5, 6);
MyThread t4 = new MyThread(dead, 7, 8); t1.start();
t2.start();
t3.start();
t4.start();
} } class MyThread extends Thread {
private DeadlockRisk dead;
private int a, b; MyThread(DeadlockRisk dead, int a, int b) {
this.dead = dead;
this.a = a;
this.b = b;
} @Override
public void run() {
dead.read();
dead.write(a, b);
}
} class DeadlockRisk {
private static class Resource {
public int value; } private Resource resourceA = new Resource();
private Resource resourceB = new Resource(); public int read() {
synchronized (resourceA) {
System.out.println("read():" + Thread.currentThread().getName() + "获取了resourceA的锁!");
synchronized (resourceB) {
System.out.println("read():" + Thread.currentThread().getName() + "获取了resourceB的锁!");
System.out.println("读成功! resourceA.value="+resourceA.value+"\tresourceB.value="+resourceB.value);
return resourceB.value + resourceA.value;
}
}
} public void write(int a, int b) {
synchronized (resourceB) {
System.out.println("write():" + Thread.currentThread().getName() + "获取了resourceB的锁!");
synchronized (resourceA) {
System.out.println("write():" + Thread.currentThread().getName() + "获取了resourceA的锁!");
resourceA.value = a;
resourceB.value = b;
System.out.println("写成功!");
}
}
}
}
运行结果为:
由运行结果可以看出,写线程Thread-0获得resourceB的锁,读线程获得了Thread-2的锁,两者都因无法获得被对方占有,而不释放的锁,使程序运行进入死锁,运行到图中某处停止。
c、volatile关键字
synchronized
”;与 synchronized
块相比,volatile 变量所需的编码较少,并且运行时开销也较少,但是它所能实现的功能也仅是 synchronized
的一部分。Java多线程编程——进阶篇二的更多相关文章
- Java多线程编程——进阶篇一
一.线程栈模型与线程的变量 要理解线程调度的原理,以及线程执行过程,必须理解线程栈模型. 线程栈是指某一时刻内存中线程调度的栈信息,当前调用的方法总是位于栈顶.线程栈的内容是随着程序的运行动态变化的, ...
- java多线程编程--基础篇
一.基本概念 a.操作系统中进程与线程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间, 一个进程中可以启动 ...
- java多线程编程核心技术(二)--对象及变量的并发访问
1.方法内部的私有变量是线程安全的,实例变量是线程不安全的. 2.A线程先持有object对象的lock锁,B线程可以以异步的方式调用object对象中的非synchronized类型的方法. 3.A ...
- Java多线程编程实战指南(核心篇)读书笔记(二)
(尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76651408冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...
- Java多线程编程实战指南(核心篇)读书笔记(五)
(尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76730459冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...
- Java多线程编程实战指南(核心篇)读书笔记(四)
(尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76690961冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...
- Java多线程编程实战指南(核心篇)读书笔记(三)
(尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76686044冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...
- Java多线程编程实战指南(核心篇)读书笔记(一)
(尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76422930冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...
- 《Java多线程编程实战指南(核心篇)》阅读笔记
<Java多线程编程实战指南(核心篇)>阅读笔记 */--> <Java多线程编程实战指南(核心篇)>阅读笔记 Table of Contents 1. 线程概念 1.1 ...
随机推荐
- row_number() over order by与利用rownum查询分页效率分析
实际测试: 数据库:70万条数据 查询第10000页,每页10条.row_number() 耗时: 2.2秒rownum 耗时:1.3秒 查询第20000页,每页10条.row_number() 耗时 ...
- mvn install
mvn install:install-file \ -DgroupId=com.weibo.datasys.weistreamng \ -DartifactId=weistreamng-092-fr ...
- JSP + AJAX完整实例及代码
(1)发送请求index.jsp,注意引入jquery.min.js文件 <%@ page language="java" contentType="text/ht ...
- ie上 CSS3114: @font-face 未能完成 OpenType 嵌入权限检查。(包括图标无法显示)
转自:http://blog.csdn.net/shore_w/article/details/8976188 @font-face是CSS3中的一个模块, 它主要是把自己定义的Web字体嵌入到网页中 ...
- tomcat重启session不过期的处理
tomcat关闭后重启,SESSION 仍然有效的处理方法很多时候明明在服务器上关闭了TOMCAT,但是客户端的表示 SESSION仍然在线,原因及解决方案如下: 每当正常关闭或者重启tomcat服务 ...
- js官网判断是否手机跳转到手机页面
<script src="http://siteapp.baidu.com/static/webappservice/uaredirect.js" type="te ...
- 【转】DNS记录类型介绍(A记录、MX记录、NS记录等)
DNS A记录 NS记录 MX记录 CNAME记录 TXT记录 TTL值 PTR值 建站名词解释:DNS A记录 NS记录 MX记录 CNAME记录 TXT记录 TTL值 PTR值 泛域名 泛解析 域 ...
- python - socket - server
网络上关于socket的介绍文章数不胜数.自己记录下学习的点点滴滴.以供将来复习学习使用. socket中文的翻译是套接字,总感觉词不达意.简单的理解就是ip+port形成的一个管理单元.也是程序中应 ...
- 20145218&20145240 《信息安全系统设计基础》实验一 开发环境的熟悉
20145218&20145240 <信息安全系统设计基础>实验一 开发环境的熟悉 课程:信息安全系统设计基础 班级:1452 姓名:(按贡献大小排名)刘士嘉 张晓涵 学号:(按贡 ...
- P1038 神经网络
#include <bits/stdc++.h> using namespace std; const int maxn = 105; struct node { int situatio ...