1. 方法内的变量不存在非线程安全问题,永远都是线程安全的,这是因为方法内部的变量具有私有特性,不需要使用同步。
2. 当两个线程同时访问同一个业务对象中的没有同步的方法,并且同时操作业务对象中的实例变量,则有可能出现非线程安全问题。
例子如下
public class SynchronizedTest2 {
public static void main(String[] args) {
HasSelfPrivateNum numRef = new HasSelfPrivateNum();
ThreadA aThread = new ThreadA(numRef);
aThread.start();
ThreadB bThread = new ThreadB(numRef);
bThread.start();
}
static class HasSelfPrivateNum{
private int num = 0;
public void addI(String name) {
try {
if("a".equals(name)) {
num = 100;
System.out.println("a set over!");
Thread.sleep(2000);
}else {
num = 200;
System.out.println("b set over!");
}
System.out.println(name + "num=" + num);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class ThreadA extends Thread{
private HasSelfPrivateNum numRef;
public ThreadA(HasSelfPrivateNum numRef) {
super();
this.numRef = numRef;
}
@Override
public void run() {
super.run();
numRef.addI("a");
}
}
static class ThreadB extends Thread{
private HasSelfPrivateNum numRef;
public ThreadB(HasSelfPrivateNum numRef) {
super();
this.numRef = numRef;
}
@Override
public void run() {
super.run();
numRef.addI("b");
}
}
}
运行结果为
a set over!
b set over!
bnum=200
anum=200
3. 如果业务对象和锁对象是属于一对一的关系,每个线程执行自己所属业务对象中的同步方法,不存在争抢关系,也是不需要锁的。
例子如下
public class SynchronizedTest3 {
public static void main(String[] args) {
HasSelfPrivateNum numRefA = new HasSelfPrivateNum();
HasSelfPrivateNum numRefB = new HasSelfPrivateNum();
ThreadA aThread = new ThreadA(numRefA);
aThread.start();
ThreadB bThread = new ThreadB(numRefB);
bThread.start();
}
static class HasSelfPrivateNum{
private int num = 0;
synchronized public void addI(String name) {
try {
if("a".equals(name)) {
num = 100;
System.out.println("a set over!");
Thread.sleep(2000);
}else {
num = 200;
System.out.println("b set over!");
}
System.out.println(name + "num=" + num);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class ThreadA extends Thread{
private HasSelfPrivateNum numRef;
public ThreadA(HasSelfPrivateNum numRef) {
super();
this.numRef = numRef;
}
@Override
public void run() {
super.run();
numRef.addI("a");
}
}
static class ThreadB extends Thread{
private HasSelfPrivateNum numRef;
public ThreadB(HasSelfPrivateNum numRef) {
super();
this.numRef = numRef;
}
@Override
public void run() {
super.run();
numRef.addI("b");
}
}
}
运行结果为
a set over!
b set over!
bnum=200
anum=100
4. 两个线程访问同一个对象的两个方法
此时有以下结论
- A线程现持有object对象的Lock锁,B线程可以以异步的方式调用object对象中的非synchronized类型的方法。
- A线程先持有object对象的Lock锁,B线程如果在这时调用object对象中的synchronized类型的方法,则需要等待,也就是同步。
- 在方法声明处添加synchronized并不是锁方法,而是锁当前类的对象。
- 在Java中只有“当对象作为锁”这种说法,并没有“锁方法”这种说法。
- 在Java语言中,“锁”就是“对象”,“对象”可以映射成“锁”,哪个线程拿到这把锁,哪个线程就可以执行这个对象中的synchronized同步方法。
- 如果在对象中使用了synchronized关键字声明非静态方法,则对象就被当成锁。
例子1:两个线程分别调用同一个对象的同步方法和非同步方法
public class SynchronizedTest4_1 {
public static void main(String[] args) {
MyObject obejct = new MyObject();
ThreadA aThread = new ThreadA(obejct);
aThread.start();
ThreadB bThread = new ThreadB(obejct);
bThread.start();
}
static class MyObject{
synchronized public void methodA() {
try {
System.out.println("begin methodA threadName=" + Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("end endTime=" + System.currentTimeMillis());
}catch (InterruptedException e) {
e.printStackTrace();
}
}
public void methodB() {
try {
System.out.println("begin methodB threadName=" + Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("end endTime=" + System.currentTimeMillis());
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class ThreadA extends Thread{
private MyObject object;
public ThreadA(MyObject object) {
super();
this.object = object;
}
@Override
public void run() {
super.run();
object.methodA();
}
}
static class ThreadB extends Thread{
private MyObject object;
public ThreadB(MyObject object) {
super();
this.object = object;
}
@Override
public void run() {
super.run();
object.methodB();
}
}
}
运行结果为
begin methodA threadName=Thread-0
begin methodB threadName=Thread-1
end endTime=1603249231399
end endTime=1603249231399
例子2:两个线程分别调用同一个对象的两个不同的同步方法
public class SynchronizedTest4_2 {
public static void main(String[] args) {
MyObject obejct = new MyObject();
ThreadA aThread = new ThreadA(obejct);
aThread.start();
ThreadB bThread = new ThreadB(obejct);
bThread.start();
}
static class MyObject{
synchronized public void methodA() {
try {
System.out.println("begin methodA threadName=" + Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("end endTime=" + System.currentTimeMillis());
}catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized public void methodB() {
try {
System.out.println("begin methodB threadName=" + Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("end endTime=" + System.currentTimeMillis());
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class ThreadA extends Thread{
private MyObject object;
public ThreadA(MyObject object) {
super();
this.object = object;
}
@Override
public void run() {
super.run();
object.methodA();
}
}
static class ThreadB extends Thread{
private MyObject object;
public ThreadB(MyObject object) {
super();
this.object = object;
}
@Override
public void run() {
super.run();
object.methodB();
}
}
}
运行结果为:
begin methodA threadName=Thread-0
end endTime=1603249500001
begin methodB threadName=Thread-1
end endTime=1603249505002
5. 脏读
我们有时候在赋值的方法进行了同步,但是在取值的方法没有进行同步,就有可能出现脏读(dirty read)
例子:
public class SynchronizedTest5 {
public static void main(String[] args) {
PublicVar publicVar = new PublicVar();
ThreadA aThread = new ThreadA(publicVar);
aThread.start();
ThreadB bThread = new ThreadB(publicVar);
bThread.start();
}
static class PublicVar{
public String username = "A";
public String password = "AA";
synchronized public void setValue(String username, String password) {
try {
this.username = username;
Thread.sleep(5000);
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() {
System.out.println("getValue method thread name=" + Thread.currentThread().getName()
+ " username" + username + " password=" + password);
}
}
static class ThreadA extends Thread{
private PublicVar publicVar;
public ThreadA(PublicVar publicVar) {
super();
this.publicVar = publicVar;
}
@Override
public void run() {
super.run();
publicVar.setValue("B", "BB");
}
}
static class ThreadB extends Thread{
private PublicVar publicVar;
public ThreadB(PublicVar publicVar) {
super();
this.publicVar = publicVar;
}
@Override
public void run() {
super.run();
publicVar.getValue();
}
}
}
运行结果为:
getValue method thread name=Thread-1 usernameB password=AA
setValue method thread name=Thread-0 usernameB password=BB
6. Synchronized锁重入
“可重入锁”是指自己可以再次获取自己的内部锁。例如。一个线程获得了某个对象锁,此时这个对象锁还没有释放,当其再次想要获取这个对象锁时还是可以获取的。
并且锁重入支持继承的环境
例子
public class SynchronizedTest6 {
public static void main(String[] args) {
MyThreadA t1 = new MyThreadA();
t1.start();
MyThreadB t2 = new MyThreadB();
t2.start();
}
static class Main{
public int i = 10;
synchronized public void operateIMainMethod() {
try {
i--;
System.out.println("main pint i=" + i);
Thread.sleep(100);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class Sub extends Main{
public int i = 10;
synchronized public void operateISubMethod() {
try {
i--;
System.out.println("sub pint i=" + i);
Thread.sleep(1000);
super.operateIMainMethod();
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class Sub2 extends Main{
public int i = 10;
synchronized public void operateISubMethod() {
try {
i--;
System.out.println("sub2 pint i=" + i);
Thread.sleep(1000);
super.operateIMainMethod();
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class MyThreadA extends Thread{
@Override
public void run() {
Sub sub = new Sub();
sub.operateISubMethod();
}
}
static class MyThreadB extends Thread{
@Override
public void run() {
Sub2 sub2 = new Sub2();
sub2.operateISubMethod();
}
}
}
运行结果
sub pint i=9
sub2 pint i=9
main pint i=9
main pint i=9
7. 出现异常,锁会自动释放
当一个线程执行的代码出现异常时,其所持有的锁会自动释放。Thread.java类中suspend()方法和sleep(millis)方法被调用后并不释放锁。
8. 重写方法
重写方法如果不使用synchronized关键字,即是非同步方法,使用后变成同步方法。
9. 单一方法内的sychronized同步语句块
用关键字synchronized声明方法在某些情况下是有弊端的,例如:A线程调用同步方法执行一个长时间的任务,那么B线程等待的时间就比较长,这种情况可以使用synchronized同步语句块来解决,以提高运行效率。此时只有运行到同步语句块的时候才会需要获取锁,方法内的其他代码都不需要锁。
例子1:synchronized声明方法
public class Task{
public String getData1;
public String getData2;
synchronized public void doLongTimeTask() {
try {
System.out.println("begin task");
Thread.sleep(3000);
getData1 = "长时间处理任务后从远程返回的值1 threadName="
+ Thread.currentThread().getName();
getData2 = "长时间处理任务后从远程返回的值2 threadName="
+ Thread.currentThread().getName();
System.out.println(getData1);
System.out.println(getData2);
System.out.println("end task");
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
例子2:synchronized同步语句块
static class Task{
public String getData1;
public String getData2;
public void doLongTimeTask() {
try {
System.out.println("begin task");
Thread.sleep(3000);
synchronized(this) {
getData1 = "长时间处理任务后从远程返回的值1 threadName="
+ Thread.currentThread().getName();
getData2 = "长时间处理任务后从远程返回的值2 threadName="
+ Thread.currentThread().getName();
System.out.println(getData1);
System.out.println(getData2);
System.out.println("end task");
}
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
10. 多个synchronized同步语句块
在使用同步synchronized(this)代码块时需要注意,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对同一个object中所有其他synchronized(this)同步代码块的访问将被阻塞。
我们常用的PrintStream.println方法也是使用的同步代码块。
11. synchronized同步语句块和synchronized修饰的方法
使用synchronized(this)同步代码块将当前类的对象作为锁,使用synchronized修饰的方法也是将当前类所在的对象作为锁,都是同一把锁,所以会相互阻塞。
例子
public class SynchronizedTest11 {
public static void main(String[] args) {
Task task = new Task();
ThreadA aThread = new ThreadA(task);
aThread.start();
ThreadB bThread = new ThreadB(task);
bThread.start();
}
static class Task{
public void doLongTimeTask() {
try {
synchronized(this) {
System.out.println("=======run===doLongTimeTask====start");
Thread.sleep(5000);
System.out.println("=======run===doLongTimeTask====end");
}
}catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized public void otherMethod() {
System.out.println("-----run---otherMethod");
}
}
static class ThreadA extends Thread{
private Task task;
public ThreadA(Task task) {
super();
this.task = task;
}
@Override
public void run() {
super.run();
task.doLongTimeTask();
}
}
static class ThreadB extends Thread{
private Task task;
public ThreadB(Task task) {
super();
this.task = task;
}
@Override
public void run() {
super.run();
task.otherMethod();
}
}
}
运行结果:
=======run===doLongTimeTask====start
=======run===doLongTimeTask====end
-----run---otherMethod
12. 将任意对象作为锁
多个线程调用同一个对象中的不同名称的synchronized同步方法或synchronzied(this)同步代码块时,调用的效果都是按顺序执行,即同步。
synchronized同步代码块和synchronized同步方法基本一样,都是下面两个规则:
- 对于其他synchronized同步方法或synchronized(this)同步代码块调用呈同步效果
- 同一时间只有一个线程可以执行synchronized同步方法(synchronized同步代码块)中的代码
除了使用synchronized(this)格式来创建同步代码块,其实Java还支持将“任意对象”作为锁来实现同步的功能,这个“任意对象”大多数是实例变量及方法的参数。使用格式为synchronized(非this对象)。
synchronized(非this对象X)同步代码的作用:当多个线程争抢相同的“非this对象X”的锁时,同一时间只有一个线程可以执行synchronized(非this对象X)同步代码块中的代码。
例子:
public class SynchronizedTest12 {
public static void main(String[] args) {
Service service = new Service();
ThreadA aThread = new ThreadA(service);
aThread.start();
ThreadB bThread = new ThreadB(service);
bThread.start();
}
static class Service{
private String anyString = new String();
public void a() {
try {
synchronized(anyString) {
System.out.println("in synchronized anyString, threadName="
+ Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("exit synchronized anyString, threadName="
+ Thread.currentThread().getName());
}
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class ThreadA extends Thread{
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
super.run();
service.a();
}
}
static class ThreadB extends Thread{
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
super.run();
service.a();
}
}
}
运行结果:
in synchronized anyString, threadName=Thread-0
exit synchronized anyString, threadName=Thread-0
in synchronized anyString, threadName=Thread-1
exit synchronized anyString, threadName=Thread-1
13. 同一个对象中的不同锁
如果synchronized(对象X)中的对象不是同一个对象,那么即便在同一个对象锁,也不会产生锁竞争。
例子:
public class SynchronizedTest13 {
public static void main(String[] args) {
Service service = new Service();
ThreadA aThread = new ThreadA(service);
aThread.start();
ThreadB bThread = new ThreadB(service);
bThread.start();
}
static class Service{
private String anyString = new String();
public void a() {
try {
synchronized(anyString) {
System.out.println("in a, threadName="
+ Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("exit a, threadName="
+ Thread.currentThread().getName());
}
}catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized public void b() {
System.out.println("in b, threadName="
+ Thread.currentThread().getName());
}
}
static class ThreadA extends Thread{
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
super.run();
service.a();
}
}
static class ThreadB extends Thread{
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
super.run();
service.b();
}
}
}
运行结果:
in a, threadName=Thread-0
in b, threadName=Thread-1
exit a, threadName=Thread-0
14. 方法被调用时随机的
同步代码块放在非同步synchronized方法中进行声明,并不能保证调用方法的线程的执行同步顺序性,也就是线程调用方法的顺序是无序的,虽然在同步块中执行的顺序是同步的。
例子:
public class SynchronizedTest14 {
public static void main(String[] args) {
MyList list = new MyList();
ThreadA aThread = new ThreadA(list);
aThread.setName("A");
aThread.start();
ThreadB bThread = new ThreadB(list);
bThread.setName("B");
bThread.start();
}
static class MyList{
synchronized public void add(String username) {
System.out.println("in add,threadName="
+ Thread.currentThread().getName());
System.out.println("exit add,threadName="
+ Thread.currentThread().getName());
}
}
static class ThreadA extends Thread{
private MyList list;
public ThreadA(MyList list) {
super();
this.list = list;
}
@Override
public void run() {
super.run();
for(int i = 1; i < 1000; i++) {
list.add("threadA" + i);
}
}
}
static class ThreadB extends Thread{
private MyList list;
public ThreadB(MyList list) {
super();
this.list = list;
}
@Override
public void run() {
super.run();
for(int i = 1; i < 1000; i++) {
list.add("threadB" + i);
}
}
}
}
运行结果太过于长,只截取部分
in add,threadName=A
exit add,threadName=A
in add,threadName=A
exit add,threadName=A
in add,threadName=A
exit add,threadName=A
in add,threadName=B
exit add,threadName=B
in add,threadName=B
exit add,threadName=B
in add,threadName=A
exit add,threadName=A
in add,threadName=A
exit add,threadName=A
in add,threadName=B
exit add,threadName=B
in add,threadName=A
exit add,threadName=A
15. 不同步导致的逻辑错误
有些方法不进行同步化,则会出现逻辑上的错误
例子如下:
public class SynchronizedTest15 {
public static void main(String[] args) throws InterruptedException {
MyOneList list = new MyOneList();
ThreadA aThread = new ThreadA(list);
aThread.setName("A");
aThread.start();
ThreadB bThread = new ThreadB(list);
bThread.setName("B");
bThread.start();
Thread.sleep(6000);
System.out.println("listSize=" + list.getSize());
}
static class MyOneList{
private List<String> list = new ArrayList<String>();
synchronized public void add(String data) {
list.add(data);
}
synchronized public int getSize() {
return list.size();
}
}
static class MyService{
public MyOneList addServiceMethod(MyOneList list, String data) {
try {
if(list.getSize() < 1) {
Thread.sleep(2000);
list.add(data);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return list;
}
}
static class ThreadA extends Thread{
private MyOneList list;
public ThreadA(MyOneList list) {
super();
this.list = list;
}
@Override
public void run() {
super.run();
MyService msRef = new MyService();
msRef.addServiceMethod(list, "A");
}
}
static class ThreadB extends Thread{
private MyOneList list;
public ThreadB(MyOneList list) {
super();
this.list = list;
}
@Override
public void run() {
super.run();
MyService msRef = new MyService();
msRef.addServiceMethod(list, "B");
}
}
}
运行结果为
listSize=2
这就是没有对MyService.addServiceMethod()方法进行同步导致的错误
16. 静态同步方法synchronized方法和synchronized(class)代码块
关键字synchronized还可以应用在static静态方法上,如果这样写,那是对当前的*.java文件对应的Class类对象进行持锁,Class类的对象是单例的,更具体的说,在静态static方法上使用synchronized关键字声明同步方法时,使用当前静态方法所在类对应Class类的单例对象作为锁。
例子1:静态同步方法和非静态同步方法是不同的锁
public class SynchronizedTest16 {
public static void main(String[] args) throws InterruptedException {
Service service = new Service();
ThreadA aThread = new ThreadA(service);
aThread.setName("A");
aThread.start();
ThreadB bThread = new ThreadB(service);
bThread.setName("B");
bThread.start();
}
static class Service{
synchronized public static void printA() {
try {
System.out.println("in A ,threadName=" + Thread.currentThread().getName());
Thread.sleep(3000);
System.out.println("exit A ,threadName=" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized public void printB() {
try {
System.out.println("in B ,threadName=" + Thread.currentThread().getName());
Thread.sleep(3000);
System.out.println("exit B ,threadName=" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class ThreadA extends Thread{
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
super.run();
service.printA();
}
}
static class ThreadB extends Thread{
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
super.run();
service.printB();
}
}
}
运行结果
in A ,threadName=A
in A ,threadName=B
exit A ,threadName=B
exit A ,threadName=A
例子2:同一个类的不同对象的不同synchronized静态方法也是同一把锁
public class SynchronizedTest16_2 {
public static void main(String[] args) throws InterruptedException {
Service service1 = new Service();
ThreadA aThread = new ThreadA(service1);
aThread.setName("A");
aThread.start();
Service service2 = new Service();
ThreadB bThread = new ThreadB(service2);
bThread.setName("B");
bThread.start();
}
static class Service{
synchronized public static void printA() {
try {
System.out.println("in A ,threadName=" + Thread.currentThread().getName());
Thread.sleep(3000);
System.out.println("exit A ,threadName=" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized public static void printB() {
try {
System.out.println("in B ,threadName=" + Thread.currentThread().getName());
Thread.sleep(3000);
System.out.println("exit B ,threadName=" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class ThreadA extends Thread{
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
super.run();
service.printA();
}
}
static class ThreadB extends Thread{
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
super.run();
service.printB();
}
}
}
运行结果
in A ,threadName=A
exit A ,threadName=A
in B ,threadName=B
exit B ,threadName=B
例子3:synchronized静态方法和synchronized(Class)是同一把锁
public class SynchronizedTest16_3 {
public static void main(String[] args) throws InterruptedException {
Service service1 = new Service();
ThreadA aThread = new ThreadA(service1);
aThread.setName("A");
aThread.start();
Service service2 = new Service();
ThreadB bThread = new ThreadB(service2);
bThread.setName("B");
bThread.start();
}
static class Service{
synchronized public static void printA() {
try {
System.out.println("in A ,threadName=" + Thread.currentThread().getName());
Thread.sleep(3000);
System.out.println("exit A ,threadName=" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void printB() {
try {
synchronized(Service.class) {
System.out.println("in B ,threadName="
+ Thread.currentThread().getName());
Thread.sleep(3000);
System.out.println("exit B ,threadName="
+ Thread.currentThread().getName());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class ThreadA extends Thread{
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
super.run();
service.printA();
}
}
static class ThreadB extends Thread{
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
super.run();
service.printB();
}
}
}
运行结果
in A ,threadName=A
exit A ,threadName=A
in B ,threadName=B
exit B ,threadName=B
17. String常量池特性和同步相关问题
JVM具有String常量池的功能,当synchronized(string)同步块与String联合使用时,要注意常量池带来的一些意外。
当你传入的String的值是一样的时候,锁可能就是相同的。
例子
public class SynchronizedTest17 {
public static void main(String[] args) throws InterruptedException {
Service service = new Service();
ThreadA aThread = new ThreadA(service);
aThread.setName("A");
aThread.start();
ThreadB bThread = new ThreadB(service);
bThread.setName("B");
bThread.start();
}
static class Service{
public static void print(String stringParam) {
try {
synchronized(stringParam) {
while(true) {
System.out.println("in print,threadName="
+ Thread.currentThread().getName());
Thread.sleep(1000);
System.out.println("exit print,threadName="
+ Thread.currentThread().getName());
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class ThreadA extends Thread{
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
super.run();
service.print("AA");
}
}
static class ThreadB extends Thread{
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
super.run();
service.print("AA");
}
}
}
截取部分运行结果:
in print,threadName=A
exit print,threadName=A
in print,threadName=A
exit print,threadName=A
in print,threadName=A
exit print,threadName=A
in print,threadName=A
exit print,threadName=A
in print,threadName=A
exit print,threadName=A
in print,threadName=A
exit print,threadName=A
in print,threadName=A
exit print,threadName=A
18. 多线程的死锁
Java线程死锁是一个经典的多线程问题,因为不同的线程都在等待根本不可能被释放的锁,从而导致所有的任务都无法继续完成。在多线程技术中,“死锁”是必须避免的,因为这会造成线程“假死”。
例子:
public class SynchronizedTest18 {
public static void main(String[] args) throws InterruptedException {
DealThread t1 = new DealThread();
t1.setFlag("a");
Thread thread1 = new Thread(t1);
thread1.start();
Thread.sleep(1000);
t1.setFlag("b");
Thread thread2 = new Thread(t1);
thread2.start();
}
static class DealThread 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("a".equals(username)) {
synchronized (lock1) {
try {
System.out.println("username= " + username);
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(lock2) {
System.out.println("按lock1->lock2代码顺序执行了");
}
}
}
if("b".equals(username)) {
synchronized (lock2) {
try {
System.out.println("username= " + username);
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(lock1) {
System.out.println("按lock2->lock1代码顺序执行了");
}
}
}
}
}
}
运行结果
username= a
username= b