参考:
文章出处于:【Java并发编程之深入理解】Synchronized的使用
以下内容都经过实验。
在并发编程中存在线程安全问题,主要原因有:1.存在共享数据 2.多线程共同操作共享数据。关键字synchronized可以保证在同一时刻,只有一个线程可以执行某个方法或某个代码块,同时synchronized可以保证一个线程的变化可见(可见性),即可以代替volatile。
synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性。
可以用于解决Java并发问题,可以确保线程互斥的访问同步代码。
synchronized的使用 Java中每个对象都可以作为锁,这是synchronized实现同步的基础。
使用方法:
普通同步方法:锁是当前实例对象。当进入该对象的一个同步方法,则不能同时再进入该对象的另一个同步方法,可以进入非同步方法。
静态同步方法:锁是当前类,即class对象,进入同步代码前需要获得当前类锁。
同步方法块:所示括号里的对象,对给定对象加锁,进入同步代码块之前要获得该对象的对象锁。
synchronized的特质和缺点 实践 多个线程访问同一个对象的同一个方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 public class JavaSynchronizedTest implements Runnable { static int i = 0 ; public synchronized void increaseSync () { i++; } public void increase () { i++; } @Override public void run () { for (int j = 0 ; j < 10000 ; j++) { increaseSync(); } } public static void main (String[] args) throws InterruptedException { JavaSynchronizedTest test = new JavaSynchronizedTest(); Thread t1 = new Thread(test); Thread t2 = new Thread(test); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(i); } }
一个线程获取了该对象的锁之后,其他线程来访问其他synchronized实例方法现象 一个获取了对象锁后,其他线程就需要等该线程释放该对象锁之后,再由其他线程调用同步方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package javatest;public class JavaSynchronizedTest implements Runnable { public synchronized void methodSync1 () { System.out.println("Method 1 start" ); try { System.out.println("Method 1 execute" ); Thread.sleep(3000 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 1 end" ); } public synchronized void methodSync2 () { System.out.println("Method 2 start" ); try { System.out.println("Method 2 execute" ); Thread.sleep(1000 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 2 end" ); } public static void main (String[] args) throws InterruptedException { new Thread(test::methodSync1).start(); new Thread(test::methodSync2).start(); } }
一个线程获取了该对象的锁之后,其他线程来访问其他非synchronized实例方法现象 一个线程获取对象所进入同步方法后,依旧可以直接调用非同步方法,不需要锁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 public class JavaSynchronizedTest implements Runnable { public synchronized void methodSync1 () { System.out.println("Method 1 start" ); try { System.out.println("Method 1 execute" ); Thread.sleep(3000 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 1 end" ); } public synchronized void methodSync2 () { System.out.println("Method 2 start" ); try { System.out.println("Method 2 execute" ); Thread.sleep(1000 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 2 end" ); } public synchronized void methodSync3 () { System.out.println("Method 3 start" ); try { System.out.println("Method 3 execute" ); Thread.sleep(1000 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 3 end" ); } @Override public void run () { for (int j = 0 ; j < 10000 ; j++) { increase(); } } public static void main (String[] args) throws InterruptedException { JavaSynchronizedTest test = new JavaSynchronizedTest(); new Thread(test::methodSync1).start(); new Thread(test::methodSync2).start(); new Thread(test::methodSync1).start(); new Thread(test::methodSync3).start(); } }
当多个线程作用于不同的对象 不同对象的同步方法,互不影响,因为他们各自的对象锁不一样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 package javatest;public class JavaSynchronizedTest implements Runnable { public synchronized void methodSync1 () { System.out.println("Method 1 start" ); try { System.out.println("Method 1 execute" ); Thread.sleep(3000 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 1 end" ); } public synchronized void methodSync2 () { System.out.println("Method 2 start" ); try { System.out.println("Method 2 execute" ); Thread.sleep(1000 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 2 end" ); } public static void main (String[] args) throws InterruptedException { JavaSynchronizedTest test = new JavaSynchronizedTest() JavaSynchronizedTest test2 = new JavaSynchronizedTest(); new Thread(test::methodSync1).start(); new Thread(test2::methodSync2).start(); } }
synchronized作用于静态方法 静态方法的锁是类锁,一个线程进入静态方法之后,另一个线程需要等待该线程执行完毕才可以进入。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 package javatest;public class JavaSynchronizedTest implements Runnable { static int i = 0 ; public synchronized void increaseSync () { i++; } public void increase () { i++; } public static synchronized void increaseStatic () { i++; } @Override public void run () { for (int j = 0 ; j < 10000 ; j++) { increaseStatic(); } } public static void main (String[] args) throws InterruptedException { JavaSynchronizedTest test = new JavaSynchronizedTest(); JavaSynchronizedTest test2 = new JavaSynchronizedTest(); Thread t1 = new Thread(test); Thread t2 = new Thread(test2); t1.start(); t2.start(); Thread.sleep(2000 ); System.out.println(i); } }
synchronized作用于同步代码块 在某些情况下,我们编写的方法体可能比较大,同时存在一些比较耗时的操作,而需要同步的代码又只有一小部分,如果直接对整个方法进行同步操作,可能会得不偿失。此时我们可以使用同步代码块的方式对需要同步的代码进行包裹,这样就无需对整个方法进行同步操作了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 public class JavaSynchronizedTest implements Runnable { static int i = 0 ; public void increase () { i++; } @Override public void run () { synchronized (lock) { for (int j = 0 ; j < 10000 ; j++) { increase(); } } } public static void main (String[] args) throws InterruptedException { JavaSynchronizedTest test2 = new JavaSynchronizedTest(); Thread tt1 = new Thread(test2); Thread tt2 = new Thread(test2); tt1.start(); tt2.start(); tt1.join(); tt2.join(); System.out.println(i); } }