深圳大学-Java程序设计实验-线程应用

实验目的

掌握Java程序设计中的线程同步等技术。

实验内容

1

(1). 运行以下三个程序(每个程序运行10次),并对输出结果给出分析。在报告中附上程序截图和详细的文字说明。

程序1:

package problem1_1;
class PrintChar implements Runnable{
    private char charToPrint;
    private int times;
    public PrintChar(char c,int t){
        charToPrint=c;
        times=t;
    }
    @Override
    public void run(){
        for(int i=0;i<times;i++){
            System.out.print(charToPrint);
        }

    }
}
class PrintNum implements Runnable{
    private int lastNum;
    public PrintNum(int n){
        lastNum=n;
    }
    @Override
    public void run(){
        for(int i=1;i<=lastNum;i++){
            System.out.print(" "+i);
        }
    }
}
public class TaskThreadDemo {
    public static void main(String[] args) {
        Runnable printA=new PrintChar('a',100);
        Runnable printB=new PrintChar('b',100);
        Runnable print100=new PrintNum(100);

        Thread thread1=new Thread(printA);
        Thread thread2=new Thread(printB);
        Thread thread3=new Thread(print100);

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

运行结果截图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
运行10次,会发现每一次由于线程同步技术,会导致每一次输出都不一样。但是a、b的输出线程总能在数字线程前完成。当调大ab输出量时,比如次数调整为500时,会出现三个线程同时工作的场景,如下图所示。
在这里插入图片描述

程序2:

package problem1_2;
import java.util.concurrent.*;
class PrintChar implements Runnable{
    private char charToPrint;
    private int times;
    public PrintChar(char c,int t){
        charToPrint=c;
        times=t;
    }
    @Override
    public void run(){
        for(int i=0;i<times;i++){
            System.out.print(charToPrint);
        }

    }
}
class PrintNum implements Runnable{
    private int lastNum;
    public PrintNum(int n){
        lastNum=n;
    }
    @Override
    public void run(){
        for(int i=1;i<=lastNum;i++){
            System.out.print(" "+i);
        }
    }
}
public class ExecutorDemo {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(3);

        executor.execute(new PrintChar('a',500));
        executor.execute(new PrintChar('b',500));
        executor.execute(new PrintNum(500));

        executor.shutdown();
    }
}

运行结果截图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在main()方法中,首先通过Executors.newFixedThreadPool(3)创建一个固定大小为3的线程池,其中线程池中最多同时运行3个线程。然后,通过executor.execute()方法将三个任务提交给线程池执行。分别是两个PrintChar任务,一个打印字符’a’,一个打印字符’b’,每个任务打印100次;还有一个PrintNum任务,打印从1到100的数值。最后,调用executor.shutdown()方法关闭线程池。通过使用线程池和多个任务,可以并发地执行这些任务,提高程序的执行效率。运行效果与程序1类似,当调大ab输出量时,比如将次数调整为500时,会出现三个线程同时工作的场景,如下图所示。
在这里插入图片描述

程序3:

package problem1_3;
import java.util.concurrent.*;

public class AccountWithoutSync {
    private static Account account = new Account();

    public static void main(String[] args) {
        ExecutorService executor = Executors.newCachedThreadPool();
        for (int i = 0; i < 100; i++) {
            executor.execute(new AddAPennyTask());
        }
        executor.shutdown();
        while (!executor.isTerminated()) {
        }
        System.out.println("What is balance?" + account.getBalance());
    }

    private static class AddAPennyTask implements Runnable {
        public void run() {
            account.deposit(1);
        }
    }

