多线程学习(七)线程的同步-同步方法

转载 2016年06月01日 10:58:31
Java线程:线程的同步-同步方法
线程的同步是保证多线程安全访问竞争资源的一种手段。
线程的同步是Java多线程编程的难点,往往开发者搞不清楚什么是竞争资源、什么时候需要考虑同步,怎么同步等等问题,当然,这些问题没有很明确的答案,但有些原则问题需要考虑,是否有竞争资源被同时改动的问题?
 
在本文之前,请参阅《Java线程:线程的同步与锁》,本文是在此基础上所写的。
 
对于同步,在具体的Java代码中需要完成一下两个操作:
把竞争访问的资源标识为private;
同步哪些修改变量的代码,使用synchronized关键字同步方法或代码。
当然这不是唯一控制并发安全的途径。
 
synchronized关键字使用说明
synchronized只能标记非抽象的方法,不能标识成员变量。
 
为了演示同步方法的使用,构建了一个信用卡账户,起初信用额为100w,然后模拟透支、存款等多个操作。显然银行账户User对象是个竞争资源,而多个并发操作的是账户方法oper(int x),当然应该在此方法上加上同步,并将账户的余额设为私有变量,禁止直接访问。
 
 
/** 
* Java线程:线程的同步 

* @author leizhimin 2009-11-4 11:23:32 
*/
 
public class Test { 
        public static void main(String[] args) { 
                User u = new User("张三", 100); 
                MyThread t1 = new MyThread("线程A", u, 20); 
                MyThread t2 = new MyThread("线程B", u, -60); 
                MyThread t3 = new MyThread("线程C", u, -80); 
                MyThread t4 = new MyThread("线程D", u, -30); 
                MyThread t5 = new MyThread("线程E", u, 32); 
                MyThread t6 = new MyThread("线程F", u, 21); 

                t1.start(); 
                t2.start(); 
                t3.start(); 
                t4.start(); 
                t5.start(); 
                t6.start(); 
        } 


class MyThread extends Thread { 
        private User u; 
        private int y = 0; 

        MyThread(String name, User u, int y) { 
                super(name); 
                this.u = u; 
                this.y = y; 
        } 

        public void run() { 
                u.oper(y); 
        } 


class User { 
        private String code; 
        private int cash; 

        User(String code, int cash) { 
                this.code = code; 
                this.cash = cash; 
        } 

        public String getCode() { 
                return code; 
        } 

        public void setCode(String code) { 
                this.code = code; 
        } 

        /** 
         * 业务方法 
         * @param x 添加x万元 
         */
 
        public synchronized void oper(int x) { 
                try { 
                        Thread.sleep(10L); 
                        this.cash += x; 
                        System.out.println(Thread.currentThread().getName() + "运行结束,增加“" + x + "”,当前用户账户余额为:" + cash); 
                        Thread.sleep(10L); 
                } catch (InterruptedException e) { 
                        e.printStackTrace(); 
                } 
        } 

