线程同步关键字 synchronized

在java的多线程编程中多个线程操作同一数据时会出现数据错误,主要原因是多个线程同时处理数据时同时获取了数据,但是有些线程没有来得及操作数据,然后另一个线程获取到了之前的值,然后引起数据异常

具体代码如下:

public class SynchronizedMethod {
    public static void main(String[] args) {
        MoneyMethod moneyMethod = new MoneyMethod();
        for (int i = 0; i < 5; i++) {
            // 使用lamdba表达式实现runable接口
            Thread t1 = new Thread(()-> {moneyMethod.addMoney();}, "t1"+i);
            t1.start();
        }
        for (int i = 0; i < 5; i++) {
            Thread t2 = new Thread(new MyThread(moneyMethod), "t2"+i);
            t2.start();
        }
        
    }
}

class MyThread implements Runnable {
    MoneyMethod moneyMethod ;
    /**
     * 
     */
    public MyThread(MoneyMethod moneyMethod) {
        // TODO Auto-generated constructor stub
        this.moneyMethod = moneyMethod;
    }
    public void run() {
        moneyMethod.subMoney();
    }
}

class MoneyMethod {
    int money =20;
    public void addMoney() {
        getMoney();
        money++;
    }
    public  void subMoney() {
        getMoney();
        money--;
    }
    public  void getMoney() {
        System.out.println(Thread.currentThread().getName()+":::getMoney:::"+money);
    }
}

结果值如下:

t10:::getMoney:::20
t11:::getMoney:::21
t13:::getMoney:::21
t14:::getMoney:::22
t12:::getMoney:::24
t23:::getMoney:::24
t22:::getMoney:::24
t24:::getMoney:::23
t21:::getMoney:::22
t20:::getMoney:::21

为了避免这种错误,在java中使用关键字 synchronized 来处理操作

包括两种操作:在方法前修饰、在代码块前修饰

方法前修饰如下:

public class SynchronizedMethod {
    public static void main(String[] args) {
        MoneyMethod moneyMethod = new MoneyMethod();
        for (int i = 0; i < 5; i++) {
            // 使用lamdba表达式实现runable接口
            Thread t1 = new Thread(()-> {moneyMethod.addMoney();}, "t1"+i);
            t1.start();
        }
        for (int i = 0; i < 5; i++) {
            Thread t2 = new Thread(new MyThread(moneyMethod), "t2"+i);
            t2.start();
        }
        
    }
}

class MyThread implements Runnable {
    MoneyMethod moneyMethod ;
    /**
     * 
     */
    public MyThread(MoneyMethod moneyMethod) {
        // TODO Auto-generated constructor stub
        this.moneyMethod = moneyMethod;
    }
    public void run() {
        moneyMethod.subMoney();
    }
}

class MoneyMethod {
    int money =20;
    public synchronized void addMoney() {
        getMoney();
        money++;
    }
    public synchronized void subMoney() {
        getMoney();
        money--;
    }
    public synchronized void getMoney() {
        System.out.println(Thread.currentThread().getName()+":::getMoney:::"+money);
    }
}

结果如下:

t10:::getMoney:::20
t13:::getMoney:::21
t12:::getMoney:::22
t11:::getMoney:::23
t14:::getMoney:::24
t20:::getMoney:::25
t21:::getMoney:::24
t22:::getMoney:::23
t23:::getMoney:::22
t24:::getMoney:::21

请注意,构造函数无法同步 - 使用synchronized带有构造函数关键字是语法错误,而且各个实例对象的同步方法之间不会相互影响,在静态方法上添加synchronized会影响所有的实例对象

但是在同步方法中有缺陷,就是如果对象的所有方法都是同步方法,则锁都是当前对象,所有如果不是操作同一数据,会影响效率。在java中可以使用同步代码块来处理这样的问题

同步方法的代码如下:

public class SynchronizedCodeBlock {
    public static void main(String[] args) {
        MoneyMethod2 moneyMethod = new MoneyMethod2();
        for (int i = 0; i < 5; i++) {
            // 使用lamdba表达式实现runable接口
            Thread t1 = new Thread(() -> {
            
                moneyMethod.addMoney();
            }, "t1" + i);
            t1.start();
        }

        for (int i = 0; i < 5; i++) {
            Thread t2 = new Thread(new MyThread2(moneyMethod), "t2" + i);
            t2.start();
        }
        
        for (int i = 0; i < 5; i++) {
            // 使用lamdba表达式实现runable接口
            Thread t1 = new Thread(() -> {
                
                moneyMethod.addMoney2();
            }, "t3" + i);
            t1.start();
        }

        for (int i = 0; i < 5; i++) {
            Thread t2 = new Thread(new MyThread3(moneyMethod), "t4" + i);
            t2.start();
        }

    }
}

class MyThread2 implements Runnable {
    MoneyMethod2 moneyMethod;

    /**
     * 
     */
    public MyThread2(MoneyMethod2 moneyMethod) {
        // TODO Auto-generated constructor stub
        this.moneyMethod = moneyMethod;
    }

    public void run() {
        
        moneyMethod.subMoney();
    }
}

class MyThread3 implements Runnable {
    MoneyMethod2 moneyMethod;

    /**
     * 
     */
    public MyThread3(MoneyMethod2 moneyMethod) {
        // TODO Auto-generated constructor stub
        this.moneyMethod = moneyMethod;
    }

    public void run() {
    
        moneyMethod.subMoney2();
    }
}

class MoneyMethod2 {
    int money = 100;
    int money2 = 600;
    Object lock1 = new Object();
    Object lock2 = new Object();