    private static class Account {
        private int balance = 0;
        public int getBalance() {
            return balance;
        }
        public void deposit(int amount) {
            int newBalance = balance + amount;
            try {
                Thread.sleep(5);
            } catch (InterruptedException ex) {

            }
            balance = newBalance;
        }
    }
}

运行结果:
(十次运行结果):
在这里插入图片描述
在这里插入图片描述
首先,在 AccountWithoutSync 类中,有两个静态内部类:AddAPennyTask 类和Account 类。AddAPennyTask 类实现了 Runnable 接口,表示一个任务。在 run 方法中,调用了 account.deposit(1) 方法,向账户存入 1 个单位的金额。Account 类表示一个账户对象,它包含了一个 balance 变量用于保存账户的余额。getBalance 方法用于获取账户的余额,deposit 方法用于存入指定金额到账户。在 deposit 方法中,将原来的余额与存入的金额相加,然后通过 Thread.sleep(5) 方法模拟一段耗时操作,最后更新账户的余额。
在该类中,有一个静态变量 account,表示账户对象。在 main 方法中,创建了一个 ExecutorService 线程池对象 executor,通过 Executors.newCachedThreadPool() 方法创建一个可以根据需要自动调整线程数量的线程池。然后,使用一个循环向线程池提交了 100 个任务,每个任务都是一个 AddAPennyTask 对象,该任务会调用 account.deposit(1) 方法将 1 个单位的金额存入账户。接下来,调用 executor.shutdown() 方法关闭线程池的提交功能。之后,通过一个循环判断线程池是否已经终止执行,等待所有任务执行完成。最后,输出账户的余额,即调用 account.getBalance() 方法获取账户的余额,并打印输出。
结果分析:由于没有使用线程同步机制,所以多个线程可能会同时访问和修改账户的余额,导致并发问题和不确定的结果,所以输出结果是不确定的

2

编写Java应用程序实现如下功能:第一个线程输出数字1,2,…,12,第二个线程输出英文单词数字和月份One January, Two February, …, Twelve December,输出的顺序和格式为1OneJanuary2TwoFebruary…12TwelveDecember,即每1个数字紧跟着2个英文单词的方式。要求线程间实现通信。要求采用实现Runnable接口和Thread类的构造方法的方式创建线程,而不是通过Thread类的子类的方式。在报告中附上程序截图、运行结果截图和详细的文字说明。

package problem2;

public class Main{
    private static final Object lock = new Object();
    private static int cnt = 1;
    private static boolean number_huihe= true;
    public static class print_num implements Runnable {
        @Override
        public void run() {
            synchronized (lock) {
                try {
                    while (cnt<= 12) {
                        if (number_huihe) {
                            System.out.print(cnt);
                            number_huihe = false;
                            lock.notify();
                        } else {
                            lock.wait();
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static class print_word implements Runnable {
        private String[] numbers = {"One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve"};
        private String[] months = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};

        @Override
        public void run() {
            synchronized (lock) {
                try {
                    while (cnt <= 12) {
                        if (!number_huihe) {
                            System.out.print(numbers[cnt-1] + months[cnt-1]);
                            number_huihe = true;
                            cnt++;
                            lock.notify();
                        } else {
                            lock.wait();
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static void main(String[] args) {
        Thread numThread = new Thread(new print_num());
        Thread wordThread = new Thread(new print_word());

        numThread.start();
        wordThread.start();
    }
}

运行结果截图:
在这里插入图片描述
文字说明:
代码中定义了两个类 print_num 和 print_word,实现了 Runnable 接口。在 print_num 线程的 run 方法中,使用 synchronized (lock) 锁定了共享的 lock 对象。然后,在循环中判断 cnt 的值是否小于等于 12,如果是,则打印当前的数字 cnt,将 number_huihe (该变量用于确定当前是否是打印阿拉伯数字)置为 false,并通过 lock.notify() 唤醒print_word 线程,然后进入等待状态,然后执行print_word 线程。在 print_word 线程的 run 方法中,同样使用 synchronized (lock) 锁定了共享的 lock 对象。然后,在循环中判断 cnt 的值是否小于等于 12,如果是,则打印当前数字对应的英文单词和月份,并将 number_huihe 置为 true,递增 cnt,表示已经进行了一轮。然后通过 lock.notify() 唤醒 print_num 线程,并自己进入等待状态,以此不断循环,直至任务结束。在 main 方法中启动了两个线程,由此完成轮流打印数字和英文数字和英文月份。

3

编写Java应用程序实现如下功能:创建工作线程,模拟银行现金账户取款操作。多个线程同时执行取款操作时,如果不使用同步处理,会造成账户余额混乱,要求使用syncrhonized关键字同步代码块,以保证多个线程同时执行取款操作时,银行现金账户取款的有效和一致。要求采用实现Runnable接口和Thread类的构造方法的方式创建线程,而不是通过Thread类的子类的方式。在报告中附上程序截图、运行结果截图和详细的文字说明。

package problem3;
import java.util.Random;

class Account implements Runnable{
    private int balance;
    public Account(int b) {
        this.balance = b;
    }
    public int getBalance() {
        return balance;
    }
    public synchronized void withdraw(int amount) {
        System.out.println(Thread.currentThread().getName()+"正在取款");
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            System.out.println("当前余额为"+balance+"元");
            System.out.println("取款金额为"+amount+"元");
            System.out.println("剩余金额为"+balance+"元");
            System.out.println("---------------------");
            if(balance<10){
                System.out.println("剩余金额为"+balance+"元,"+"余额过少,暂停取款");
                System.exit(0);
            }
        }else{
            System.out.println("取款金额为"+amount+"元");
            System.out.println("当前余额为"+balance+"元");
            System.out.println("取款金额大于当前余额,取款失败");
            System.out.println("---------------------");
        }
    }
    @Override
    public void run(){
        while(true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Random r=new Random();
            int w=r.nextInt(66);
            withdraw(w);
        }
    }

}

public class Main {
    public static void main(String[] args) {
        Account account=new Account(666);
        Thread thread1=new Thread(account);
        Thread thread2=new Thread(account);
        Thread thread3=new Thread(account);

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

运行结果截图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
文字说明:
代码定义了一个Account类,实现了Runnable接口,getBalance函数用于返回余额,withdraw函数用于取款并打印取款时的信息,用synchronized关键字确保了同一时刻只有一个线程可以进行取款操作,run() 方法是接口 Runnable 的实现方法,在其中使用一个无限循环模拟不断进行取款操作的场景。在每次循环中,线程会休眠 1 秒钟,然后生成一个随机的取款金额,并调用 withdraw() 方法进行取款操作。在main方法里头,创建了一个Account对象,并启动三个线程,模拟多个线程并发地对同一个账户进行取款的场景,当余额低于10元的时候,终止程序。

4

有一座东西向的桥,只能容纳一个人,桥的东边有20个人(记为E1,E2,…,E20)和桥的西边有20个人(记为W1,W2,…,W20),编写Java应用程序让这些人到达对岸,每个人用一个线程表示,桥为共享资源,在过桥的过程中输出谁正在过桥(不同人之间用逗号隔开)。运行10次,分别统计东边和西边的20人先到达对岸的次数。要求采用实现Runnable接口和Thread类的构造方法的方式创建线程,而不是通过Thread类的子类的方式。在报告中附上程序截图、运行结果截图和详细的文字说明。

package problem4;

class Person implements Runnable {
    private String name;
    private int num;

    public Person(String name, int num) {
        this.name = name;
        this.num = num;
    }

    @Override
    public void run() {
        System.out.println(name + "正在过桥......");
        if(name.startsWith("E")){
            Main.east_cnt++;
        }
        else{
            Main.west_cnt++;
        }
        if (Main.east_cnt == 20) {
            Main.east_first_cnt++;
            Main.east_cnt = 0;
            Main.west_cnt = -1;
        } else if (Main.west_cnt == 20) {
            Main.west_first_cnt++;
            Main.west_cnt = 0;
            Main.east_cnt = -1;
        }
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

public class Main {
    public static int east_cnt = 0;
    public static int west_cnt = 0;
    public static int east_first_cnt = 0;
    public static int west_first_cnt = 0;
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            System.out.println("第" + (i + 1) + "次过桥:");
            Person[] east = new Person[20];
            Person[] west = new Person[20];

            Thread[] threadWest = new Thread[20];
            Thread[] threadEast = new Thread[20];

            for (int j = 0; j < 20; j++) {
                int index = j + 1;
                east[j] = new Person("E" + index, j + index);
                west[j] = new Person("W" + index, j + index);
                threadEast[j] = new Thread(east[j]);
                threadWest[j] = new Thread(west[j]);
            }

            for (int j = 0; j < 20; j++) {
                threadEast[j].start();
                threadWest[j].start();
            }

            for (int j = 0; j < 20; j++) {
                try {
                    threadEast[j].join();
                    threadWest[j].join();

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("----------------------");
        }
        System.out.println("东边先到达对岸的次数:" + east_first_cnt);
        System.out.println("西边先到达对岸的次数:" + west_first_cnt);
    }
}

运行结果截图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
文字说明:
该代码首先定义了Person类,实现了Runnable接口,具有名字,编号这两个属性,然后
实现了run()方法,用于模拟人过桥的行为。在run()方法中,首先打印出人的名字表示正在过桥,然后根据人的名字判断是来自东边还是西边,根据条件对Main类中的静态计数变量进行更新,通过Thread.sleep(100)模拟人过桥的时间。在Main类里面,具有四个静态变量,分别是east_cnt,west_cnt,east_first_cnt,west_first_cnt,人在过桥时,east_cnt,west_cnt用于记录当前过桥的人是东边还是西边的,当east_cnt或west_cnt先达到20时,更新east_first_cnt或west_first_cnt,这两个变量用来统计东边和西边的20人先到达对岸的次数。main方法中,模拟了十次过桥,在每次循环中,创建两个长度为 20 的 Person 数组,分别表示东边和西边的人,然后创建两个长度为 20 的 Thread 数组,分别用于管理东边和西边人的线程,然后初始化人数组和线程数组,并启动所有线程。接着使用循环等待所有线程执行完毕,打印分隔符,表示一次过桥结束。循环结束后,打印东边和西边先到达对岸的次数。

  • 13
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值