synchronized锁

为什么使用synchronized锁 如果多个线程对同一变量,并发访问修改,没有做同步处理最终就会导致出现脏读的问题。
非线程安全的变量 方法内的变量为线程安全的变量:jvm方法调用在操作数栈中,而操作数栈是每个线程独有的,线程间的栈是不共享的,所以方法内的变量是每个线程独有的,所以不会出现脏读(题外话:在单个线程间栈内创建数据如果存在值相同的,就指向同一个地址,就是栈共享)。
实例变量为非线程安全的变量:实例变量在实例化后将在堆上分配,而堆是线程间共享的,那么就会出现非线程安全的问题。
synchronized锁重入

  1. 可重入锁概念就是自己可以再次获取自己的内部锁。
public class MyService {public synchronized void method1() { System.out.println("method1"); method2(); }public synchronized void method2() { System.out.println("method2"); method3(); }public synchronized void method3() { System.out.println("method3"); } }public class ServiceThread extends Thread {@Override public void run() { MyService ms = new MyService(); ms.method1(); } }public class Test {public static void main(String[] args) { ServiceThread st = new ServiceThread(); st.start(); } }

结果:
[Console output redirected to file:D:\console.txt] method1 method2 method3

  1. 可重入锁支持父子继承关系中
public class Father { public synchronized void execute1() { System.out.println("Father execute1"); } }public class Children extends Father{ public synchronized void execute2() { System.out.println("Children execute2"); execute1(); } }public class ServiceThread extends Thread { @Override public void run() { Children c = new Children(); c.execute2(); } }public class Test {public static void main(String[] args) { ServiceThread st = new ServiceThread(); st.start(); } }

结果:
[Console output redirected to file:D:\console.txt] Children execute2 Father execute1

异常,锁自动释放
public class Children {public synchronized void execute2() { System.out.println("Children execute2"); int i = 3 / 0; System.out.println("i = " + i); } }public class ServiceThread extends Thread {@Override public void run() { Children c = new Children(); c.execute2(); } }public class Test {public static void main(String[] args) { ServiceThread st = new ServiceThread(); st.start(); } }

结果:
[Console output redirected to file:D:\console.txt] Children execute2 Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero at cn.spy.thread.test.Children.execute2(Children.java:8) at cn.spy.thread.test.ServiceThread.run(ServiceThread.java:8)

synchronized同步代码块 【synchronized锁】synchronized方法对当前对象进行加锁,synchronized代码块是对某一个对象进行加锁。
1. 同步代码块和同步方法的区别
同步方法的特点就是只要是当前对象的同步方法,当前线程没执行完,其他线程只能等着。而同步代码块则是对于不同对象的同步代码块,线程1执行同步代码块1时,线程2也可以执行同步代码块2,这样效率会有所提升。而在我看来实际上两种方式只是细粒度上进一步得到了提升。同步代码块可以进行更加细微的操作。
2. synchronized(this)锁定当前对象
public class MyTask {public void execute() { synchronized (this) { for (int i = 0 ; i < 3 ; i++) { System.out.println(Thread.currentThread().getName() + " " + i); } } } }public class MyThreadA extends Thread{private MyTask myTask; public MyThreadA(MyTask myTask) { this.myTask = myTask; }@Override public void run() { myTask.execute(); } }public class MyThreadB extends Thread{private MyTask myTask; public MyThreadB(MyTask myTask) { this.myTask = myTask; }@Override public void run() { myTask.execute(); } }public class MyTest {public static void main(String[] args) { MyTask myTask = new MyTask(); MyThreadA mtA = new MyThreadA(myTask); mtA.setName("MyThreadA"); MyThreadB mtB = new MyThreadB(myTask); mtB.setName("MyThreadB"); mtA.start(); mtB.start(); } }

结果:
MyThreadA 0 MyThreadA 1 MyThreadA 2 MyThreadB 0 MyThreadB 1 MyThreadB 2

静态同步synchronized synchronized关键字加到static静态方法上是给Class类上锁,而synchronized关键字加到非static静态方法上是给对象上锁。
public class MyTask {public synchronized void execute() { System.out.println(Thread.currentThread().getName() + " execute start"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); }System.out.println(Thread.currentThread().getName() + " execute end"); }public synchronized void print() { System.out.println(Thread.currentThread().getName() + " print start"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); }System.out.println(Thread.currentThread().getName() + " print end"); } }public class MyThreadA extends Thread{private MyTask myTask; public MyThreadA(MyTask myTask) { this.myTask = myTask; }@Override public void run() { myTask.execute(); } }public class MyThreadB extends Thread{private MyTask myTask; public MyThreadB(MyTask myTask) { this.myTask = myTask; }@Override public void run() { myTask.print(); } }public class MyTest {public static void main(String[] args) { MyTask myTask = new MyTask(); MyThreadA mtA = new MyThreadA(myTask); mtA.setName("MyThreadA"); MyThreadB mtB = new MyThreadB(myTask); mtB.setName("MyThreadB"); mtA.start(); mtB.start(); } }

结果:
MyThreadA execute start MyThreadA execute end MyThreadB print start MyThreadB print end

总结
  1. 方法内的变量是线程安全的变量,实例变量为非线程安全的变量。
  2. synchronized锁支持重重入。
  3. synchronized可重入锁支持父子继承关系中
  4. 遇到异常,synchronized锁自动释放。
  5. synchronized修饰方法意味着对当前调用方法的对象加锁同步。
  6. synchronized(this)锁定当前的对象。
  7. synchronized(class)锁定指定的类。
  8. synchronized修饰静态方法意味着给静态方法所在的类上锁。

    推荐阅读