Java多线程编程核心技术---对象及变量的并发访问(二)
数据类型String的常量池特性
在JVM中具有String常量池缓存的功能。
public class Service {
public static void print(String str){
try {
synchronized (str) {
while (true) {
System.out.println(Thread.currentThread().getName());
Thread.sleep(500);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class ThreadA extends Thread {
@Override
public void run() {
Service.print("AA");
}
}
public class ThreadB extends Thread {
@Override
public void run() {
Service.print("AA");
}
}
public class Test {
public static void main(String[] args) {
ThreadA a = new ThreadA();
a.setName("A");
ThreadB b = new ThreadB();
b.setName("B");
a.start();
b.start();
}
}
控制台打印结果如下:
...
A
A
A
A
A
A
...
出现这种情况就是因为Sting的两个值都是AA,两个线程持有相同的锁,所以造成线程B不能执行。因此在大多数情况下,同步synchronized代码块都不实用String作为锁对象,而改用其他,比如new Object()实例化一个Object对象,但他并不放入缓存中。
同步synchronized方法无限等待与解决
public class Service {
synchronized public void methodA(){
System.out.println("methodA begin...");
boolean condition = true;
while (condition) {
}
System.out.println("methodA end...");
}
synchronized public void methodB(){
System.out.println("methodB begin...");
System.out.println("methodB end...");
}
}
public class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.methodA();
}
}
public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.methodB();
}
}
public class Run {
public static void main(String[] args) {
Service service = new Service();
ThreadA a = new ThreadA(service);
a.setName("A");
ThreadB b = new ThreadB(service);
b.setName("B");
a.start();
b.start();
}
}
控制台打印结果如下:
methodA begin...
线程A处于死循环状态,线程B永远无法拿到Service对象锁而一直得不到运行。
对Service对象做如下修改:
public class Service {
Object object1 = new Object();
Object object2 = new Object();
public void methodA() {
synchronized (object1) {
System.out.println("methodA begin...");
boolean condition = true;
while (condition) {
}
System.out.println("methodA end...");
}
}
public void methodB() {
synchronized (object2) {
System.out.println("methodB begin...");
System.out.println("methodB end...");
}
}
}
此时控制台打印结果如下:
methodA begin...
methodB begin...
methodB end...
methodA()和methodB()对不同的对象加锁,所以线程A持有的锁不会对线程B造成影响。
多线程的死锁
Java线程死锁是一个经典的多线程问题,因为不同的线程都在等待根本不可能被释放的锁,从而导致所有的任务都无法继续完成。
public class DeadThread implements Runnable {
public String username;
public Object lock1 = new Object();
public Object lock2 = new Object();
public void setFlag(String username) {
this.username = username;
}
@Override
public void run() {
if (username.equals("a")) {
synchronized (lock1) {
try {
System.out.println("username=" + username);
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (lock2) {
System.out.println("按lock1->lock2代码顺序执行了");
}
}
}
if (username.equals("b")) {
synchronized (lock2) {
try {
System.out.println("username=" + username);
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (lock1) {
System.out.println("按lock2->lock1代码顺序执行了");
}
}
}
}
public static void main(String[] args) {
try {
DeadThread t1 = new DeadThread();
t1.setFlag("a");
Thread thread1 = new Thread(t1);
thread1.start();
Thread.sleep(200);
t1.setFlag("b");
Thread thread2 = new Thread(t1);
thread2.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
控制台打印结果如下:
username=a
username=b
此时程序不结束,处于死锁状态。
可以使用jps命令查看当前线程的id,然后使用jstack -l id来检查是否存在死锁。
内置类与静态内置类
//内置类
public class PublicClass {
private String username;
private String password;
class PrivateClass{
private String age;
private String address;
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public void printPublicProperty() {
System.out.println(username + "-" + password);
}
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public static void main(String[] args) {
PublicClass publicClass = new PublicClass();
publicClass.setUsername("admin");
publicClass.setPassword("123456");
System.out.println(publicClass.getUsername() + "-" + publicClass.getPassword());
PrivateClass privateClass = publicClass.new PrivateClass();
privateClass.setAddress("shanghai");
privateClass.setAge("25");
System.out.println(privateClass.getAddress() + "-" + privateClass.getAge());
privateClass.printPublicProperty();
}
}
控制台打印结果如下:
admin-123456
shanghai-25
admin-123456
//静态内置类
public class PublicClass {
static String username;
static String password;
static class PrivateClass{
private String age;
private String address;
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public void printPublicProperty() {
System.out.println(username + "-" + password);
}
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public static void main(String[] args) {
PublicClass publicClass = new PublicClass();
publicClass.setUsername("admin");
publicClass.setPassword("123456");
System.out.println(publicClass.getUsername() + "-" + publicClass.getPassword());
PrivateClass privateClass = new PrivateClass();
privateClass.setAddress("shanghai");
privateClass.setAge("25");
System.out.println(privateClass.getAddress() + "-" + privateClass.getAge());
privateClass.printPublicProperty();
}
}
控制台打印结果同上。
内置类与同步-实验1
public class OutClass {
static class Inner{
public void method1() {
synchronized ("其他的锁") {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "i=" + i);
try {
Thread.sleep(100);
} catch (Exception e) {
}
}
}
}
public synchronized void method2() {
for (int i = 11; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + "i=" + i);
try {
Thread.sleep(100);
} catch (Exception e) {
}
}
}
}
public static void main(String[] args) {
final Inner inner = new Inner();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
inner.method1();
}
}, "A");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
inner.method2();
}
}, "B");
t1.start();
t2.start();
}
}
控制台打印结果如下:
Ai=0
Bi=11
Bi=12
Ai=1
Bi=13
Ai=2
Ai=3
Bi=14
Ai=4
Bi=15
Bi=16
Ai=5
Ai=6
Bi=17
Ai=7
Bi=18
Bi=19
Ai=8
Ai=9
由于持有不同的对象监视器,所以打印结果是乱序的。
内置类与同步-实验2
public class OutClass {
static class InnerClass1{
public void method1(InnerClass2 class2) {
String threadName = Thread.currentThread().getName();
synchronized (class2) {
System.out.println(threadName + "进入InnerClass1的method1方法");
for (int i = 0; i < 5; i++) {
System.out.println("i=" + i);
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println(threadName + "离开InnerClass1的method1方法");
}
}
public synchronized void method2() {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + "进入InnerClass1的method2方法");
for (int j = 0; j < 5; j++) {
System.out.println("j=" + j);
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println(threadName + "离开InnerClass1的method2方法");
}
}
static class InnerClass2{
public synchronized void method1() {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + "进入InnerClass2的method1方法");
for (int k = 0; k < 5; k++) {
System.out.println("k=" + k);
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println(threadName + "离开InnerClass2的method1方法");
}
}
public static void main(String[] args) {
final InnerClass1 class1 = new InnerClass1();
final InnerClass2 class2 = new InnerClass2();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
class1.method1(class2);
}
}, "T1");
Thread t2 = new Thread(new Runnable() {
public void run() {
class1.method2();
}
}, "T2");
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
class2.method1();
}
}, "T3");
t1.start();
t2.start();
t3.start();
}
}
控制台打印结果如下:
T2进入InnerClass1的method2方法
T1进入InnerClass1的method1方法
j=0
i=0
j=1
i=1
i=2
j=2
j=3
i=3
i=4
j=4
T2离开InnerClass1的method2方法
T1离开InnerClass1的method1方法
T3进入InnerClass2的method1方法
k=0
k=1
k=2
k=3
k=4
T3离开InnerClass2的method1方法
同步代码块synchronized (class2)对class2上锁后,其他线程只能以同步方式调用class2中的静态同步方法。
对象锁的改变
public class MyService {
private String lock = "123";
public void testMethod() {
try {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " begin " + System.currentTimeMillis());
lock = "456";
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + " end " + System.currentTimeMillis());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class ThreadA extends Thread {
private MyService service;
public ThreadA(MyService service) {
super();
this.service = service;
}
@Override
public void run() {
service.testMethod();
}
}
public class ThreadB extends Thread {
private MyService service;
public ThreadB(MyService service) {
super();
this.service = service;
}
@Override
public void run() {
service.testMethod();
}
}
public class Run1 {
public static void main(String[] args) throws InterruptedException {
MyService service = new MyService();
ThreadA a = new ThreadA(service);
a.setName("A");
ThreadB b = new ThreadB(service);
b.setName("B");
a.start();
Thread.sleep(100);
b.start();
}
}
k控制台打印结果如下:
A begin 1465980925627
B begin 1465980925727
A end 1465980927627
B end 1465980927727
从打印结果看,A线程和B线程是以异步方式执行的,可见A线程与B线程持有的锁不同。
将以上main方法中的代码做如下修改:
public class Run1 {
public static void main(String[] args) throws InterruptedException {
MyService service = new MyService();
ThreadA a = new ThreadA(service);
a.setName("A");
ThreadB b = new ThreadB(service);
b.setName("B");
a.start();
//Thread.sleep(100);
b.start();
}
}
此时打印结果如下:
A begin 1465981162126
A end 1465981164127
B begin 1465981164128
B end 1465981166128
可见此时A线程和B线程是以同步方式执行的,A线程和B线程共同争抢的锁是“123”。
PS:只要对象不变,即使对象的属性改变,运行的结果还是同步的。将以上代码做如下修改:
public class MyService {
private StringBuilder lock = new StringBuilder("123");
public void testMethod() {
try {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " begin " + System.currentTimeMillis());
lock.append("456");
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + " end " + System.currentTimeMillis());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class Run1 {
public static void main(String[] args) throws InterruptedException {
MyService service = new MyService();
ThreadA a = new ThreadA(service);
a.setName("A");
ThreadB b = new ThreadB(service);
b.setName("B");
a.start();
Thread.sleep(100);
b.start();
}
}
此时控制台打印结果如下:
A begin 1465981411980
A end 1465981413980
B begin 1465981413980
B end 1465981415981
可见线程A和线程B是以同步方式执行的。
volatile关键字
关键字volatile与死循环
死循环例子
public class PrintString {
private boolean isContinuePrint = true;
public boolean isContinuePrint() {
return isContinuePrint;
}
public void setContinuePrint(boolean isContinuePrint) {
this.isContinuePrint = isContinuePrint;
}
public void printStringMethod() {
try {
while (isContinuePrint) {
System.out.println("printStringMethod is running...threadName=" + Thread.currentThread().getName());
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
PrintString printString = new PrintString();
printString.printStringMethod();
System.out.println("停止线程...");
printString.setContinuePrint(false);
}
}
控制台打印结果如下:
printStringMethod is running...threadName=main
printStringMethod is running...threadName=main
printStringMethod is running...threadName=main
printStringMethod is running...threadName=main
printStringMethod is running...threadName=main
......
main线程在printString.printStringMethod()中陷入了死循环,后面的printString.setContinuePrint(false)得不到执行。
解决同步死循环
修改上面的代码
public class PrintString implements Runnable {
private boolean isContinuePrint = true;
public boolean isContinuePrint() {
return isContinuePrint;
}
public void setContinuePrint(boolean isContinuePrint) {
this.isContinuePrint = isContinuePrint;
}
public void printStringMethod() {
try {
while (isContinuePrint) {
System.out.println("printStringMethod is running...threadName=" + Thread.currentThread().getName());
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run() {
printStringMethod();
}
public static void main(String[] args) throws InterruptedException {
PrintString printString = new PrintString();
new Thread(printString).start();
Thread.sleep(5000);
System.out.println("停止线程...");
printString.setContinuePrint(false);
}
}
此时控制台打印结果如下:
printStringMethod is running...threadName=Thread-0
printStringMethod is running...threadName=Thread-0
printStringMethod is running...threadName=Thread-0
printStringMethod is running...threadName=Thread-0
printStringMethod is running...threadName=Thread-0
停止线程...
此时main线程设置isContinuePrint=false,可以使另一个线程停止执行。
++注:《Java多线程编程核心技术》P120讲将上面的代码运行在-server服务器模式中的64bit的JVM上时,会出现死循环。实际测试并未出现死循环,暂未弄清原因。++
解决异步死循环
package com.umgsai.thread22;
public class RunThread extends Thread {
volatile private boolean isRunning = true;
public boolean isRunning() {
return isRunning;
}
public void setRunning(boolean isRunning) {
this.isRunning = isRunning;
}
@Override
public void run() {
System.out.println("进入run方法...");
while (isRunning) {
System.out.println("running....");
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("线程被停止...");
}
public static void main(String[] args) {
try {
RunThread runThread = new RunThread();
runThread.start();
Thread.sleep(1000);
runThread.setRunning(false);
System.out.println("已将isRunning设置为false");
} catch (Exception e) {
e.printStackTrace();
}
}
}
控制台打印结果如下:
进入run方法...
running....
running....
已将isRunning设置为false
线程被停止...
使用volatile关键字强制从公共内存中读取变量。
使用volatile关键字增加了实例变量在多个线程之间的可见性,但是volatile关键字不支持原子性。
synchronized和volatile的比较
- 关键字volatile是线程同步的轻量级实现,性能比synchronized好。volatile只能修饰变量,synchronized可以修饰方法和代码块。
- 多线程访问volatile不会发生阻塞,synchronized会出现阻塞。
- volatile能保证数据的可见性,但不能保证原子性。synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存和公共内存中的数据做同步。
- 关键字volatile解决的是变量在多个线程之间的可见性,synchronized解决的是多个线程之间访问资源的同步性。
volatile的非原子性
public class VolatileTest extends Thread {
volatile public static int count;
private static void addCount(){
for (int i = 0; i < 100; i++) {
count++;
}
System.out.println("count=" + count);
}
@Override
public void run() {
addCount();
}
public static void main(String[] args) {
VolatileTest[] volatileTests = new VolatileTest[100];
for (int i = 0; i < 100; i++) {
volatileTests[i] = new VolatileTest();
}
for (int i = 0; i < 100; i++) {
volatileTests[i].start();
}
}
}
控制台打印结果如下:
......
count=5332
count=5232
count=5132
count=5032
count=4932
count=4854
count=4732
count=4732
将以上代码中的addCount方法加上synchronized关键字
public class VolatileTest extends Thread {
volatile public static int count;
//一定要加static关键字,这样synchronized与static锁的内容就是VolatileTest类了,也就达到同步效果了。
synchronized private static void addCount(){
for (int i = 0; i < 100; i++) {
count++;
}
System.out.println("count=" + count);
}
@Override
public void run() {
addCount();
}
public static void main(String[] args) {
VolatileTest[] volatileTests = new VolatileTest[100];
for (int i = 0; i < 100; i++) {
volatileTests[i] = new VolatileTest();
}
for (int i = 0; i < 100; i++) {
volatileTests[i].start();
}
}
}
此时控制台打印结果如下:
......
count=9300
count=9400
count=9500
count=9600
count=9700
count=9800
count=9900
count=10000
使用原子类进行i++操作
i++操作除了使用synchronized关键字同步外,还可以使用AtomicInteger原子类实现。
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicIntegerTest extends Thread {
private AtomicInteger count = new AtomicInteger(0);
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(count.incrementAndGet());
}
}
public static void main(String[] args) {
AtomicIntegerTest atomicIntegerTest = new AtomicIntegerTest();
Thread t1 = new Thread(atomicIntegerTest);
Thread t2 = new Thread(atomicIntegerTest);
Thread t3 = new Thread(atomicIntegerTest);
Thread t4 = new Thread(atomicIntegerTest);
Thread t5 = new Thread(atomicIntegerTest);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
控制台打印结果如下:
......
4992
4993
4994
4995
4996
4997
4998
4999
5000
5个线程成功累加到5000。
原子类也并不完全安全
public class MyService {
public static AtomicLong atomicLong = new AtomicLong();
public void addNum() {
System.out.println(Thread.currentThread().getName() + " 加了100之后是:" + atomicLong.addAndGet(100));
atomicLong.addAndGet(1);
}
}
public class MyThread extends Thread {
private MyService myService;
public MyThread(MyService myService) {
this.myService = myService;
}
@Override
public void run() {
myService.addNum();
}
}
public class Run {
public static void main(String[] args) {
try {
MyService myService = new MyService();
MyThread[] array = new MyThread[100];
for (int i = 0; i < array.length; i++) {
array[i] = new MyThread(myService);
}
for (int i = 0; i < array.length; i++) {
array[i].start();;
}
Thread.sleep(1000);
System.out.println(myService.atomicLong.get());
} catch (Exception e) {
e.printStackTrace();
}
}
}
控制台打印结果如下:
......
Thread-89 加了100之后是:8987
Thread-96 加了100之后是:9493
Thread-80 加了100之后是:9594
Thread-94 加了100之后是:9695
Thread-97 加了100之后是:9796
Thread-95 加了100之后是:9896
Thread-98 加了100之后是:9998
Thread-99 加了100之后是:10098
10100
累加的结果是正确的,但是打印顺序的错的,这是因为虽然addAndGet方法是原子的,但是方法和方法之间的调用却不是原子的。
对以上代码做如下修改:
public class MyService {
public static AtomicLong atomicLong = new AtomicLong();
synchronized public void addNum() {
System.out.println(Thread.currentThread().getName() + " 加了100之后是:" + atomicLong.addAndGet(100));
atomicLong.addAndGet(1);
}
}
此时控制台打印结果如下:
......
Thread-86 加了100之后是:9392
Thread-87 加了100之后是:9493
Thread-88 加了100之后是:9594
Thread-92 加了100之后是:9695
Thread-93 加了100之后是:9796
Thread-94 加了100之后是:9897
Thread-95 加了100之后是:9998
Thread-98 加了100之后是:10099
10100
此时线程以同步方式执行addNum方法,每次先加100再加1.
synchronized代码块有volatile同步的功能
关键字synchronized可以使多个线程访问同一个资源具有同步性,而且还可以将线程工作内存中的私有变量与公共内存中的变量进行同步。
public class Service {
private boolean isContinueRun = true;
public void runMethod() {
while (isContinueRun) {
}
System.out.println("stop...");
}
public void stopMethod() {
isContinueRun = false;
}
}
public class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
this.service = service;
}
@Override
public void run() {
service.runMethod();
}
}
public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
this.service = service;
}
@Override
public void run() {
service.stopMethod();
}
}
public class Run {
public static void main(String[] args) {
try {
Service service = new Service();
ThreadA a = new ThreadA(service);
a.start();
Thread.sleep(1000);
ThreadB b = new ThreadB(service);
b.start();
System.out.println("已经发起停止命令了");
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行main,控制台打印结果如下:
已经发起停止命令了
出现死循环。
对Service类做如下修改:
public class Service {
private boolean isContinueRun = true;
public void runMethod() {
String anyString = new String();
while (isContinueRun) {
synchronized (anyString) {
}
}
System.out.println("stop...");
}
public void stopMethod() {
isContinueRun = false;
}
}
此时控制台打印结果如下:
已经发起停止命令了
stop...
关键字synchronized可以保证在同一时刻,只有一个线程可以执行某一个方法或某一个代码块。它包含两个特征:互斥性和可见性。同步synchronized不仅可以解决一个线程看到对象处于不一致的状态,还可以保证进入同步方法或者同步代码块的每个线程都看到由同一个锁保护之前所有的修改结果。
Java多线程编程核心技术---对象及变量的并发访问(二)的更多相关文章
- Java多线程编程核心技术---对象及变量的并发访问(一)
synchronized同步方法 "非线程安全"其实会在多个线程对同一个对象中的实例变量进行并发访问时发生,产生的后果就是"脏读",也就是渠道的数据其实是被更改 ...
- Java多线程编程核心 - 对象及变量的并发访问
1.什么是“线程安全”与“非线程安全”? “非线程安全”会在多个线程对同一对象总的实例变量进行并发访问时发生,产生的后果是“脏读”,也就是取到的数据其实是被更改过的. “线程安全”是以获得的实例变量的 ...
- Java多线程编程核心技术(二)对象及变量的并发访问
本文主要介绍Java多线程中的同步,也就是如何在Java语言中写出线程安全的程序,如何在Java语言中解决非线程安全的相关问题.阅读本文应该着重掌握如下技术点: synchronized对象监视器为O ...
- Java多线程编程核心技术-第2章-对象及变量的并发访问-读书笔记
第 2 章 对象及变量的并发访问 本章主要内容 synchronized 对象监视器为 Object 时的使用. synchronized 对象监视器为 Class 时的使用. 非线程安全是如何出现的 ...
- Java多线程编程核心技术---学习分享
继承Thread类实现多线程 public class MyThread extends Thread { @Override public void run() { super.run(); Sys ...
- Java多线程编程核心技术
Java多线程编程核心技术 这本书有利于对Java多线程API的理解,但不容易从中总结规律. JDK文档 1. Thread类 部分源码: public class Thread implements ...
- 《Java 多线程编程核心技术》- 笔记
作为业务开发人员,能够在工作中用到的技术其实不多.虽然平时老是说什么,多线程,并发,注入,攻击!但是在实际工作中,这些东西不见得用得上.因为,我们用的框架已经把这些事做掉了. 比如web开发,外面有大 ...
- Java多线程编程核心技术(三)多线程通信
线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体.线程间的通信就是成为整体的必用方案之一,可以说,使线程间进行通信后,系统之间的交互性会更强大,在大大提高CPU利用率的同时 ...
- Java多线程编程核心技术(一)Java多线程技能
1.进程和线程 一个程序就是一个进程,而一个程序中的多个任务则被称为线程. 进程是表示资源分配的基本单位,线程是进程中执行运算的最小单位,亦是调度运行的基本单位. 举个例子: 打开你的计算机上的任务管 ...
随机推荐
- 1.Android常见异常:android.view.WindowLeaked 分析以及解决办法
在项目中遇到WindowManager: Activity has leaked window问题,其实在stackoverflow.com可以找到详细答案:http://stackoverflow ...
- 67.Android中的数据存储总结
转载:http://mp.weixin.qq.com/s?__biz=MzIzMjE1Njg4Mw==&mid=2650117688&idx=1&sn=d6c73f9f04d0 ...
- [bzoj3670][2014湖北省队互测week2]似乎在梦中见过的样子
Description 已知一个字符串S,求它有多少个形如A+B+A的子串(len(A)>=k,len(B)>=1 ). Input 第一行一个字符串,第二行一个数 k. Output 仅 ...
- 【BZOJ-3626】LCA 树链剖分
3626: [LNOI2014]LCA Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1428 Solved: 526[Submit][Status ...
- SQL Server表结构和数据导入到MySQL
借助的工具:Navicat for MySQL,链接:http://pan.baidu.com/s/1kVCw8IF 密码:g927 可以很明确的肯定,主键和自增列是没办法导入的,只能是表结构和数据. ...
- ecshop /category.php SQL Injection Vul
catalog . 漏洞描述 . 漏洞触发条件 . 漏洞影响范围 . 漏洞代码分析 . 防御方法 . 攻防思考 1. 漏洞描述 Relevant Link: http://sebug.net/vuld ...
- 通过broadcastreceiver 监听短信问题
在mainfest中 订阅 短信到来的广播时候 发现找不到 <action android:name="android.provider.Telephony.SMS_RECEIV ...
- excel处理数字的时候避免自动转为double
帮同事解决了一个POI解析Excel的功能,就是他想读出单元格中的原始内容,但是poi在处理数字的时候会自动转换为double了,这样对于一些对1和1.00有严格区分的场景下,会出现问题.我看网上很多 ...
- sscanf()函数的使用及其实例
资料引自: 传送门 sscanf函数原型: Int sscanf( const char * src, const char * format, ...); int scanf( const char ...
- python deep copy and shallow copy
Python中对于对象的赋值都是引用,而不是拷贝对象(Assignment statements in Python do not copy objects, they create bindings ...