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 ...
随机推荐
- Javascript 异步编程的4种方法
你可能知道,Javascript语言的执行环境是"单线程"(single thread). 所谓"单线程",就是指一次只能完成一件任务.如果有多个任务,就必须排 ...
- 利用iframe将div放置在object的上面
这两天在写一个用object上传照片的网页,在上传时,需要弹出一个进度条的div,我就按照正常的思维模式,将进度条div设为绝对定位,将其z-index设置的很大,结果,进度条div被object给遮 ...
- javascript使用两个逻辑非运算符(!!)的原因
javascript使用两个逻辑非运算符(!!)的原因: 在有些代码中可能大家可能会注意到有些地方使用了两个逻辑非运算符,第一感觉就是没有必要,比如操作数是true的话,使用两个逻辑非的返回值还是tr ...
- 关于GreenOdoo的一个Bug
动态创建字段的时候,虽然字段已经创建,但是显示的时候会报没有新创建的字段错误: 但是原版是没有任何问题的,记录一下.
- mysql字符乱码
解决mysql字符乱码思路: mysql服务器字符集 mysql客户端字符集 系统字符集 生产环境改字符集: 1.导出表结构到 scam.sql文件中 2.更改scam.sql文件中的字符集为想要的字 ...
- JAVA生成二维码图片代码
首先需要导入 QRCode.jar 包 下载地址看这里 http://pan.baidu.com/s/1o6qRFqM import java.awt.Color;import java.awt. ...
- 在windows7下安装CentOS
需要用到的软件 EasyBCD 设置索引菜单 PA5.2_Portable 分区助手 WinGrub 查看硬盘代号 1.使用分区助手,腾出至少4GB的空间,并格式化为fat32格式,将CentOS的I ...
- Win8.1屏幕亮度自动调节关闭方法
细心的朋友会发现,Win8.1系统的笔记本屏幕亮度有时候,会根据外界光线亮度以及温度自动调节屏幕亮度,尽管看似比较智能,但有时候我们并不希望笔记本屏幕亮度受光线影响,忽暗忽亮.如果我们希望Win8笔记 ...
- 数据库---T-SQL语句提纲
T-SQL语句: 创建表:create table Name(Code varchar(50)) 主键:primary key自增长列:auto_increment外键关系:references非空: ...
- LaTex 数学公式
\usepackage{amsmath} 常用宏包 \usepackage{arydshln} 此宏包带虚线 $ $ 行内公式 $$ $$ 行间公式 \[ \] 行间公式 \numberwithin{ ...