关于线程的练习题

练习1:三个售票窗口同时出售20张票

public class Sale extends Thread {
    
    
	private String name;
	private static int num = 20;
	private static Object lock = new Object();
	public Sale(String name) {
    
    
		this.name = name;
	}
	@Override
	public void run() {
    
    
		while (num > 0) {
    
    
			try {
    
    
				sleep(100);
			} catch (InterruptedException e) {
    
    
				e.printStackTrace();
			}
			synchronized (lock) {
    
    
				if (num > 0) {
    
    
					System.out.println(name + "售出第" + num + "张票...");
					num--;
				} else
					System.out.println("票已经售尽...");
			}
		}
	}

	public static void main(String[] args) {
    
    
		for (int i = 0; i < 3; i++) {
    
    
			new Sale("第" + (i + 1) + "个窗口").start();
		}
	}
}

练习2:两个人AB通过一个账户A在柜台取钱和B在ATM机取钱

分析:两个人AB实际上就是两个线程,操作同一个账号实际上为了避免出现问题则必须使用锁

public class Account {
    
    
	private double balance=10000;
	public synchronized void sub(double amount) {
    
    
		if (balance > amount) {
    
    
			try {
    
    
				Thread.sleep(1000);
			} catch (InterruptedException e) {
    
    
				e.printStackTrace();
			}
			balance = balance - amount;
		}
	}
}
public class Sub extends Thread {
    
    
	private Account account;
	private double amount;
	public Sub(Account a, double am) {
    
    
		this.account = a;
		this.amount = am;
	}
	@Override
	public void run() {
    
    
		account.sub(amount);
	}
}

练习3:过山洞问题:请按要求编写多线程应用程序,模拟多个人通过一个山洞:

1、这个山洞每次只能通过一个人,每个人通过山洞的时间为2秒;
2、随机生成10个人,同时准备过此山洞,并且定义一个变量用于记录通过人的信息

public class 山洞 {
    
    
	private StringBuffer logs = new StringBuffer();
	private int num=0;
	private int counter =0;
	private static final Object lock=new Object();
	public void 过山洞(){
    
    
		logs.append("第"+(++num)+"人"+Thread.currentThread().getName()+"到达山洞\n");
		synchronized (lock) {
    
    
			try {
    
    
				Thread.sleep(100);
			} catch (Exception e) {
    
    
				e.printStackTrace();
			}
			logs.append("第"+(++counter)+"人"+Thread.currentThread().getName()+"穿过山洞\n");
		}
	}
	public void show(){
    
    
		System.out.println(logs);
	}
}
public class Test山洞 {
    
    
	public static void main(String[] args) {
    
    
		String[] arr = new String[] {
    
     "Rose", "Black", "Tomos", "KKK", "AAA", "CCC" };
		Thread[] ts = new Thread[arr.length];
		Random r = new Random();
		山洞 shan = new 山洞();
		for (int i = 0; i < ts.length; i++) {
    
    
			String name = null;
			while (name == null) {
    
    
				int pos = r.nextInt(arr.length);
				String temp = arr[pos];
				if (temp != null) {
    
    
					name = temp;
					arr[pos] = null;
				}
			}
			ts[i] = new Thread(() -> {
    
    
				shan.过山洞();
			}, name);
			ts[i].start();
		}
		for (Thread temp : ts) {
    
    
			if (temp != null) {
    
    
				try {
    
    
					temp.join();
				} catch (InterruptedException e) {
    
    
					e.printStackTrace();
				}
			}
		}
		shan.show();
	}
}

练习4:模拟实现乌龟和兔子赛跑

总长2000米
规则:兔子每 0.1 秒 5 米的速度,每跑20米休息1秒
乌龟每 0.1 秒跑 2 米,不休息
当有一方到达终点时,另外一方立即终止。

