同步
同步的问题
当给狗狗食物的同时,狗狗又在吃,这会导致在运行过程中会出现食物的数据的错乱,有时候会多出数据,有时候会少出数据,这就让狗狗有时候会很吃亏,那么该如何解决呢?
实验体现
package multiThread2;
public class Animal{
private String name;
private int year;
Animal(){}
private int food;
public Animal(String name,int year,int food){
this.name = name;
this.year = year;
this.food = food;
}
@Override
public String toString() {
return "姓名:"+ name + ",年龄:" + year + ",来干饭了,还剩"+ food + "个食物";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getFood() {
return food;
}
public void setFood(int food) {
this.food = food;
}
public void eatFood(){
this.food--;
}
public void addFood(){
this.food++;
}
}
package multiThread2;
public class main {
public static void main(String[] args) throws InterruptedException {
Animal a2 = new Animal("旺财",3,200);
System.out.println(a2.getName()+"原本的食物有"+a2.getFood());
Thread[] addThread = new Thread[200];
Thread[] reduceThread = new Thread[200];
// n个线程增加狗的食物
for (int i = 0; i < 200; i++) {
Thread t = new Thread(() -> {
a2.addFood();
try{
Thread.sleep(100);
}catch (Exception e){
e.printStackTrace();
}
});
t.start();
addThread[i]=t;
}
// n个线程减少狗的食物
for (int i = 0; i < 200; i++) {
Thread t = new Thread(() ->{
a2.eatFood();
try {
Thread.sleep(100);
}catch (Exception e){
e.printStackTrace();
}
});
t.start();
reduceThread[i]=t;
}
// 等待所有增加线程的结束
for(Thread t: addThread){
try {
t.join();
}catch (Exception e){
e.printStackTrace();
}
}
for (Thread t: reduceThread) {
try {
t.join();
}catch (Exception e){
e.printStackTrace();
}
}
System.out.println(a2.getName()+"的食物变成了"+a2.getFood()+"个");
}
}
同步出现问题的原因
增加食物和减少食物是同时发生的事件,这会导致在增加食物的反应时间的同时,减少食物的情况正在发生,来不及增加食物,这会导致食物结果减少,反之亦然。
解决方案
在增加食物这一个线程发生的过程中,其他线程禁止访问
- 增加线程获取到食物数据,并进行运算
- 在运算期间,减少线程试图来访问食物数据,但是不被允许
- 在增加线程执行完毕之后,减少线程才能访问食物数据
- 减少数据运行完毕
synchronized 同步对象概念
Object someObject = new Object();
synchronized(someObject){
}
- synchronized表示当前线程,独占对象someObject当前线程独占 了对象someObject,如果有其他线程试图占有对象someObject,就会等待,直到当前线程释放对someObject的占用。
- someObject 又叫同步对象,所有的对象,都可以作为同步对象
为了达到同步的效果,必须使用同一个同步对象 - 释放同步对象的方式: synchronized 块自然结束,或者有异常抛出
package multiThread2;
public class Animal{
private String name;
private int year;
Animal(){}
private int food;
public Animal(String name,int year,int food){
this.name = name;
this.year = year;
this.food = food;
}
@Override
public String toString() {
return "姓名:"+ name + ",年龄:" + year + ",来干饭了,还剩"+ food + "个食物";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getFood() {
return food;
}
public void setFood(int food) {
this.food = food;
}
public void eatFood(){
this.food--;
}
public void addFood(){
this.food++;
}
}
package multiThread2;
public class main {
public static void main(String[] args) throws InterruptedException {
Animal a2 = new Animal("旺财", 3, 200);
System.out.println(a2.getName() + "原本的食物有" + a2.getFood());
Thread[] addThread = new Thread[200];
Thread[] reduceThread = new Thread[200];
Object someObject = new Object();
// n个线程增加狗的食物
// for (int i = 0; i < 5; i++) {
Thread t1 = new Thread(() -> {
a2.addFood();
try {
System.out.println("t1试图进行");
System.out.println("t1试图占有someObject对象");
synchronized (someObject) {
System.out.println("t1占有someObject对象");
Thread.sleep(100);
System.out.println("t1释放someObject对象");
}
System.out.println("t1结束");
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
});
t1.start();
// addThread[i] = t;
// }
// n个线程减少狗的食物
// for (int i = 0; i < 5; i++) {
Thread t2 = new Thread(() -> {
a2.eatFood();
try {
System.out.println("t2试图进行");
System.out.println("t2试图占有someObject对象");
synchronized (someObject) {
System.out.println("t2占有someObject对象");
Thread.sleep(100);
System.out.println("t2释放someObject对象");
}
System.out.println("t2结束");
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
});
t2.start();
// reduceThread[i]=t;
// }
// 等待所有增加线程的结束
// for(Thread t: addThread){
// try {
// t.join();
// }catch (Exception e){
// e.printStackTrace();
// }
// }
// for (Thread t: reduceThread) {
// try {
// t.join();
// }catch (Exception e){
// e.printStackTrace();
// }
// }
// System.out.println(a2.getName()+"的食物变成了"+a2.getFood()+"个");
}
}
在方法前加一个修饰符synchronized
此时主函数可以不用加入synchronized
线程安全的类
如果一个类,它的方法都是有synchronized修饰的,那么该类就是线程安全的类