        @Override 
        public String toString() { 
                return "User{" + 
                                "code='" + code + '\'' + 
                                ", cash=" + cash + 
                                '}'; 
        } 
}
 
输出结果:
线程A运行结束,增加“20”,当前用户账户余额为:120 
线程F运行结束,增加“21”,当前用户账户余额为:141 
线程E运行结束,增加“32”,当前用户账户余额为:173 
线程C运行结束,增加“-80”,当前用户账户余额为:93 
线程B运行结束,增加“-60”,当前用户账户余额为:33 
线程D运行结束,增加“-30”,当前用户账户余额为:3 

Process finished with exit code 0
 
 
反面教材,不同步的情况,也就是去掉oper(int x)方法的synchronized修饰符,然后运行程序,结果如下:
线程A运行结束,增加“20”,当前用户账户余额为:61 
线程D运行结束,增加“-30”,当前用户账户余额为:63 
线程B运行结束,增加“-60”,当前用户账户余额为:3 
线程F运行结束,增加“21”,当前用户账户余额为:61 
线程E运行结束,增加“32”,当前用户账户余额为:93 
线程C运行结束,增加“-80”,当前用户账户余额为:61 

Process finished with exit code 0
 
很显然,上面的结果是错误的,导致错误的原因是多个线程并发访问了竞争资源u,并对u的属性做了改动。
 
可见同步的重要性。
 
 
注意:
通过前文可知,线程退出同步方法时将释放掉方法所属对象的锁,但还应该注意的是,同步方法中还可以使用特定的方法对线程进行调度。这些方法来自于java.lang.Object类。
 
void notify()    
                    唤醒在此对象监视器上等待的单个线程。    
void notifyAll()    
                    唤醒在此对象监视器上等待的所有线程。    
void wait()    
                    导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。    
void wait(long timeout)    
                    导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量。    
void wait(long timeout, int nanos)    
                    导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量。
 
结合以上方法,处理多线程同步与互斥问题非常重要,著名的生产者-消费者例子就是一个经典的例子,任何语言多线程必学的例子。
问题解答:
Q:帐户操作的时候,为什么要先睡10毫秒?操作完以后还要再睡10毫秒?这与实际业务不符吧?
A:先睡10ms是为了保证让其他的线程能得到cpu等资源然后可以运行,不然的话没法模拟现实情况。现实中oper()方法里面的步骤很多,譬如权限验证、数据计算啊等,而deomo中不可能出现这么多操作,所以为了模拟,需要sleep。如果不sleep,可能线程a在执行oper时需要的时间很少执行完后,线程b才开始执行,这样的话,oper()方法加不加synchronized关键字就没啥区别了。



【多线程】【同步】【等待唤醒机制】【JDK1.5多线程】【线程优先级&常见方法】

一、多线程基本概念 1、进程:是一个正在执行中的程序。                每一个进程执行都有一个执行的顺序,该顺序是一个执行路径或者叫一个控制单元。 2、线程:就是进程中的一个独立的控制单...

[Java]Java多线程数据安全(同步线程的方法)

预备知识Thread.yield():API中解释: 暂停当前正在执行的线程对象,并执行其他线程。 注意:这里的其他也包含当前线程,即,当前线程也能够再次抢占CPU。Thread.sleep(long...

黑马程序员-多线程(创建线程、方法、同步、通信)总结

---------------------- android培训、java培训、期待与您交流! ---------------------- 一、多线程 进程:运行中的程序; 线程:进程可以有多...

马士兵-多线程学习第04课 线程的同步

1.思考为什么会有线程的同步?线程不同步会带来哪些问题?        我们首先要来了解一个例子,假设我有一张银行卡和存折共享里面的钱,假设有3000块钱,有一天我去银行拿着存折去取钱,到了柜台我取...
  • hblfyla
  • hblfyla
  • 2016年11月01日 22:06
  • 631

Python多线程学习(二、线程的同步)

假设两个线程对象t1和t2都要对num=0进行增1运算,t1和t2都各对num修改10次,num的最终的结果应该为20。但是由于是多线程访问,有可能出现下面情况:在num=0时,t1取得num=0。系...

IOS多线程系统学习之线程同步与线程通信

多线程编程是有趣的事情,它很容易突然出现“错误情况”,这是由于系统的线程调度具有一定的随机性造成的。不过,即使程序偶然出现“错误情况”,这是由于系统的线程调度具有一定的随机性造成的。不过,即使程序偶然...

线程概念,状态及状态之间的关系,实现多线程方法,实现同步线程的方式

1.线程概念:是进程中的一个执行控制单元,执行路径;一个进程中至少有一个线程在负责控制程序称为单线程;一个进程中有多个执行路径时,这个程序称为多线程 2.状态:就绪,运行,synchronize阻塞...

3种多线程实现同步方法

  • 2013年04月03日 22:31
  • 2KB
  • 下载

Java线程学习笔记(二)---多线程同步方法

同步方法额可以控制对类成员变量的访问。每个类的实例对象都对应这一把锁,每个同步方法都必须在获得该类实例对象的锁才能执行,方法一旦被执行,就独占该锁,而其他的线程只能等待,知道从该方法返回时才将锁释放。...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:多线程学习(七)线程的同步-同步方法
举报原因:
原因补充:

(最多只允许输入30个字)