1. 数据类型String 的常量池特性
public class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.print("AA");
}
}
public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.print("AA");
}
}
public class Service {
public static void print(String stringParam) {
try {
synchronized (stringParam) {
while (true) {
System.out.println(Thread.currentThread().getName());
Thread.sleep(1000);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Run {
public static void main(String[] args) {
Service service = new Service();
ThreadA a = new ThreadA(service);
a.setName("A");
a.start();
ThreadB b = new ThreadB(service);
b.setName("B");
b.start();
}
}
运行效果
A
A
A
出现这样的情况原因是String的两个值都是AA,两个线程持有相同的锁,所以造成了B线程不能执行。因此在大多数情况下,同步sync代码块都不使用String作为锁对象。而改用其他,比如new Object()实例化一个Object对象,但不放入缓存中。
例子如下:
public class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.print(new Object());
}
}
public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.print(new Object());
}
}
public class Service {
public static void print(Object object) {
try {
synchronized (object) {
while (true) {
System.out.println(Thread.currentThread().getName());
Thread.sleep(1000);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Run {
public static void main(String[] args) {
Service service = new Service();
ThreadA a = new ThreadA(service);
a.setName("A");
a.start();
ThreadB b = new ThreadB(service);
b.setName("B");
b.start();
}
}
运行结果如下:
A
B
A
B
可以看到交替打印,说明持有的锁不是一个。
2. 同步sync方法无限等待与解决
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 Service {
synchronized public void methodA() {
System.out.println("methodA begin");
boolean isContinueRun = true;
while (isContinueRun) {
}
System.out.println("methodA end");
}
synchronized public void methodB() {
System.out.println("methodB begin");
System.out.println("methodB end");
}
}
public class Run {
public static void main(String[] args) {
Service service = new Service();
ThreadA athread = new ThreadA(service);
athread.start();
ThreadB bthread = new ThreadB(service);
bthread.start();
}
}
运行结果:
methodA begin
线程B永远得不到运行的机会,锁死了。这时可以用同步块来解决,更改后的如下:
public class Service {
Object object1 = new Object();
public void methodA() {
synchronized (object1) {
System.out.println("methodA begin");
boolean isContinueRun = true;
while (isContinueRun) {
}
System.out.println("methodA end");
}
}
Object object2 = new Object();
public void methodB() {
synchronized (object2) {
System.out.println("methodB begin");
System.out.println("methodB end");
}
}
}
运行结果如下:
methodA begin
methodB begin
methodB end
3. 多线程的死锁
死锁是一个经典的多线程问题,因为不同的线程都在等待根本不可能被释放的锁。在多线程技术中,死锁是必须避免的,因为这会造成程序的假死。
例子
public 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 (username.equals("a")) {
synchronized (lock1) {
try {
System.out.println("username = " + username);
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
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 (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (lock1) {
System.out.println("按 lock2->lock1代码顺序执行了");
}
}
}
}
}
public class Run {
public static void main(String[] args) {
try {
DealThread t1 = new DealThread();
t1.setFlag("a");
Thread thread1 = new Thread(t1);
thread1.start();
Thread.sleep(100);
t1.setFlag("b");
Thread thread2 = new Thread(t1);
thread2.start();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
运行结果:
username = a
username = b
结论:死锁是程序的bug,在设计程序的时候要避免双方互相持有对方的锁的情况。
只要互相等待对方释放锁就有可能出现死锁。
4. 内置类与静态内置类
(1) 内置类与同步:实验1
在内置类中有两个同步的方法,但使用的是不同的锁,打印的结果也是异步的。
public class OutClass {
static class Inner {
public void method1() {
synchronized ("其它的锁") {
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName() + " i="
+ i);
try {
Thread.sleep(100);
} catch (InterruptedException 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 (InterruptedException e) {
}
}
}
}
}
public class Run {
public static void main(String[] args) {
final Inner inner = new Inner();
Thread t1 = new Thread(new Runnable() {
public void run() {
inner.method1();
}
}, "A");
Thread t2 = new Thread(new Runnable() {
public void run() {
inner.method2();
}
}, "B");
t1.start();
t2.start();
}
}
运行结果:
A i=1
B i=11
B i=12
A i=2
A i=3
B i=13
B i=14
A i=4
B i=15
A i=5
B i=16
A i=6
B i=17
A i=7
B i=18
A i=8
B i=19
A i=9
B i=20
A i=10
由于持有不同的对象监视器,所以打印结果就是乱序的。
(2) 静态内置类与同步:实验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 < 10; i++) {
System.out.println("i=" + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
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 < 10; j++) {
System.out.println("j=" + j);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
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 < 10; k++) {
System.out.println("k=" + k);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
System.out.println(threadName + " 离开InnerClass2类中的method1方法");
}
}
}
public class Run {
public static void main(String[] args) {
final InnerClass1 in1 = new InnerClass1();
final InnerClass2 in2 = new InnerClass2();
Thread t1 = new Thread(new Runnable() {
public void run() {
in1.method1(in2);
}
}, "T1");
Thread t2 = new Thread(new Runnable() {
public void run() {
in1.method2();
}
}, "T2");
// //
// //
Thread t3 = new Thread(new Runnable() {
public void run() {
in2.method1();
}
}, "T3");
t1.start();
t2.start();
t3.start();
}
}
运行结果如下:
T1 进入InnerClass1类中的method1方法
i=0
T2 进入InnerClass1类中的method2方法
j=0
i=1
j=1
i=2
j=2
i=3
j=3
i=4
j=4
i=5
j=5
i=6
j=6
i=7
j=7
i=8
j=8
i=9
j=9
T1 离开InnerClass1类中的method1方法
T3 进入InnerClass2类中的method1方法
k=0
T2 离开InnerClass1类中的method2方法
k=1
k=2
k=3
k=4
k=5
k=6
k=7
k=8
k=9
T3 离开InnerClass2类中的method1方法
结论:
同步代码块sync(class2)对class2上锁后,其他线程只能以同步的方式调用 class2中的静态同步方法。
5. 锁对象的改变
在将任何数据类型作为同步锁时,需要注意的是:是否有多个线程同时持有锁对象,如果同时持有相同的锁对象,则这些线程之间就是同步的;如果分别获得锁对象,这些线程之间就是异步的。