    public synchronized void addMoney() {
//         (lock1) {
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            getMoney();
            money++;
//        }

    }

    public synchronized void subMoney() {
//        synchronized (lock1) {
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            getMoney();
            money--;
//        }
    }

    public synchronized void addMoney2() {
//        synchronized (lock2) {
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            getMoney2();
            money2++;
//        }
    }
    public synchronized void subMoney2() {
//        synchronized (lock2) {
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            getMoney2();
            money2--;
//        }
    }

    public void getMoney() {
        System.out.println(Thread.currentThread().getName() + ":::getMoney:::" + money+":::"+new Date().getTime());
    }

    public void getMoney2() {
        System.out.println(Thread.currentThread().getName() + ":::getMoney2:::" + money2+":::"+new Date().getTime());
    }
}

 

执行结果:

t10:::getMoney:::100:::1540435235076
t44:::getMoney2:::600:::1540435239076
t43:::getMoney2:::599:::1540435243076
t42:::getMoney2:::598:::1540435247076
t41:::getMoney2:::597:::1540435251076
t40:::getMoney2:::596:::1540435255077
t34:::getMoney2:::595:::1540435259077
t33:::getMoney2:::596:::1540435263077
t32:::getMoney2:::597:::1540435267078
t31:::getMoney2:::598:::1540435271078
t30:::getMoney2:::599:::1540435275079
t24:::getMoney:::101:::1540435279080
t23:::getMoney:::100:::1540435283080
t22:::getMoney:::99:::1540435287080
t21:::getMoney:::98:::1540435291080
t20:::getMoney:::97:::1540435295080
t13:::getMoney:::96:::1540435299080
t14:::getMoney:::97:::1540435303080
t11:::getMoney:::98:::1540435307081
t12:::getMoney:::99:::1540435311082

使用了同步代码块的代码:

public class SynchronizedCodeBlock {
    public static void main(String[] args) {
        MoneyMethod2 moneyMethod = new MoneyMethod2();
        for (int i = 0; i < 5; i++) {
            // 使用lamdba表达式实现runable接口
            Thread t1 = new Thread(() -> {
            
                moneyMethod.addMoney();
            }, "t1" + i);
            t1.start();
        }

        for (int i = 0; i < 5; i++) {
            Thread t2 = new Thread(new MyThread2(moneyMethod), "t2" + i);
            t2.start();
        }
        
        for (int i = 0; i < 5; i++) {
            // 使用lamdba表达式实现runable接口
            Thread t1 = new Thread(() -> {
                
                moneyMethod.addMoney2();
            }, "t3" + i);
            t1.start();
        }

        for (int i = 0; i < 5; i++) {
            Thread t2 = new Thread(new MyThread3(moneyMethod), "t4" + i);
            t2.start();
        }

    }
}

class MyThread2 implements Runnable {
    MoneyMethod2 moneyMethod;

    /**
     * 
     */
    public MyThread2(MoneyMethod2 moneyMethod) {
        // TODO Auto-generated constructor stub
        this.moneyMethod = moneyMethod;
    }

    public void run() {
        
        moneyMethod.subMoney();
    }
}

class MyThread3 implements Runnable {
    MoneyMethod2 moneyMethod;

    /**
     * 
     */
    public MyThread3(MoneyMethod2 moneyMethod) {
        // TODO Auto-generated constructor stub
        this.moneyMethod = moneyMethod;
    }

    public void run() {
    
        moneyMethod.subMoney2();
    }
}

class MoneyMethod2 {
    int money = 100;
    int money2 = 600;
    Object lock1 = new Object();
    Object lock2 = new Object();

    public  void addMoney() {
        synchronized(lock1) {
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            getMoney();
            money++;
        }

    }

    public void subMoney() {
        synchronized (lock1) {
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            getMoney();
            money--;
        }
    }

    public void addMoney2() {
        synchronized (lock2) {
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            getMoney2();
            money2++;
        }
    }
    public void subMoney2() {
        synchronized (lock2) {
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            getMoney2();
            money2--;
        }
    }

    public void getMoney() {
        System.out.println(Thread.currentThread().getName() + ":::getMoney:::" + money+":::"+new Date().getTime());
    }

    public void getMoney2() {
        System.out.println(Thread.currentThread().getName() + ":::getMoney2:::" + money2+":::"+new Date().getTime());
    }
}

结果:

t10:::getMoney:::100:::1540435584843
t30:::getMoney2:::600:::1540435584844
t23:::getMoney:::101:::1540435588844
t44:::getMoney2:::601:::1540435588845
t24:::getMoney:::100:::1540435592844
t43:::getMoney2:::600:::1540435592845
t22:::getMoney:::99:::1540435596844
t42:::getMoney2:::599:::1540435596846
t20:::getMoney:::98:::1540435600845
t41:::getMoney2:::598:::1540435600847
t21:::getMoney:::97:::1540435604845
t40:::getMoney2:::597:::1540435604847
t14:::getMoney:::96:::1540435608846
t34:::getMoney2:::596:::1540435608848
t13:::getMoney:::97:::1540435612846
t33:::getMoney2:::597:::1540435612848
t11:::getMoney:::98:::1540435616847
t31:::getMoney2:::598:::1540435616849
t12:::getMoney:::99:::1540435620847
t32:::getMoney2:::599:::1540435620849

从以上两种情况可以发现使用了同步代码块后两个不同锁的代码块可以异步执行

 

posted @ 2018-10-25 10:49  vstarcui  阅读(1054)  评论(0编辑  收藏  举报