线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏。
1、方法内的变量为线程安全
“非线程安全”问题存在于“实例变量”中,如果是方法内部的私有变量,则不存在“非线程安全”问题(这是方法内部的变量是私有的特性造成的,所得结果也就是“线程安全”的了。
2、实例变量非线程安全
如果多个线程共同访问1个对象中的实例变量,则可能出现”非线程安全“问题。
如果对象仅有1个实例变量,则有可能出现覆盖的情况。
- public class HasSelfPrivateNum {
- private int num = 0;
- public synchronized void addI(String username) {
- try {
- if (username.equals(“a”)) {
- num = 100;
- System.out.println(”a set over!”);
- Thread.sleep(2000);
- } else {
- num = 200;
- System.out.println(”b set over!”);
- }
- System.out.println(username + ” num=” + num);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
public class HasSelfPrivateNum {
private int num = 0;
public synchronized void addI(String username) {
try {
if (username.equals("a")) {
num = 100;
System.out.println("a set over!");
Thread.sleep(2000);
} else {
num = 200;
System.out.println("b set over!");
}
System.out.println(username + " num=" + num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- public class MyThread1 extends Thread {
- private HasSelfPrivateNum numRef;
- public MyThread1(HasSelfPrivateNum numRef) {
- super();
- this.numRef = numRef;
- }
- public void run() {
- numRef.addI(”a”);
- }
- }
public class MyThread1 extends Thread {
private HasSelfPrivateNum numRef;
public MyThread1(HasSelfPrivateNum numRef) {
super();
this.numRef = numRef;
}
public void run() {
numRef.addI("a");
}
}
- public class MyThread2 extends Thread{
- private HasSelfPrivateNum numRef;
- public MyThread2 (HasSelfPrivateNum numRef) {
- super();
- this.numRef = numRef;
- }
- public void run() {
- numRef.addI(”b”);
- }
- }
public class MyThread2 extends Thread{
private HasSelfPrivateNum numRef;
public MyThread2 (HasSelfPrivateNum numRef) {
super();
this.numRef = numRef;
}
public void run() {
numRef.addI("b");
}
}
- //内部类
- class Test {
- public static void main(String[] args) {
- HasSelfPrivateNum numRef = new HasSelfPrivateNum();//只创建一个对象
- MyThread1 myThread1 = new MyThread1(numRef);
- myThread1.start();
- MyThread2 myThread2 = new MyThread2(numRef);
- myThread2.start();
- }
- }
//内部类
class Test {
public static void main(String[] args) {
HasSelfPrivateNum numRef = new HasSelfPrivateNum();//只创建一个对象
MyThread1 myThread1 = new MyThread1(numRef);
myThread1.start();
MyThread2 myThread2 = new MyThread2(numRef);
myThread2.start();
}
}
b set over! b num=200 a set over! a num=100 当然结果也有可能是先输出b,再输出a,线程有随机性 |
a set over! b set over! b num=200 a num=200 |
3、多个对象多个锁
上面的HasSelfPrivateNum类,MyThread1,MyThread2不变,只修改main方法代码如下
- //内部类,这个方法里创建了两个HasSelfPrivateNum对象
- class Test {
- public static void main(String[] args) {
- HasSelfPrivateNum numRef1 = new HasSelfPrivateNum();
- HasSelfPrivateNum numRef2 = new HasSelfPrivateNum();
- MyThread1 aThread1 = new MyThread1(numRef1);
- aThread1.start();
- MyThread2 bThread2 = new MyThread2(numRef2);
- bThread2.start();
- }
- }
//内部类,这个方法里创建了两个HasSelfPrivateNum对象
class Test {
public static void main(String[] args) {
HasSelfPrivateNum numRef1 = new HasSelfPrivateNum();
HasSelfPrivateNum numRef2 = new HasSelfPrivateNum();
MyThread1 aThread1 = new MyThread1(numRef1);
aThread1.start();
MyThread2 bThread2 = new MyThread2(numRef2);
bThread2.start();
}
}
a set over! b set over! b num=200 a num=100 |
从上面程序运行结果来看,虽然在Has SelfPrivateNumJava中使用了synchronized关键字,但打印的顺序却不是同步的,是交叉的。为什么是这样的结果呢?
关键字synchronized取得的锁都是对象锁,而不是把一段代码或方法(函数)当作锁,所以在上面的示例中,哪个线程先执行带synchronized关键字的方法,哪个线程就持有该方法所属对象的锁Lock,那么其他线程只能呈等待状态,前提是多个线程访问的是同一个对象。
但如果多个线程访间多个对象,则JVM会创建多个锁。上面的示例就是创建了2个HasSelfPrivateNumjava类的对象,所以就会产生出2个锁。
同步的单词为synchronized,异步的单词为asynchronized。
结论:调用用关键字synchronized声明的方法一定是排队运行的。另外需要牢牢记住“共享”这两个字,只有共享资源的读写访问才需要同步化,如果不是共享资源,那么根本就没有同步的必要。
—————————————————————————————————————————–
4、脏读
虽然在赋值时进行了同步,但在取值时有可能出现一些意想不到的意外,这种情况就是脏读(dirtyRead)。发生脏读的情况是在读取实例变量时,此值已经被其他线程更改过了。
- public class PublicVar {
- public String username = “username”;
- public String password = “password”;
- public synchronized void setValue(String username, String password) {
- try {
- this.username = username;
- Thread.sleep(3000);
- this.password = password;
- System.out.println(”setValue method thread name=” + Thread.currentThread().getName()
- + ”, username=” +username + “, password=”+password);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- public void getValue() { //如果加synchronized,就不会出现脏读
- System.out.println(”getValue method thread name=” + Thread.currentThread().getName()
- + ”, username=” +username + “, password=”+password);
- }
- }
public class PublicVar {
public String username = "username";
public String password = "password";
public synchronized void setValue(String username, String password) {
try {
this.username = username;
Thread.sleep(3000);
this.password = password;
System.out.println("setValue method thread name=" + Thread.currentThread().getName()
+ ", username=" +username + ", password="+password);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void getValue() { //如果加synchronized,就不会出现脏读
System.out.println("getValue method thread name=" + Thread.currentThread().getName()
+ ", username=" +username + ", password="+password);
}
}
- public class MyThread1 extends Thread {
- private PublicVar publicVar;
- public MyThread1(PublicVar publicVar) {
- super();
- this.publicVar = publicVar;
- }
- public void run() {
- publicVar.setValue(”username2”, “password2”);
- }
- }
public class MyThread1 extends Thread {
private PublicVar publicVar;
public MyThread1(PublicVar publicVar) {
super();
this.publicVar = publicVar;
}
public void run() {
publicVar.setValue("username2", "password2");
}
}
- class Test {
- public static void main(String[] args) throws InterruptedException {
- PublicVar publicVar = new PublicVar();
- MyThread1 myThread1 = new MyThread1(publicVar);
- myThread1.start();
- Thread.sleep(2000);
- publicVar.getValue();
- }
- }
class Test {
public static void main(String[] args) throws InterruptedException {
PublicVar publicVar = new PublicVar();
MyThread1 myThread1 = new MyThread1(publicVar);
myThread1.start();
Thread.sleep(2000);
publicVar.getValue();
}
}
getValue method thread name=main, username=username2, password=password setValue method thread name=Thread-0, username=username2, password=password2 |
出现脏读是因为public void getValue()方法并不是同步的,所以可以在任意时候进行调用。解决办法当然就是加上同步synchronized关键字
可见,方法setValue()和getValue()被依次执行。通过这个案例不仅要知道脏读是通过synchronized关键字解决的,还要知道如下内容:
当A线程调用anyObject对象加入synchronized关键字的X方法时,A线程就获得了X方法锁,更准确地讲,是获得了时象的锁,所以其他线程必须等A线程执行完毕才可以调用X方法,但B线程可以随意调用其他的非synchronized同步方法。
当A线程调用anyObject对象加入synchronized关健字的X方法时,A线程就获得了X方法所在对象的锁,所以其他线程必须等A线程执行完毕才可以调用X方法,而B线程如果调用声明了synchronized关键字的非X方法时,必须等A线程将X方法执行完,也就是释放对象锁后才可以调用。这时A线程已经执行了一个完整的任务,也就是说usernarne和password这两个实例变量已经同时被赋值,不存在脏读的基本环境。
脏读一定会出现操作实例变量的情况下,这就是不同线程“争抢”实例变量的结果。
—————————————————————————————————————–
5、synchronized锁重入
关键字synchronized拥有锁重入的功能。这也证明在一个synchronized方法/块的内部调用本类的其他synchronized方法/块时,是永远可以得到锁的。
“可重入锁”的概念是:自己可以再次获取自己的内部锁。比如有1条线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的,如果不可锁重入的话,就会造成死锁。
可重入锁也支持在父子类继承的环境中。
- public class Service {
- public synchronized void service1() {
- System.out.println(”service1”);
- service2();
- }
- public synchronized void service2() {
- System.out.println(”service2”);
- service3();
- }
- public synchronized void service3() {
- System.out.println(”service3”);
- }
- }
public class Service {
public synchronized void service1() {
System.out.println("service1");
service2();
}
public synchronized void service2() {
System.out.println("service2");
service3();
}
public synchronized void service3() {
System.out.println("service3");
}
}
- public class MyThread extends Thread {
- public void run() {
- Service service = new Service();
- service.service1();
- }
- }
public class MyThread extends Thread {
public void run() {
Service service = new Service();
service.service1();
}
}
- class Test {
- public static void main(String[] args) throws InterruptedException {
- MyThread myThread = new MyThread();
- myThread.start();
- }
- }
class Test {
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread();
myThread.start();
}
}
service1 service2 service3 |
—————————————————————————————————-
6、出现异常,锁自动释放
当一个线程执行的代码出现异常时,其所持有的锁会自动释放。
- public class Service {
- public synchronized void testMethod() {
- if (Thread.currentThread().getName().equals(“a”)) {
- System.out.println(”ThreadName=” + Thread.currentThread().getName()
- +”, run beginTime=” + System.currentTimeMillis());
- int i = 1;
- while (i == 1) {
- if ((“” + Math.random()).substring(0, 8).equals(“0.123456”)) {
- System.out.println(”ThreadName=” + Thread.currentThread().getName()
- +”, run beginTime=” + System.currentTimeMillis());
- Integer.parseInt(”a”);
- } else {
- System.out.println(”Thread B run Time=” + System.currentTimeMillis());
- }
- }
- }
- }
- }
public class Service {
public synchronized void testMethod() {
if (Thread.currentThread().getName().equals("a")) {
System.out.println("ThreadName=" + Thread.currentThread().getName()
+", run beginTime=" + System.currentTimeMillis());
int i = 1;
while (i == 1) {
if (("" + Math.random()).substring(0, 8).equals("0.123456")) {
System.out.println("ThreadName=" + Thread.currentThread().getName()
+", run beginTime=" + System.currentTimeMillis());
Integer.parseInt("a");
} else {
System.out.println("Thread B run Time=" + System.currentTimeMillis());
}
}
}
}
}
- public class MyThread1 extends Thread {
- private Service service;
- public MyThread1(Service service) {
- super();
- this.service = service;
- }
- public void run() {
- service.testMethod();
- }
- }
public class MyThread1 extends Thread {
private Service service;
public MyThread1(Service service) {
super();
this.service = service;
}
public void run() {
service.testMethod();
}
}
- public class MyThread2 extends Thread{
- private Service service;
- public MyThread2(Service service) {
- super();
- this.service = service;
- }
- public void run() {
- service.testMethod();
- }
- }
public class MyThread2 extends Thread{
private Service service;
public MyThread2(Service service) {
super();
this.service = service;
}
public void run() {
service.testMethod();
}
}
- //内部类
- class Test {
- public static void main(String[] args) {
- try {
- Service service = new Service();
- MyThread1 aThread = new MyThread1(service);
- aThread.setName(”a”);
- aThread.start();
- Thread.sleep(1000);
- MyThread2 bThread2 = new MyThread2(service);
- bThread2.setName(”b”);
- bThread2.start();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
//内部类
class Test {
public static void main(String[] args) {
try {
Service service = new Service();
MyThread1 aThread = new MyThread1(service);
aThread.setName("a");
aThread.start();
Thread.sleep(1000);
MyThread2 bThread2 = new MyThread2(service);
bThread2.setName("b");
bThread2.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Thread B run Time=1462333804804 Thread B run Time=1462333804804 Thread B run Time=1462333804804 ThreadName=a, run beginTime=1462333804804 Exception in thread “a” java.lang.NumberFormatException: For input string: “a” at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) |
7、同步不具有继承性