Java中synchronized是用来表示同步的,synchronized可以用来修饰一个方法(static方法和非static方法),也可以用来修饰一段代码块;
修饰实例方法:
public synchronized void x() throws InterruptedException {
for (int i = 0; i < 5; i++) {
Thread.sleep(1000);
System.out.println("x <--------------");
}
}
public static synchronized void staticX() throws InterruptedException {
for (int i = 0; i < 5; i++) {
Thread.sleep(1000);
System.out.println("staticX <--------------");
}
}
’static’的方法属于类方法,它属于这个Class(注意:这里的Class不是指Class的某个具体对象),那么static获取到的锁,就是当前调用这个方法的对象所属的类(Class,而不再是由这个Class产生的某个具体对象了)。而‘非static’方法获取到的锁,就是当前调用这个方法的对象的锁了。所以,他们之间不会产生互斥。
main方法:
public static void main(String[] args) {
final Test test1 = new Test();
Thread thread = new Thread(new Runnable() {
public void run() {
try {
test1.x();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "a");
Thread thread1 = new Thread(new Runnable() {
public void run() {
try {
Test.staticX();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}, "b");
thread.start();
thread1.start();
}
运行结果:
x <--------------
staticX <--------------
x <--------------
staticX <--------------
staticX <--------------
x <--------------
x <--------------
staticX <--------------
staticX <--------------
x <--------------
那当我们想让所有这个类下面的对象都同步的时候,也就是让所有这个类下面的对象共用同一把锁的时候,我们如何办呢?
代码如下:
public class Test2 {
//创建一个对象
final static Test2 ce = new Test2();
public static void staticX() throws Exception {
/**此处的synchronized 和下面的 x()中的synchronized它们调用的都是同一对象‘ce’,
* 那么此时Test2中的两个方法(所有的方法),公用的都是同一把锁
* 此处解释的可能不太正确,反正大致就是这个意思
*/
synchronized (ce) {
for (int i = 0; i < 5; i++) {
Thread.sleep(1000);
System.out.println("staticX.......................");
}
}
}
public void x() throws InterruptedException {
synchronized (ce) {
for (int i = 0; i < 5; i++) {
Thread.sleep(1000);
System.out.println("x.......................");
}
}
}
public static void main(String[] args) {
final Test2 test1 = new Test2();
Thread thread = new Thread(new Runnable() {
public void run() {
try {
test1.x();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "a");
Thread thread1 = new Thread(new Runnable() {
public void run() {
try {
Test2.staticX();
} catch (Exception e) {
e.printStackTrace();
}
}
}, "b");
thread1.start();
thread.start();
}
}
运行结果:
staticX.......................
staticX.......................
staticX.......................
staticX.......................
staticX.......................
x.......................
x.......................
x.......................
x.......................
x.......................
一个关于多线程的实验,摘录自http://blog.csdn.net/hey_a_hao/article/details/10155503
/**
* Created by LUOFENG on 2017/3/21.
*/
public class JoinThread extends Thread {
public static int i = 0;
public synchronized void inc() {
i++;
}
@Override
public void run() {
for (int x = 0; x < 10; x++) {
inc();
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread[] t = new Thread[10];
for (int i = 0; i < t.length; i++) {
t[i] = new JoinThread();
}
for (int i = 0; i < t.length; i++) {
t[i].start();
}
for (int i = 0; i < t.length; i++) {
t[i].join();
}
System.out.println(JoinThread.i);
}
}
执行完发现,i并没有如想像中的输出1000,即使i添加volatile进行修饰,也不会输出1000,值是随机变化的。
将inc()方法添加static修饰,结果无问题,准确无误的输出1000。
另外一种改法,将代码改成:
修改成:
结果无问题,准确无误的输出1000
这里主要涉及到类对象(static方法),对象方法(非static方法)
我们知道,当synchronized修饰一个static方法时,多线程下,获取的是类锁(即Class本身,注意:不是实例);
当synchronized修饰一个非static方法时,多线程下,获取的是对象锁(即类的实例对象)
所以,当synchronized修饰一个static方法时,创建线程不管是new JoinThread()还是new Thread(new JoinThread()),在run方法中执行inc()方法都是同步的;
相反,当synchronized修饰一个非static方法时,如果用new JoinThread()还是new Thread(new JoinThread())方式创建线程,就无法保证同步操作,因为这时
inc()是属于对象方法,每个线程都执有一个独立的对象实例new JoinThread(),所以多线程下执行inc()方法并不会产生互斥,也不会有同步操作。
另外如果考虑到变更的原子操作,可使用atomic包下面的包装对象,这些对象都是对volatile修饰变量的一种延伸,可保证变量的原子操作而不用去同步方法或
代码块是否同步。