7、卖票案例:同步代码块
1、锁多条语句操作共享数据,可以使用同步代码块实现
格式: synchronized(任意对象){
多条语句操作共享数据的代码
}
2、synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁
优点:解决多线程的数据安全问题
缺点:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率
/*卖票案例
* 需求:某电影院有100张票,三个窗口卖票,设计一个程序模拟该电影院卖票
* 思路:
* 1.定义一个sellTicket类实现Runnable接口,里面定义一个成员变量:private int ticket = 100;
* 2.在sellTicket类中重写run()实现卖票,代码步骤如下:
* A.判定票数大于0就卖票,并告知是哪个窗口卖的
* B.卖了票之后,总票数要减1
* C.票卖完了,也有可能有人来问余票,所以这里用死循环让卖票的动作一直执行
* 3.定义一个测试类sellTicketDemo,里面有main方法,代码步骤如下:
* A.创建sellTicket类的对象
* B.创建三个Thread类对象,把sellTicket对象作为构造方法的参数,并给出对应的窗口名称
* C.启动线程
* */
public class SellTicketDemo {
public static void main(String[] args) {
sellTicket st = new sellTicket();
//创建三个对象
Thread t1 = new Thread(st,"NO1");
Thread t2 = new Thread(st,"NO2");
Thread t3 = new Thread(st,"NO3");
//启动线程
t1.start();
t2.start();
t3.start();
}
}
ublic class sellTicket implements Runnable {
private int ticket = 100;
private Object obj = new Object(); //定义在外面就是三个对象使用同一把锁
@Override
public void run() {
while(true) {
//使用同步锁保证三个窗口不会出售同一张票
synchronized (obj) {
if (ticket > 0) {
//模拟出票时间
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + ticket + "张票");
ticket--;
}
}
}
}
}
8、同步方法
1、格式:修饰符 synchronized 返回值类型 方法名(方法参数){ }
2、同步方法的锁对象是:this
3、同步静态方法格式:修饰符 static synchronized 返回值类型 方法名(方法参数){ }
4、同步静态方法的锁对象是:. 类名.class
/*卖票案例
* 需求:某电影院有100张票,三个窗口卖票,设计一个程序模拟该电影院卖票
* 思路:
* 1.定义一个sellTicket类实现Runnable接口,里面定义一个成员变量:private int ticket = 100;
* 2.在sellTicket类中重写run()实现卖票,代码步骤如下:
* A.判定票数大于0就卖票,并告知是哪个窗口卖的
* B.卖了票之后,总票数要减1
* C.票卖完了,也有可能有人来问余票,所以这里用死循环让卖票的动作一直执行
* 3.定义一个测试类sellTicketDemo,里面有main方法,代码步骤如下:
* A.创建sellTicket类的对象
* B.创建三个Thread类对象,把sellTicket对象作为构造方法的参数,并给出对应的窗口名称
* C.启动线程
* */
public class SellTicketDemo {
public static void main(String[] args) {
sellTicket st = new sellTicket();
//创建三个对象
Thread t1 = new Thread(st,"NO1");
Thread t2 = new Thread(st,"NO2");
Thread t3 = new Thread(st,"NO3");
//启动线程
t1.start();
t2.start();
t3.start();
}
}
public class sellTicket implements Runnable {
private static int ticket = 100;
private Object obj = new Object(); //定义在外面就是三个对象使用同一把锁
private int x = 0;
@Override
public void run() {
while(true) {
if(x%2 == 0) {
//使用同步锁保证三个窗口不会出售同一张票
synchronized (sellTicket.class) {
if (ticket > 0) {
//模拟出票时间
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + ticket + "张票");
ticket--;
}
}
}else{
sellTicket();
}
x++;
}
}
private static synchronized void sellTicket() {
if (ticket > 0) {
//模拟出票时间
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + ticket + "张票");
ticket--;
}
}
}
9、 线程同步
1、 StringBuffer(线程安全):被StringBuilder(线程不安全)替代
2、 Vector(线程安全):被ArrayList(线程不安全)替代
3、 Hashtable(线程安全):被HashMap(线程不安全)替代
public class ThreadDemo1 {
public static void main(String[] args) {
//线程安全
StringBuffer sb1 = new StringBuffer();
//线程不安全
StringBuilder sb2 = new StringBuilder();
//线程安全
Vector<String> v = new Vector<String>();
//线程不安全
ArrayList<String> arr = new ArrayList<String>();
//线程安全
Hashtable<String,String> ht = new Hashtable<String,String>();
//线程不安全
HashMap<String,String> hm = new HashMap<String,String>();
//调用synchronizedList()这个方法就把集合包装成了一个线程安全的集合类
List<String> list = Collections.synchronizedList(new ArrayList<String>());
}
}
10、 Lock锁
1、 获得锁:void lock();
2、 释放锁:void unlock();
注:Lock是接口不能直接实例化,采用它的实现类ReentranLock来实例化
ReentranLock无参构造方法:ReentranLock()创建一个ReentranLock实例
public class sellTicket implements Runnable{
private int tickets = 100;
private Lock lock = new ReentrantLock();
@Override
public void run() {
while(true){
//一般情况会把加锁、解锁的动作放在try{...}finally{...}代码块中
try{
lock.lock(); //加锁
if(tickets>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+tickets+"张票");
tickets--;
}
}finally {
lock.unlock(); //解锁
}
}
}
}
public class SellTicketDemo {
public static void main(String[] args) {
sellTicket st = new sellTicket();
Thread t1 = new Thread(st,"窗口1");
Thread t2 = new Thread(st,"窗口2");
Thread t3 = new Thread(st,"窗口3");
t1.start();
t2.start();
t3.start();
}
}
11、生产者消费者
Object类的等待和唤醒方法:
1. Void wait(): 导致当前线程等待,直到另一个线程调用该对象的notify()方法或者notifyAll()方法
2. Void notify(): 唤醒正在等待对象监视器的单个线程
3. Void notifyAll(): 唤醒正在等待对象监视器的所有线程
/*生产者、消费者案例
1.奶箱类(Box):定义一个成员变量,表示第X瓶奶,提供存储牛奶和获取牛奶的操作
2.生产者类(Producer):实现Runnable接口,重写run()方法,调用存储牛奶的操作
3.消费者类(Customer):实现Runnable接口,重写run()方法,调用获取牛奶的操作
4.测试类(BoxDemo):里面有main方法,main方法中的代码步骤如下:
A.创建奶箱对象,这是共享数据区域
B.创建生产者对象,把奶箱对箱作为构造方法的参数传递,因为在这个类中要调用存储牛奶的操作
C.创建消费者对象,把奶箱对箱作为构造方法的参数传递,因为在这个类中要调用获取牛奶的操作
D.创建2个线程对象,分别把生产者对象和消费者对象作为构造方法参数传递
E.启动线程
* */
public class BoxDemo {
public static void main(String[] args) {
//创建奶箱对象,这是共享数据区域
Box b = new Box();
//创建生产者对象,把奶箱对箱作为构造方法的参数传递,因为在这个类中要调用存储牛奶的操作
Producer p = new Producer(b);
//创建消费者对象,把奶箱对箱作为构造方法的参数传递,因为在这个类中要调用获取牛奶的操作
Customer c = new Customer(b);
//创建2个线程对象,分别把生产者对象和消费者对象作为构造方法参数传递
Thread t1 = new Thread(p);
Thread t2 = new Thread(c);
//启动线程
t1.start();
t2.start();
}
}
//奶箱类
public class Box {
//定义一个成员变量,表示第X瓶奶
private int milk;
//定义一个成员变量,表示奶箱的状态
private boolean state = false;
//存储牛奶的方法
public synchronized void put(int milk){
//如果有牛奶,等待消费,而不是生产
if (state){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果没有牛奶,就生产牛奶
this.milk = milk;
System.out.println("送奶工将第"+this.milk+"瓶奶放入奶箱");
//生产完毕之后,修改奶箱状态
state = true;
//唤醒其它等待的线程
notifyAll();
}
//获取牛奶的方法
public synchronized void get(){
//如果没有牛奶,就要等待生产
if(!state){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果有牛奶,就消费牛奶
System.out.println("用户拿到第"+this.milk+"瓶奶");
//消费完毕之后,修改奶箱状态
state = false;
notifyAll();
}
}
//生产者类
public class Producer implements Runnable{
private Box b;
public Producer(Box b) {
this.b = b;
}
@Override
public void run() {
//生产了5瓶
for (int i = 0; i <=5 ; i++) {
b.put(i);
}
}
}
//消费者类
public class Customer implements Runnable {
private Box b;
public Customer(Box b) {
this.b = b;
}
@Override
public void run() {
while(true){
b.get();
}
}
}