- Synchronized是java的关键字,被java语音原生支持
- 是最基本的互斥同步手段
- 是并发编程中的元老级角色,是并发编程的必学内容
首先引入oracle对Synchronized的解释:
同步方法支持一种简单的策略来防止线程干扰和内存一致性错误:如果一个对象对多个线程可见,则对该对象变量的所有读取或写入都是通过同步方法完成的。
简单的来说就是:在多线程同时执行一段程序时,能够保证在同一时刻最多只有一个线程执行该段程序代码,以达到保证并发安全的效果
首先看看在不使用并发的后果 一个i++ 10000次结果会是多少呢
public class Add implements Runnable {
static Add instance = new Add();
static int i = 0;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
t1.join(); //等待线程执行完再执行后面的代码
t2.join();
System.out.println(i);
}
@Override
public void run() {
for (int j = 0; j < 10000; j++) {
i++;
}
}
}
复制代码
上面的栗子每次运行的结果都不一样,这是因为:两个线程同时i++, 最后结果会比预计的少(i++是三个操作,读取i, i++, 将i写入内存),所以在多线程的情况下每一步执行完都有可能被打断,所以i值有可能没写进内存就执行另一个线程的i++操作了,这种情况我们称为线程不安全。
Synchronized的两个用法
1、对象锁:
a、方法锁(默认锁对象为this当前实例对象)
b、同步代码块锁(自己指定锁对象)
2、类锁:
概念:Java类可能有很多个对象,但只有一个Class对象,锁类时Class对象的锁(锁类只能在同一时刻被一个对象拥有)
形式:
a、使用关键字synchronized修饰静态的方法
b、synchronized(*.class)代码块
复制代码
方法锁的栗子:
public class SimpleExample implements Runnable {
static SimpleExample instance1 = new SimpleExample();
@Override
public void run() {
method1();
method2();
}
public synchronized void method1() {
System.out.println(Thread.currentThread().getName() + " method1 start");
try {
Thread.sleep(1000);
} catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " method1 end");
}
public synchronized void method2() {
System.out.println(Thread.currentThread().getName() + " method2 start");
try {
Thread.sleep(1000);
} catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " method2 end");
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(instance1);
Thread t2 = new Thread(instance1);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("finished");
}
}
/**
结果:
Thread-0 method1 start
Thread-0 method1 end
Thread-0 method2 start
Thread-0 method2 end
Thread-1 method1 start
Thread-1 method1 end
Thread-1 method2 start
Thread-1 method2 end
*/
复制代码
对上述的结果有点不理解,(给两个方法都加上锁,当第一个线程执行完第一个方法之后,第一个方法不是会释放掉,那么第二个线程应该会有可能在第一个线程执行第二个方法之前去执行第一个方法,但结果总是第一个线程执行完再到第二个线程)自己的理解是这样的:应该两个方法上的synchronized关键字都是加在this上的,就像同步代码块那样子,如果加在同一个对象上,那么线程会依次执行。ps:希望高手能指教...
使用关键字synchronized修饰静态的方法,这样可以在一些全局方法上做到同步
public class SimpleExample implements Runnable {
static SimpleExample instance1 = new SimpleExample();
static SimpleExample instance2 = new SimpleExample();
@Override
public void run() {
method1();
}
public static synchronized void method1() { //如果把static去掉,线程就不会是同步的
System.out.println(Thread.currentThread().getName() + " method1 start");
try {
Thread.sleep(500);
} catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " method1 end");
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(instance1);
Thread t2 = new Thread(instance2);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("finished");
}
}
/**
结果:
Thread-0 method1 start
Thread-0 method1 end
Thread-1 method1 start
Thread-1 method1 end
*/
复制代码
总结
- 一把锁只能同时被一个线程获取,没有拿到锁的线程必须等待
- 每个实例都对应有自己的一把锁,不同实例之间互不影响;例外:锁对象是×。class以及synchronied修饰的是static方法的时候,所有对象共用一把锁
- 无论是方法正常执行完毕或者方法抛出异常,都会释放锁