Java----线程同步
案例:卖票
要求:某电影院正在上映国产大片,它有100张票,而该电影院有三个窗口卖票,请设计一个程序模拟该电影院卖票
思路:
① 定义一个类SellTicket来实现Runnable接口,里面定义个变量
② 重写Run()方法,步骤如下:
A:若票数大于0,就卖票,并说明是哪个窗口在卖
B:卖了票后,票数要减一
C:没票了,但肯定还有人来问,所以设置一个死循环
③ 定义一个测试类SellTicketDemo,里面有main()方法,步骤如下:
A:创建SellTicket对象
B:创建三个Thread对象,将SellTicket对象作为构造方法的参数,并给出名称
C:启动线程
卖票出现的问题:
- 相同票出现了多次
- 出现了负数的票
问题的原因:
- 线程的随机性导致
卖票案例数据安全问题的解决
为什么出现问题?(这也是我们判断多线程程序是否会有数据安全问题的标准)
- 是否是多线程环境
- 是否有共享数据
- 是否有多条语句操作共享数据(切入点)
(如果这三条都是的话,那么肯定出现数据安全性问题)
如何解决多线程数据安全问题?
- 基本思想:让程序没有安全问题的环境(破坏掉上面三个条件中的一个)
如何实现?
- 把多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可
- 利用java提供的同步代码块解决
同步代码块:
锁住多条语句(线程)操作共享数据,可以使用同步代码块来实现
- 格式:
synchronized(任意对象){
多条语句(线程)操作共享数据的代码
}
- synchronized(任意对象) 相当于给代码块加锁了,任意对象相当于是锁
同步的好处和弊端
- 好处:解决了多线程数据安全问题
- 弊端:当线程很多时,因为每个线程都要去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率
同步方法
同步普通方法:
概念:就是把synchronized关键字加到方法上
- 格式:
修饰符 synchronized 返回值类型 方法名(方法参数){ }
同步普通方法的锁对象是什么呢?
- this
同步静态方法:
概念:就是把synchronized关键字加到静态方法上
-
格式
修饰符 static synchronized 返回值类型 方法名(方法参数){ }
同步静态方法的锁对象是什么呢?
- 类名.class
线程安全的类
StringBuffer==StringBuilder
Vector==ArrayList
Hashtable<K,V>==Hashmap<K,V>
以上左边三者均为线程安全的类,区别是:
- 左边可实现线程安全,右边不行
- 右边在执行相同操作时,更快,因为它们不执行同步
lock锁
由于我们不理解同步代码块和同步方法在哪里上的锁(synchronized)和在哪里释放的锁,所以JDK5新增一个锁对象Lock
Lock比synchronized方法和语句有更多更广泛的锁定操作
Lock中提供了锁和释放锁的方法:
- void lock( ) :获得锁
- void unlock( ) :释放锁
Lock是接口,不能直接实例化,这里采用它的实现类ReentrantLock来实例化:
ReentrantLock的构造方法:
- ReentrantLock():创建一个ReentrantLock的实例
注意:
-
一般使用try{
lock.lock();
要锁的代码块
}
finally{
lock.unlock();
}
这个方法来用lock锁
生产者与消费者模式
一个经典的多线程协作的模式。
- 生产者线程用于生产数据
- 消费者线程用于消费数据
为了解耦生产者与消费者之间的关系,通常建立一个共享的数据区域,就像是一个仓库
为了体现生产与消费过程中的等待与唤醒,Java提供了几个方法供我们使用:
Object类的等待与唤醒方法:
方法名 | 作用 |
---|---|
void wait() | 将导致当前线程等待(执行一次),直到另一个线程调用该对象的notify()或者notifyAll()方法 |
void notify() | 唤醒正在等待对象监视器的单个线程 |
void notifyAll() | 唤醒正在等待对象监视器的所有线程 |
生产者与消费者案例
生产者与消费者案例包含的类:
- 奶箱类(Box):定义一个成员变量,表示第X瓶奶,提供存储牛奶和获取牛奶的炒作
- 生产者类(Producer):实现Runnable接口,重写run()方法,调用存储牛奶操作
- 消费者类(Customer):实现Runnable接口,重写run()方法,调用获取牛奶操作
- 测试类(BoxDemo):main()方法:
步骤:
①创建奶箱对象,共用区域
②创建生产者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用存储牛奶操作
③创建消费者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用获取牛奶操作
④创建两个线程对象,分别把消费者和生产者对象作为构造参数传递
⑤启动线程
代码实现:
标签:同步,Java,synchronized,对象,lock,----,线程,方法 From: https://www.cnblogs.com/change624/p/16793019.html