修饰非静态成员方法java
synchronized public void sync(){ }
修饰静态成员方法安全
synchronized public static void sync(){ }
类锁代码块多线程
synchronized (类.class){ }
对象锁代码块ide
synchronized (this|对象){ }
synchronized 修饰非静态方法时能够看作是锁 this 对象,修饰静态方法时能够看作是锁方法所在的类。函数
各个线程想要访问被 synchronized 修饰的代码块,就要取得 synchronized 声明的锁。若是两个线程的目标是同一个锁,就会出现阻塞的现象,因此两个线程不能同时访问同一个锁下的代码,保证了多线程在执行时最终结果不会出错。这与共享变量是否为静态无关。this
public class ThreadDemo extends Thread { @Override public synchronized void run() { for (int i = 0; i < 10000; i++) { Main.i++; } System.out.println("执行完成"); } }
直接将继承的 run() 方法标记为 synchronized ,做用是对 Main 类中的 i 变量作 10000 次累加操做。线程
public class Main { static int i = 0; public static void main(String[] args) throws InterruptedException { ThreadDemo threadDemo=new ThreadDemo(); Thread t1 = new Thread(threadDemo); Thread t2 = new Thread(threadDemo); Thread t3 = new Thread(threadDemo); Thread t4 = new Thread(threadDemo); t1.start(); t2.start(); t3.start(); t4.start(); t1.join(); t2.join(); t3.join(); t4.join(); System.out.println(i); } } //输出结果: //执行完成 //执行完成 //执行完成 //执行完成 //40000
能够看到当4个线程所有执行完毕以后,变量 i 成功的累加了 40000 次,没有出现丢失操做的状况。code
若是咱们将 main() 方法修改以下:对象
public static void main(String[] args) throws InterruptedException { Thread t1 = new ThreadDemo(); Thread t2 = new ThreadDemo(); Thread t3 = new ThreadDemo(); Thread t4 = new ThreadDemo(); t1.start(); t2.start(); t3.start(); t4.start(); t1.join(); t2.join(); t3.join(); t4.join(); System.out.println(i); } //输出结果: //执行完成 //执行完成 //执行完成 //执行完成 //27579
能够看到丢失了很多的累加操做。观察先后两个 main() 方法建立线程的方式能够发现,前面的 main() 方法是使用了同一个对象来建立了4个不一样的线程,然后一个 main() 方法使用了4个不一样的 ThreadDemo 对象建立了4个线程。咱们用 synchronized 修饰的是一个非静态成员函数,至关于对该方法建立了 this 的对象锁。在第一个 main() 方法中使用同一个对象来建立 4 个不一样线程就会让 4 个线程争夺同一个对象锁,这样,在同一时间内,仅能有一个线程能访问 synchronized 修饰的方法。而在第二种 main() 方法中,4 个线程各自对应一个对象锁,4 个线程之间没有竞争关系,对象锁天然没法生效。继承
public class ThreadDemo extends Thread { @Override public void run() { synchronized (ThreadDemo.class) { for (int i = 0; i < 10000; i++) { Main.i++; } System.out.println("执行完成"); } } }
将修饰方法的 synchronized 改成对 ThreadDemo.class 上锁的代码块
public class ThreadDemo2 extends Thread { @Override public void run() { synchronized (ThreadDemo2.class) { for (int i = 0; i < 10000; i++) { Main.i++; } System.out.println("执行完成"); } } }
再建立一个相同的类命名为 ThreadDemo2 ,与 ThreadDemo 不一样的是,ThreadDemo2 中,synchronized 对 ThreadDemo2.class 上锁。
public static void main(String[] args) throws InterruptedException { Thread t1 = new ThreadDemo(); Thread t2 = new ThreadDemo(); Thread t3 = new ThreadDemo2(); Thread t4 = new ThreadDemo2(); t1.start(); t2.start(); t3.start(); t4.start(); t1.join(); t2.join(); t3.join(); t4.join(); System.out.println(i); } //输出结果: //执行完成 //执行完成 //执行完成 //执行完成 //33054
4 个线程分别由 ThreadDemo 和 ThreadDemo2 来建立,显然获得的结果与预期的 40000 不符。若是咱们将 ThreadDemo2 中的 synchronized 改成对 ThreadDemo.class 上锁:
public class ThreadDemo2 extends Thread { @Override public void run() { synchronized (ThreadDemo.class) { for (int i = 0; i < 10000; i++) { Main.i++; } System.out.println("执行完成"); } } } //输出结果: //执行完成 //执行完成 //执行完成 //执行完成 //40000
能够看到,虽然是声明在两个不一样的类中的 synchronized 代码块,可是因为都是对 ThreadDemo.class 上锁,因此 4 个线程之间仍是创建了竞争关系,同时只能有一个线程访问被 synchronized 修饰的代码。
因此 synchronized 关键字的本质是限制线程访问一段代码,而限制的条件就是,在全部被加上相同锁的代码上,同一时间,只能有一个线程在运行。这与你要修改什么样的共享变量无关。在我刚接触到的时候觉得类锁和对象锁是分别针对静态共享变量和非静态共享变量的,但事实上锁的是要执行的代码块,而不是代码块将要访问的共享变量。