非线程安全问题
对于方法内部有
私有
变量的,则不存在非线程安全
问题。即线程是安全
的。
如果 多个线程共同访问1个对象,则有可能出现非线程安全
例如:
1 | public class Count { |
结果:1
2
3
4Thread-0 print
98
Thread-1 print
99
要使线程安全
加上Synchronized关键字上锁,锁的是对象 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public class ThreadA extends Thread {
private Count count;
public ThreadA(Count count) {
this.count=count;
}
public void run() {
synchronized (count) {
System.out.println(Thread.currentThread().getName() + " print ");
count.print();
}
}
}
结果:1
2
3
4Thread-1 print
99
Thread-0 print
98
脏读
多线程调用一个方法,避免出现线程不安全现象,加上了synchronized关键字,虽然赋值时进行了同步,但是有可能出现读取实例变量时,这个值已经被其他线程更改了,这就是脏读现象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 public class Student {
private String username="张三";
private String password="111";
synchronized public void setStudent(String username, String password) throws InterruptedException {
this.username = username;
Thread.sleep(2000);
this.password = password;
System.out.println("setStudent methods use"+Thread.currentThread().getName()+",print username="+username+",password="+password);
}
public void getStudent(){
System.out.println(Thread.currentThread().getName()+": username="+username+",password="+password);
}
public class ThreadB extends Thread {
private Student student;
public ThreadB(Student student) {
this.student = student;
}
public void run() {
try {
student.setStudent("李四","222");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public class TestThreadB {
public static void main(String[] args) throws InterruptedException {
Student student=new Student();
ThreadB thread=new ThreadB(student);
thread.start();
Thread.sleep(1000);
student.getStudent();
}
}
结果:1
2main: username=李四,password=111
setStudent methods useThread-0,print username=李四,password=222
脏读数据产生原因,getStudent方法并不同步的,所以可以在任意时候进行调用,解决办法就是在方法前加上synchronized,成为同步方法
改进
1
2
3
4 synchronized public void getStudent(){
System.out.println(Thread.currentThread().getName()+": username="+username+",password="+password);
}
}
结果:
setStudent methods useThread-0,print username=李四,password=222
main: username=李四,password=222
Synchronized 拥有锁重入的功能
可重入锁:自己可以再次获取自己的内部锁。
Synchronized同步代码块效率问题
当两个并发线程访问一个对象object中的synchronized代码块时,一段时间内只能执行一个线程,另一个线程进入等待,等前面这个线程完成,才能继续执行。
- 然后就有人提出run方法里,将一部分必要的上锁成为同步代码块,一个线程访问object的同步代码,另一个线程仍然可以访问它的非同步代码块。
但是最后发现访问同步代码块的要等待执行,非同步代码块的是交叉执行
sychronized同步方法和sychronized(this)同步代码块都满足以下两点
- 对其他sychronized同步方法或sychronized(this)同步代码块调用呈阻塞状态
- 同一时间只有一个线程可以执行sychronized同步方法中的代码