synchronized是进行同步处理而保证线程安全。在一个方法中,若是是方法内的私有变量,那个这个变量是线程安全的,可是类中的实例变量是可能会出现线程安全问题的,当多个线程对这个实例变量进行修改,就可能会出现结果并非咱们指望的结果。java
下面一段代码就出现了线程安全问题。
原本当username为a的时候,num应该为100,可是因为设置让t1休眠了2秒,致使num被刷新成了200,致使最后输出时a和b的num都是200。安全
public class Service { private int num = 0; public void add(String username) { try { if (username.equals("a")) { num = 100; Thread.sleep(2000); } else { num = 200; } System.out.println(username + " " + num); } catch (InterruptedException e) { e.printStackTrace(); } } } public class ThreadA extends Thread { private Service service; public ThreadA(Service service) { this.service = service; } @Override public void run() { super.run(); service.add("a"); } } public class ThreadB extends Thread { private Service service; public ThreadB(Service service) { this.service = service; } @Override public void run() { super.run(); service.add("b"); } } public class Test { public static void main(String[] args) { Service service = new Service(); ThreadA t1 = new ThreadA(service); ThreadB t2 = new ThreadB(service); t1.start(); t2.start(); } }
运行结果:
异步
下面给add方法加个synchronized关键字。
能够看到输出正确。
synchronized进行了同步,使得线程按照顺序进行访问,因为线程t1和t2的监视器都是同一个实例,至关于拥有同一个锁对象,因此能够进行同步访问。ide
public class Service { private int num = 0; public synchronized void add(String username) { try { if (username.equals("a")) { num = 100; Thread.sleep(2000); } else { num = 200; } System.out.println(username + " " + num); } catch (InterruptedException e) { e.printStackTrace(); } } }
输出结果:
性能
上面的代码中是使用synchronized将整个方法进行上锁,只有当一个方法执行完毕后,另外一个线程才能够执行这个方法,这样会致使性能损耗很大。
返回到线程安全问题,目的实际上是为了解决对临界资源访问的问题,因此其实只须要将临界资源进行上锁就能够了,其余部分实际上是能够异步进行的。this
在下面代码中,doSomeTask方法里前半部分没有进行同步,后面使用了同步代码块进行加锁。
从输出结果能够看到,前面部分A、B两个线程是异步进行访问的,后部分是同步进行访问的。线程
public class Service { public void doSomeTask(String username) { for (int i = 0; i < 5; i++) { System.out.println("没有同步 " + Thread.currentThread().getName() + " " + i); } System.out.println(); synchronized (this) { for (int i = 0; i < 5; i++) { System.out.println("同步了" + Thread.currentThread().getName() + " " + i); } } } } public class ThreadA extends Thread { private Service service; public ThreadA(Service service) { this.service = service; } @Override public void run() { super.run(); service.doSomeTask("a"); } } public class ThreadB extends Thread { private Service service; public ThreadB(Service service) { this.service = service; } @Override public void run() { super.run(); service.doSomeTask("b"); } } public class Test { public static void main(String[] args) { Service service = new Service(); ThreadA t1 = new ThreadA(service); ThreadB t2 = new ThreadB(service); t1.setName("A"); t2.setName("B"); t1.start(); t2.start(); } }
运行结果:
code
因此使用synchronized同步代码块能够将须要加锁的部分进行上锁就好了,这样能够提升性能。
可使用synchronized同步代码块加锁临界资源,这样就能够避免出现线程安全问题。
在JDK1.8中的ConcurrentHashMap也使用synchronized锁,synchronized锁的性能已经有了很大的提升。对象
static方法为类方法,那么对static方法加上synchronized锁后呢?加锁的到底是什么呢?
其实这时候监视器上锁的对象为这个类对象,而不是一个具体的实例对象,就是全部该类的实例访问这个方法都会进行加锁。blog
从下面实例能够看出,虽然是两个实例四个线程访问该方法,可是仍是进行了同步,由于全部实例访问的是同一把锁,也就是Service类的对象锁,只要是监视器锁对象是同一个,那么都是会进行上锁同步的。
public class Service { public static synchronized void doSomeTask(String username) { for (int i = 0; i < 5; i++) { System.out.println("同步了" + Thread.currentThread().getName() + " " + i); } } } public class ThreadA extends Thread { private Service service; public ThreadA(Service service) { this.service = service; } @Override public void run() { super.run(); service.doSomeTask("a"); } } public class ThreadB extends Thread { private Service service; public ThreadB(Service service) { this.service = service; } @Override public void run() { super.run(); service.doSomeTask("b"); } } public class Test { public static void main(String[] args) { Service service = new Service(); Service service1 = new Service(); ThreadA t1 = new ThreadA(service); ThreadB t2 = new ThreadB(service); ThreadA t3 = new ThreadA(service1); ThreadB t4 = new ThreadB(service1); t1.setName("A"); t2.setName("B"); t3.setName("C"); t4.setName("D"); t1.start(); t2.start(); t3.start(); t4.start(); } }
运行结果:
使用同步代码块也能够实现上述功能。
在同步代码块中,将监视的锁对象设置为Service.class,表明监视的是这个类锁,因此也对这个类进行加锁同步。
public class Service { public void doSomeTask(String username) { synchronized (Service.class) { for (int i = 0; i < 5; i++) { System.out.println("同步了" + Thread.currentThread().getName() + " " + i); } } } }
四个线程访问两个方法,线程A和C访问task1,是一个普通的synchronized方法,线程B和D访问task2,是一个static synchronized方法。
从输出结果中能够看出,A、C进行了同步,B、D也进行同步,可是B和C在一开始是交替出现输出,表明B和C其实没有同步,就证实它们的锁不是同一把锁。
A和C线程获取的实例的对象锁,而B和D线程获取的是这个类的锁。
public class Service { public synchronized void task1() { for (int i = 0; i < 5; i++) { System.out.println("task1 " + Thread.currentThread().getName() + " " + i); } } public static synchronized void task2() { for (int i = 0; i < 5; i++) { System.out.println("task2 " + Thread.currentThread().getName() + " " + i); } } } public class ThreadA extends Thread { private Service service; public ThreadA(Service service) { this.service = service; } @Override public void run() { super.run(); service.task1(); } } public class ThreadB extends Thread { private Service service; public ThreadB(Service service) { this.service = service; } @Override public void run() { super.run(); service.task2(); } } public class Test { public static void main(String[] args) { Service service = new Service(); ThreadA t1 = new ThreadA(service); ThreadB t2 = new ThreadB(service); ThreadA t3 = new ThreadA(service); ThreadB t4 = new ThreadB(service); t1.setName("A"); t2.setName("B"); t3.setName("C"); t4.setName("D"); t1.start(); t2.start(); t3.start(); t4.start(); } }
运行结果:
综上: