看了些网上相关的文章,感觉有些结果不对,于是自己跑了一遍这些demo并附上每段程序结果和一些心得。
非线程安全的代码
public class Run {
public static void main(String[] args) {
HasSelfPrivateNum numRef = new HasSelfPrivateNum();
ThreadA athread = new ThreadA(numRef);
ThreadB bthread = new ThreadB(numRef);
athread.start();
bthread.start();
}
}
class HasSelfPrivateNum {
private int num = 0;
public 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) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class ThreadA extends Thread {
private HasSelfPrivateNum numRef;
public ThreadA(HasSelfPrivateNum numRef) {
super();
this.numRef = numRef;
}
@Override
public void run() {
super.run();
numRef.addI("a");
}
}
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!
b num=200
a num=200
A线程给num传100后进入sleep状态;B线程又传200,A线程恢复后打印出来的结果就是200。
给addI加上synchroized:
public class Run {
public static void main(String[] args) {
HasSelfPrivateNum numRef = new HasSelfPrivateNum();
ThreadA athread = new ThreadA(numRef);
ThreadB bthread = new ThreadB(numRef);
athread.start();
bthread.start();
}
}
class HasSelfPrivateNum {
private int num = 0;
synchronized public 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) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class ThreadA extends Thread {
private HasSelfPrivateNum numRef;
public ThreadA(HasSelfPrivateNum numRef) {
super();
this.numRef = numRef;
}
@Override
public void run() {
super.run();
numRef.addI("a");
}
}
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!
a num=100
b set over!
b num=200
加上锁不会出现多个线程同时进入一个方法块的情况,只有当一个代码块执行完后才执行另一个线程。
多线程多对象:
public class Run {
public static void main(String[] args) {
HasSelfPrivateNum numRef1 = new HasSelfPrivateNum();
HasSelfPrivateNum numRef2 = new HasSelfPrivateNum();
ThreadA athread = new ThreadA(numRef1);
ThreadB bthread = new ThreadB(numRef2);
athread.start();
bthread.start();
}
}
class HasSelfPrivateNum {
private int num = 0;
synchronized public 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) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class ThreadA extends Thread {
private HasSelfPrivateNum numRef;
public ThreadA(HasSelfPrivateNum numRef) {
super();
this.numRef = numRef;
}
@Override
public void run() {
super.run();
numRef.addI("a");
}
}
class ThreadB extends Thread {
private HasSelfPrivateNum numRef;
public ThreadB(HasSelfPrivateNum numRef) {
super();
this.numRef = numRef;
}
@Override
public void run() {
super.run();
numRef.addI("b");
}
}
结果:
b set over!
a set over!
b num=200
a num=100
多对象因为相互之间是独立的,不存在要获得对象锁的问题,去掉synchronized关键字结果也是一样的。多对象也不需要保证线程安全了。
同步块synchronized (this)
public class Run {
public static void main(String[] args) {
ObjectService service = new ObjectService();
ThreadA a = new ThreadA(service);
a.setName("a");
a.start();
ThreadB b = new ThreadB(service);
b.setName("b");
b.start();
}
}
class ObjectService {
public void serviceMethod() {
try {
synchronized (this) {
System.out.println("begin time=" + System.currentTimeMillis());
Thread.sleep(2000);
System.out.println("end end=" + System.currentTimeMillis());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ThreadA extends Thread {
private ObjectService service;
public ThreadA(ObjectService service) {
super();
this.service = service;
}
@Override
public void run() {
super.run();
service.serviceMethod();
}
}
class ThreadB extends Thread {
private ObjectService service;
public ThreadB(ObjectService service) {
super();
this.service = service;
}
@Override
public void run() {
super.run();
service.serviceMethod();
}
}
结果
begin time=1494832547802
end end=1494832549803
begin time=1494832549803
end end=1494832551803
synchronized (this)的this是ObjectService的一个对象。
当一个线程进入synchronized(this){}中,对象的其它包含synchronized的代码是被当前线程独占的。
关于synchronized (this)最后还会专门举例说明。
非this对象
public class Run {
public static void main(String[] args) {
Service service = new Service("xiaobaoge");
ThreadA a = new ThreadA(service);
a.setName("A");
a.start();
ThreadB b = new ThreadB(service);
b.setName("B");
b.start();
}
}
class Service {
String anyString = new String();
public Service(String anyString){
this.anyString = anyString;
System.out.println("anyString: " + anyString);
}
public void setUsernamePassword(String username, String password) {
try {
synchronized (anyString) {
System.out.println("thread name: " + Thread.currentThread().getName()
+ " go to sync at: " + System.currentTimeMillis());
Thread.sleep(3000);
System.out.println("thread name: " + Thread.currentThread().getName()
+ " go out of sync at: " + System.currentTimeMillis());
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.setUsernamePassword("a", "aa");
}
}
class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.setUsernamePassword("b", "bb");
}
}
结果:
anyString: xiaobaoge
thread name: A go to sync at: 1494833882106
thread name: A go out of sync at: 1494833885106
thread name: B go to sync at: 1494833885106
thread name: B go out of sync at: 1494833888106
能保证同步块里的代码是线程安全的。
静态synchronized同步方法
记住一句话:static方法是属于类的,用synchronized修饰static方法就是对类上锁。
public class Run {
public static void main(String[] args) {
ThreadA a = new ThreadA();
a.setName("A");
a.start();
ThreadB b = new ThreadB();
b.setName("B");
b.start();
}
}
class Service {
synchronized public static void printA() {
try {
System.out.println("thread: " + Thread.currentThread().getName()
+ " go to printA at " + System.currentTimeMillis());
Thread.sleep(3000);
System.out.println("thread: " + Thread.currentThread().getName()
+ " go out printA at " + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized public static void printB() {
System.out.println("thread: " + Thread.currentThread().getName() + " go to printB at "
+ System.currentTimeMillis());
System.out.println("thread: " + Thread.currentThread().getName() + " go out printB at "
+ System.currentTimeMillis());
}
}
class ThreadA extends Thread {
@Override
public void run() {
Service.printA();
}
}
class ThreadB extends Thread {
@Override
public void run() {
Service.printB();
}
}
结果:
thread: A go to printA at 1494834629062
thread: A go out printA at 1494834632062
thread: B go to printB at 1494834632062
thread: B go out printB at 1494834632062
printA 和printB 虽然是不同的方法,但因为static修饰的方法和变量都是属于类的,对static方法加锁相当于对类加锁,因此类是线程安全的。
synchronized (class)
类锁:
public class Run {
public static void main(String[] args) {
ThreadA a = new ThreadA();
a.setName("A");
a.start();
ThreadB b = new ThreadB();
b.setName("B");
b.start();
}
}
class Service {
public static void printA() {
synchronized (Service.class) {
try {
System.out.println("thread: " + Thread.currentThread().getName()
+ " go to printA at: " + System.currentTimeMillis());
Thread.sleep(3000);
System.out.println("thread: " + Thread.currentThread().getName()
+ " go out printA at: " + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void printB() {
synchronized (Service.class) {
System.out.println("thread: " + Thread.currentThread().getName()
+ " go to printB at: " + System.currentTimeMillis());
System.out.println("thread: " + Thread.currentThread().getName()
+ " go out printB at: " + System.currentTimeMillis());
}
}
}
class ThreadA extends Thread {
@Override
public void run() {
Service.printA();
}
}
class ThreadB extends Thread {
@Override
public void run() {
Service.printB();
}
}
结果:
thread: A go to printA at: 1494835990458
thread: A go out printA at: 1494835993459
thread: B go to printB at: 1494835993459
thread: B go out printB at: 1494835993459
执行printA时对类上锁,类是线程安全的。
补充关于synchronized(this)的例子
当一个线程访问类中synchronized(this)代码块时,其它没有带synchronized的代码块不是线程独占的:
public class Thread2 {
public void m4t1() {
synchronized (this) {
int i = 5;
while (i-- > 0) {
System.out
.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
}
}
public void m4t2() {
int i = 5;
while (i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
}
public static void main(String[] args) {
final Thread2 myt2 = new Thread2();
Thread t1 = new Thread(new Runnable() {
public void run() {
myt2.m4t1();
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
public void run() {
myt2.m4t2();
}
}, "t2");
t1.start();
t2.start();
}
}
结果:
t1 : 4
t2 : 4
t2 : 3
t1 : 3
t2 : 2
t1 : 2
t2 : 1
t1 : 1
t2 : 0
t1 : 0
如果一个线程正在访问synchronized(this)代码块,则带synchronized的其它代码块都是被当前线程独占的:
public class Thread2 {
public void m4t1() {
synchronized (this) {
int i = 5;
while (i-- > 0) {
System.out
.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
}
}
public void m4t2() {
synchronized (this){
int i = 5;
while (i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
}
}
public static void main(String[] args) {
final Thread2 myt2 = new Thread2();
Thread t1 = new Thread(new Runnable() {
public void run() {
myt2.m4t1();
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
public void run() {
myt2.m4t2();
}
}, "t2");
t1.start();
t2.start();
}
}
结果:
t1 : 4
t1 : 3
t1 : 2
t1 : 1
t1 : 0
t2 : 4
t2 : 3
t2 : 2
t2 : 1
t2 : 0
带synchronized关键字的部分都是同步代码块,当一个线程访问synchronized(this)时相当于拿到了这个对象的对象锁,对同步代码块的访问需要保证当前线程有该对象的对象锁。
有点像铁索连环,一个受影响全部受影响,解锁一个其它的才能访问。