Java练习——多线程
第一题
编写多线程应用程序,模拟多个人通过一次山洞的模拟.这个山洞每次只能通过一个人,每一个人通过山洞的时间为5秒,随机生成10个人,同时准备过此山洞,显示一下每次通过山洞人的姓名.
思路分析
- 定义了一个类继承Thread。这意味着Test1是一个线程类。
- 重写了run方法。这是线程的主要执行方法。当线程启动时,它会执行这个方法。
- 在run方法中,使用synchronized关键字来同步代码块
- 当线程进入同步代码块时,会先打印当前线程的名字和"正在过山洞…“。然后,线程会休眠5秒。休眠结束后,再打印当前线程的名字和"通过山洞!”。
完整代码
public class Test1 extends Thread{
Lock lock = new Lock();
@Override
public void run(){
synchronized (lock){
System.out.println(Thread.currentThread().getName()+"正在过山洞...");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"通过山洞!");
}
}
public static void main(String[] args) {
Test1 test1 = new Test1();
new Thread(test1,"刘一").start();
new Thread(test1,"陈二").start();
new Thread(test1,"张三").start();
new Thread(test1,"李四").start();
new Thread(test1,"王五").start();
new Thread(test1,"赵六").start();
new Thread(test1,"孙七").start();
new Thread(test1,"周八").start();
new Thread(test1,"吴九").start();
new Thread(test1,"郑十").start();
}
}
运行结果
第二题
编写一个程序,模拟3个售票窗口,来卖10张票,要求一张票只能卖一次
思路分析
- 定义一个类继承Thread,表示这是一个线程类;定义一个私有整数 ticket,表示当前剩余的票数,初始值为10。创建一个 Lock 对象
lock,用于同步。 - 线程执行逻辑 (run 方法):在循环内部,使用 synchronized (lock)
来确保同一时间只有一个线程可以检查剩余票数和卖票。如果 ticket 大于0,表示还有票,那么线程会打印出卖出的票数和已售空的票数,然后ticket 减1。之后线程休眠1秒(模拟售票间隔),然后继续循环。 - 创建了三个线程对象,分别命名为 “窗口1”、“窗口2” 和 “窗口3”,并启动它们。这意味着有三个窗口同时尝试售票。
完整代码
public class Test3 extends Thread {
private int ticket = 10;
Lock lock = new Lock();
@Override
public void run() {
while (true){
synchronized (lock) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了第" + ticket + "张票");
System.out.println("第" + ticket + "张票已售空!");
ticket--;
}
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Test3 test3 = new Test3();
new Thread(test3, "窗口1").start();
new Thread(test3, "窗口2").start();
new Thread(test3, "窗口3").start();
}
}
运行结果
总结
实现多线程有三种方式
- 继承Thread类(用得不多)
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("执行了线程的run方法_");
}
}
public class Test {
public static void main(String[] args) {
MyThread mta = new MyThread();
MyThread mtb = new MyThread();
mta.start();
mtb.start();
}
}
- 实现Runnable接口:多线程类实现Runnable接口后,还是需要Thread类下的start()方法来启动线程
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("我的线程...");
}
}
public class Test {
public static void main(String[] args) {
MyRunnable mra = new MyRunnable();
MyRunnable mrb = new MyRunnable();
new Thread(mra).start();
new Thread(mrb).start();
}
}
注意:
- 启动多线程必须是通过线程类的对象来调用start()方法。不能直接调用run()方法,如果直接调用run()则仍然是单线程,没有启动多线程。
- 线程不允许重复启动
-
实现Callable接口
- 创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
- 创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
- 使用FutureTask对象作为Thread对象的target创建并启动新线程。
- 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值