多线程的同步问题指的是多个线程同时修改一个数据的时候,可能致使的问题
多线程的问题,又叫Concurrency 问题java
假设盖伦有10000滴血,而且在基地里,同时又被对方多个英雄攻击,就是有多个线程在减小盖伦的hp,同时又有多个线程在恢复盖伦的hp
,假设线程的数量是同样的,而且每次改变的值都是1,那么全部线程结束后,盖伦应该仍是10000滴血。
咱们编一下运行试一下:web
package charactor; public class Hero{ public String name; public float hp; public int damage; //回血 public void recover(){ hp=hp+1; } //掉血 public void hurt(){ hp=hp-1; } }
package multiplethread; import charactor.Hero; public class TestThread3 { public static void main(String[] args) { final Hero gareen = new Hero(); gareen.name = "盖伦"; gareen.hp = 10000; System.out.printf("盖伦的初始血量是 %.0f%n", gareen.hp); //假设盖伦有10000滴血,而且在基地里,同时又被对方多个英雄攻击 //用JAVA代码来表示,就是有多个线程在减小盖伦的hp //同时又有多个线程在恢复盖伦的hp int n = 10000; Thread[] addThreads = new Thread[n]; Thread[] reduceThreads = new Thread[n]; //n个线程增长盖伦的hp for (int i = 0; i < n; i++) { Thread t = new Thread(){ public void run(){ gareen.recover(); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; t.start(); addThreads[i] = t; } //n个线程减小盖伦的hp for (int i = 0; i < n; i++) { Thread t = new Thread(){ public void run(){ gareen.hurt(); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; t.start(); reduceThreads[i] = t; } for (Thread t : addThreads) { try { t.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } for (Thread t : reduceThreads) { try { t.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //代码执行到这里,全部增长和减小线程都结束了 //增长和减小线程的数量是同样的,每次都增长,减小1. //那么全部线程都结束后,盖伦的hp应该仍是初始值 //可是事实上观察到的是: System.out.printf("%d个增长线程和%d个减小线程结束后%n盖伦的血量变成了 %.0f%n", n,n,gareen.hp); } }
整体解决思路是: 在增长线程访问hp期间,其余线程不能够访问hp安全
解决上述问题以前,先理解synchronized关键字的意义
以下代码:多线程
Object someObject =new Object(); synchronized (someObject){ //此处的代码只有占有了someObject后才能够执行 }
synchronized表示当前线程,独占 对象 someObject,当前线程独占 了对象someObject,若是有其余线程试图占有对象someObject,就会等待,直到当前线程释放对someObject的占用。someObject 又叫同步对象,全部的对象,均可以做为同步对象,为了达到同步的效果,必须使用同一个同步对象,释放同步对象的方式: synchronized 块天然结束,或者有异常抛出
svg
package multiplethread; import java.text.SimpleDateFormat; import java.util.Date; public class TestThread { public static String now(){ return new SimpleDateFormat("HH:mm:ss").format(new Date()); } public static void main(String[] args) { final Object someObject = new Object(); Thread t1 = new Thread(){ public void run(){ try { System.out.println( now()+" t1 线程已经运行"); System.out.println( now()+this.getName()+ " 试图占有对象:someObject"); synchronized (someObject) { System.out.println( now()+this.getName()+ " 占有对象:someObject"); Thread.sleep(5000); System.out.println( now()+this.getName()+ " 释放对象:someObject"); } System.out.println(now()+" t1 线程结束"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; t1.setName(" t1"); t1.start(); Thread t2 = new Thread(){ public void run(){ try { System.out.println( now()+" t2 线程已经运行"); System.out.println( now()+this.getName()+ " 试图占有对象:someObject"); synchronized (someObject) { System.out.println( now()+this.getName()+ " 占有对象:someObject"); Thread.sleep(5000); System.out.println( now()+this.getName()+ " 释放对象:someObject"); } System.out.println(now()+" t2 线程结束"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; t2.setName(" t2"); t2.start(); } }
全部须要修改hp的地方,有要创建在占有someObject的基础上。
而对象 someObject在同一时间,只能被一个线程占有。 间接地,致使同一时间,hp只能被一个线程修改。ui
package multiplethread; import java.awt.GradientPaint; import charactor.Hero; public class TestThread { public static void main(String[] args) { final Object someObject = new Object(); final Hero gareen = new Hero(); gareen.name = "盖伦"; gareen.hp = 10000; int n = 10000; Thread[] addThreads = new Thread[n]; Thread[] reduceThreads = new Thread[n]; for (int i = 0; i < n; i++) { Thread t = new Thread(){ public void run(){ //任何线程要修改hp的值,必须先占用someObject synchronized (someObject) { gareen.recover(); } try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; t.start(); addThreads[i] = t; } for (int i = 0; i < n; i++) { Thread t = new Thread(){ public void run(){ //任何线程要修改hp的值,必须先占用someObject synchronized (someObject) { gareen.hurt(); } try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; t.start(); reduceThreads[i] = t; } for (Thread t : addThreads) { try { t.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } for (Thread t : reduceThreads) { try { t.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.printf("%d个增长线程和%d个减小线程结束后%n盖伦的血量是 %.0f%n", n,n,gareen.hp); } }
package charactor; public class Hero{ public String name; public float hp; public int damage; //回血 public void recover(){ synchronized (this) { hp=hp+1; } } //掉血 public void hurt(){ //使用this做为同步对象 synchronized (this) { hp=hp-1; } } }
package multiplethread; import java.awt.GradientPaint; import charactor.Hero; public class TestThread { public static void main(String[] args) { final Object someObject = new Object(); final Hero gareen = new Hero(); gareen.name = "盖伦"; gareen.hp = 10000; int n = 10000; Thread[] addThreads = new Thread[n]; Thread[] reduceThreads = new Thread[n]; for (int i = 0; i < n; i++) { Thread t = new Thread(){ public void run(){ gareen.recover(); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; t.start(); addThreads[i] = t; } for (int i = 0; i < n; i++) { Thread t = new Thread(){ public void run(){ gareen.hurt(); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; t.start(); reduceThreads[i] = t; } for (Thread t : addThreads) { try { t.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } for (Thread t : reduceThreads) { try { t.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.printf("%d个增长线程和%d个减小线程结束后%n盖伦的血量是 %.0f%n", n,n,gareen.hp); } }
在recover前,直接加上synchronized ,其所对应的同步对象,就是this
和hurt方法达到的效果是同样
外部线程访问gareen的方法,就不须要额外使用synchronized 了this
package charactor; public class Hero{ public String name; public float hp; public int damage; //回血 //直接在方法前加上修饰符synchronized //其所对应的同步对象,就是this //和hurt方法达到的效果同样 public synchronized void recover(){ hp=hp+1; } //掉血 public synchronized void hurt(){ hp=hp-1; }
package multiplethread; import java.awt.GradientPaint; import charactor.Hero; public class TestThread { public static void main(String[] args) { final Hero gareen = new Hero(); gareen.name = "盖伦"; gareen.hp = 10000; int n = 10000; Thread[] addThreads = new Thread[n]; Thread[] reduceThreads = new Thread[n]; for (int i = 0; i < n; i++) { Thread t = new Thread(){ public void run(){ //recover自带synchronized gareen.recover(); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; t.start(); addThreads[i] = t; } for (int i = 0; i < n; i++) { Thread t = new Thread(){ public void run(){ //hurt自带synchronized gareen.hurt(); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; t.start(); reduceThreads[i] = t; } for (Thread t : addThreads) { try { t.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } for (Thread t : reduceThreads) { try { t.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.printf("%d个增长线程和%d个减小线程结束后%n盖伦的血量是 %.0f%n", n,n,gareen.hp); } }
若是一个类,其方法都是有synchronized修饰的,那么该类就叫作线程安全的类
同一时间,只有一个线程可以进入 这种类的一个实例 的去修改数据,进而保证了这个实例中的数据的安全(不会同时被多线程修改而变成脏数据)spa
好比StringBuffer和StringBuilder的区别
StringBuffer的方法都是有synchronized修饰的,StringBuffer就叫作线程安全的类
而StringBuilder就不是线程安全的类
线程