public abstract class Animals extends Thread {
    
    
	public static final int 总长度 = 150;
	protected int 剩余长度 = 总长度;
	protected 结束接口 end = null;
	@Override
	public void run() {
    
    
		while (剩余长度 > 0)
			跑步();
	}
	public abstract void 跑步();
	static interface 结束接口 {
    
    
		void 结束();
	}
	public void setEnd(结束接口 end) {
    
    
		this.end = end;
	}	
}
public class Rabbit extends Animals {
    
    
	@Override
	public void 跑步() {
    
    
		int dis = 5;
		剩余长度 -= dis;
		System.out.println("兔子跑了" + dis + "米,距离终点还有" + 剩余长度 + "米");
		if (剩余长度 <= 0) {
    
    
			剩余长度 = 0;
			System.out.println("兔子胜利了");
			if (end != null) {
    
    
				end.结束();
			}
		}
		try {
    
    
			if ((总长度 - 剩余长度) % 50 == 0) {
    
    
				sleep(1100);
			} else
				sleep(100);
		} catch (Exception e) {
    
    
		}
	}
public class Tortoise extends Animals {
    
    
	@Override
	public void 跑步() {
    
    
		int dis = 2;
		剩余长度 -= dis;
		System.out.println("乌龟跑了" + dis + "米,距离终点还有" + 剩余长度 + "米");
		if (剩余长度 <= 0) {
    
    
			剩余长度 = 0;
			System.out.println("乌龟胜利了");
			if (end != null) {
    
    
				end.结束();
			}
		}
		try {
    
    
			sleep(100);
		} catch (Exception e) {
    
    
			e.printStackTrace();
		}
	}
}
public class Test {
    
    
	public static void main(String[] args) {
    
    
		Rabbit tuzi = new Rabbit();
		Tortoise wugui = new Tortoise();
		tuzi.setEnd(new 结束接口的实现类(wugui));
		wugui.setEnd(new 结束接口的实现类(tuzi));
		tuzi.start();
		wugui.start();
	}
}

建立三个线程,A线程打印10次A,B线程打印10次B,C线程打印10次C,要求线程同时运行,交替打印 10次ABC

public class T1 {
    
    
	public static void main(String[] args) throws Exception {
    
    
		Object a = 123;
		Object b = 456;
		Object c = 789;
		Thread aa = new MyThread("A", c, a);
		Thread bb = new MyThread("B", a, b);
		Thread cc = new MyThread("C", b, c);
		aa.start();
		Thread.sleep(100);
		bb.start();
		Thread.sleep(100);
		cc.start();
	}
}
class MyThread extends Thread {
    
    
	private String name;
	private Object prve;
	private Object self;
	public MyThread(String name, Object prve, Object self) {
    
    
		this.name = name;
		this.prve = prve;
		this.self = self;
	}
	@Override
	public void run() {
    
    
		int count = 20;
		while (count > 0) {
    
    
			synchronized (prve) {
    
    
				synchronized (self) {
    
    
					System.out.println(name);
					count--;
					self.notify();
				}
				try {
    
    
					prve.wait();
				} catch (InterruptedException e) {
    
    
					e.printStackTrace();
				}
			}
		}
	}
}

Lock的使用

Lock是java 1.5中引入的线程同步工具,它主要用于多线程下共享资源的控制。本质上Lock仅仅是一个接口,可以通过显式定义同步锁对象来实现同步,能够提供比synchronized更广泛的锁定操作,并支持多个相关的Condition对象

  • void lock();尝试获取锁,获取成功则返回,否则阻塞当前线程
    void lockInterruptibly() throws InterruptedException;尝试获取锁,线程在成功获取锁之前被中断,则放弃获取锁,抛出异常
    boolean tryLock();尝试获取锁,获取锁成功则返回true,否则返回false
    boolean tryLock(long time, TimeUnit unit)尝试获取锁,若在规定时间内获取到锁,则返回true,否则返回false,未获取锁之前被中断,则抛出异常
  • void unlock();释放锁
  • Condition newCondition();返回当前锁的条件变量,通过条件变量可以实现类似notify和wait的功能,一个锁可以有多个条件变量

Lock有三个实现类,一个是ReentrantLock,另两个是ReentrantReadWriteLock类中的两个静态内部类ReadLock和WriteLock。
使用方法:多线程下访问(互斥)共享资源时, 访问前加锁,访问结束以后解锁,解锁的操作推荐放入finally块中。
private final ReentrantLock lock=new ReentrantLock();
在具体方法中lock.lock() try{}finally{lock.unlock}

样例1:启动4个线程,对一个int数字进行各50次加减操作,要求2个加,2个减,保证输出的线程安全

public class OperNum {
    
    
	private int num = 0;
	private final static Lock lock = new ReentrantLock(); // 构建锁对象
	public void add() {
    
    
		try {
    
    
			lock.lock(); // 申请加锁操作,如果能加上则立即返回,否则阻塞当前线程
			num++;
			System.out.println(Thread.currentThread().getName() + "add..." + num);
		} finally {
    
    
			lock.unlock(); // 具体实现采用的是重入锁,所以持有锁的线程可以多次申请同一个锁,但是申请加锁次数必须和释放锁的次数一致
		}
	}
	public void sub() {
    
    
		try {
    
    
			lock.lock();
			num--;
			System.out.println(Thread.currentThread().getName() + "sub..." + num);
		} finally {
    
    
			lock.unlock();
		}
	}
}

写2个线程,其中一个打印1-52,另一个打印a-z,打印顺序应该是12a34b56c……5152线程1执行2次数后,线程2执行1次输出

public class T1 {
    
    
	private static final Lock lock = new ReentrantLock();
	private static final Condition conNum = lock.newCondition();
	private static final Condition conChar = lock.newCondition();
	private boolean flag = false;
	public void printNum() {
    
    
		try {
    
    
			lock.lock();
			for (int i = 1; i <= 52; i++) {
    
    
				while (flag)
					try {
    
    
						conNum.await();
					} catch (InterruptedException e) {
    
    
						e.printStackTrace();
					}
				System.out.print(i);
				if (i % 2 == 0) {
    
    
					flag = !flag;
					conChar.signal();
				}
			}
		} finally {
    
    
			lock.unlock();
		}
	}
	public void printChar() {
    
    
		try {
    
    
			lock.lock();
			for (int i = 'a'; i <= 'z'; i++) {
    
    
				while (!flag)
					try {
    
    
						conChar.await();
					} catch (InterruptedException e) {
    
    
						e.printStackTrace();
					}
				System.out.print((char) i);
				flag = !flag;
				conNum.signal();
			}
		} finally {
    
    
			lock.unlock();
		}
	}
	public static void main(String[] args) {
    
    
		T1 tt = new T1();
		new Thread(() -> {
    
    
			tt.printNum();
		}).start();
		new Thread(() -> {
    
    
			tt.printChar();
		}).start();
	}
}

猜你喜欢

转载自blog.csdn.net/Lecheng_/article/details/113621869