synchronized关键字的详细分析和代码实例
在Java中,一般都是通过同步机制来解决线程安全问题的,在JDK 5.0之后又新增了Lock的方式来实现线程安全。所以说实现线程安全方式一共有三种方法
方式一:
synchronized(同步监视器){
//需要被同步的代码(操作共享数据的代码就是需要被同步的代码)
}
通过继承Thread类的方式实现多线程并解决线程安全问题:
1 package com.baozi.java; 2 3 public class WinowTest { 4 public static void main(String[] args){ 5 Window w1=new Window(); 6 Window w2=new Window(); 7 Window w3=new Window(); 8 w1.start(); 9 w2.start(); 10 w3.start(); 11 } 12 } 13 //通过继承Thread类的方法来实现多线程 14 class Window extends Thread { 15 private static int ticket = 100; 16 @Override 17 public void run() { 18 while (true) { 19 //这里通过synchronized代码块的形式来实现线程安全 20 synchronized (Window.class) { 21 if (ticket > 0) { 22 try { 23 Thread.sleep(100); 24 } catch (InterruptedException e) { 25 e.printStackTrace(); 26 } 27 System.out.println(Thread.currentThread().getName() + ":" + ticket--); 28 } else { 29 break; 30 } 31 } 32 } 33 34 } 35 }
通过实现Runnable接口的方式实现多线程并解决线程安全问题:
1 package com.baozi.java; 2 3 public class WindowTest2 { 4 public static void main(String[] args){ 5 Window2 window2 = new Window2(); 6 Thread t1 = new Thread(window2); 7 Thread t2 = new Thread(window2); 8 t1.setName("线程1"); 9 t2.setName("线程2"); 10 t1.start(); 11 t2.start(); 12 } 13 14 } 15 //通过实现Runnable接口的方式实现多线程 16 class Window2 implements Runnable{ 17 private int ticket = 100; 18 @Override 19 public void run() { 20 while (true) { 21 //这里通过synchronized代码块的形式来实现线程安全 22 synchronized (this) { 23 if (ticket > 0) { 24 try { 25 Thread.sleep(100); 26 } catch (InterruptedException e) { 27 e.printStackTrace(); 28 } 29 System.out.println(Thread.currentThread().getName() + ":" + ticket--); 30 } else { 31 break; 32 } 33 } 34 } 35 36 } 37 }
备注:
同步监视器:其实就是我们平时说的锁,任何一个类对象都可以充当同步监视器,并且针对需要操作的共享数据,要求多个线程必须共享同一个同步监视器,这样才能实现多线程共享数据的线程安全。
同步方法解决了线程安全问题,但是在操作同步代码块的时候,实质上某一时刻只有一个线程拥有锁,其他线程需要操作共享数据的时候只能等待该线程释放了所之后通过抢占方式获得锁之后才能执行。言外之意针对共享数据其实是单线程执行,这样会造成效率非常低。
方式二:
如果多个线程操作的共享数据的代码完整的声明在一个方法中,那么我们使用synchronized关键字修饰这个方法为同步方法。
通过继承Thread类的方式实现多线程并解决线程安全问题:
1 package com.baozi.java; 2 3 public class WindowTest3 { 4 public static void main(String[] args) { 5 Window3 w1 = new Window3(); 6 Window3 w2 = new Window3(); 7 Window3 w3 = new Window3(); 8 w1.start(); 9 w2.start(); 10 w3.start(); 11 } 12 } 13 14 //通过继承Thread类的方法来实现多线程 15 class Window3 extends Thread { 16 private static int ticket = 100; 17 18 @Override 19 public void run() { 20 while (true) { 21 show(); 22 } 23 } 24 25 //这里通过synchronized方法的形式来实现线程安全 26 public static synchronized void show() { 27 if (ticket > 0) { 28 try { 29 Thread.sleep(100); 30 } catch (InterruptedException e) { 31 e.printStackTrace(); 32 } 33 System.out.println(Thread.currentThread().getName() + ":" + ticket--); 34 } 35 } 36 }
通过实现Runnable接口的方式实现多线程并解决线程安全问题:
1 package com.baozi.java; 2 3 public class WindowTest2 { 4 public static void main(String[] args) { 5 Window2 window2 = new Window2(); 6 Thread t1 = new Thread(window2); 7 Thread t2 = new Thread(window2); 8 t1.setName("线程1"); 9 t2.setName("线程2"); 10 t1.start(); 11 t2.start(); 12 } 13 } 14 //通过实现Runnable接口的方式实现多线程 15 class Window2 implements Runnable { 16 private int ticket = 100; 17 18 @Override 19 public synchronized void run() { 20 while (true) { 21 if (ticket > 0) { 22 try { 23 Thread.sleep(100); 24 } catch (InterruptedException e) { 25 e.printStackTrace(); 26 } 27 System.out.println(Thread.currentThread().getName() + ":" + ticket--); 28 } else { 29 break; 30 } 31 } 32 } 33 }
备注:同步方法依然会涉及到同步监视器,只是不需要我么显示的声明。
非静态的同步方法:同步监视器可以是this(当前对象)
静态的同步方法:同步监视器可以是当前类
这样我们就能省去单独造一个对象来充当同步监视器,使代码比较简单整洁。
方式三:
使用Lock锁的方式实现线程安全,这是JDK5.0新增加的方法,在后边单独分析。