第11周-多线程

1. 本周学习总结

1.1 以你喜欢的方式(思维导图或其他)归纳总结多线程相关内容。

1109200-20170504213942289-615602620.png


线程状态转换图

1109200-20170504214017039-592075589.png

2. 书面作业

——本次PTA作业题集 多线程

1. 互斥访问与同步访问

——完成题集4-4(互斥访问)与4-5(同步访问)
1.1 除了使用synchronized修饰方法实现互斥同步访问,还有什么办法实现互斥同步访问(请出现相关代码)?

答: 除了synchronized方法外还有同步代码块、Lock。

  • 相关代码1(同步代码块):
public void deposit(int money) {
    synchronized(Account.class) {
        this.balance += money;
    }
}
  • 相关代码2(Lock):
private Lock poollock = new ReentrantLock();

public void withdraw(int money) {
    poollock.lock();
    setBalance(this.balance -= money);
    poollock.unlock();
}
1.2 同步代码块与同步方法有何区别?

答:首先语法不同;其次

  • 同步代码块:
    (1)加锁对象可以任意指定
    (2)对方法内部加锁,加锁范围小,性能更好
    (3)一个线程访问同步代码块时,另一个线程可以访问非同步代码块
  • 同步方法:
    (1)对当前对象加锁,相当于synchronized(this){}
    (2)对方法加锁,加锁范围大,性能较差
    (3)方法执行时,线程独占该锁

所声明为synchronized的成员函数中至多只有一个处于可执行的状态(因为至多只有一个能够获得该实例对应的锁),从而有效的避免了类成员变量的访问冲突。

1.3 实现互斥访问的原理是什么?请使用对象锁概念并结合相应的代码块进行说明。当程序执行synchronized同步代码块或者同步方法时,线程的状态是怎么变化的?

答:
(1)

  • 实现互斥访问的原理:
    一个线程调用加上同步的方法时,取得对象锁定;另一个线程想调用同一方法时必须等待前一个线程执行完毕释放锁定后取得锁定才能进入可执行状态。
    以下代码获得this这个对象上的内部锁;如果未获得对象锁,下面的代码就无法执行,必须等待;从而通过对象锁实现了互斥访问。
class MyCounter{
   private int i = 0;
   public void increment(){
      //i++;
      synchronized (this) {
      i++;
      }
   }
    ………
}

(2)

  • 相应程序的例子
public class SynchronizedStatementTest {

    public static void main(String[] args) throws InterruptedException {
        final MyCounter counter = new MyCounter();
        
        
        Runnable incTask = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    counter.increment();
                }
            }
        };
        Runnable decTask = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    counter.decrement();
                }
            }
        };
        Thread t1 = new Thread(incTask);
        Thread t2 = new Thread(decTask);
        t1.start();t2.start();
        t1.join();
        t2.join();
        System.out.println("counter's value is "+counter.getValue());
        System.out.println("End!");
    }
}
class MyCounter{
    private int i = 0;
    public void increment(){
        //i++;
        synchronized (this) {
            i++;
        }
    }
    public void decrement(){
        //i--;
        synchronized (this) {
            i--;
        }
    }
    public int getValue(){
        return i;
    }
}
  • 线程的状态变化情况:
    (1)线程t1开始执行时,将i加锁,取得i的值为0;
    (2)然后执行i++,此时i内存的值1,释放当前i上的锁;
    (3)这时线程t2可以开始执行并使i加锁,取得i的值为1;
    (4)执行i--,将计算后的值存到i,此时i为0,释放当前i上的锁。
    如果线程未获得对象锁,必须等待前一线程完成执行过程,释放当前对象的锁。
1.4 Java多线程中使用什么关键字实现线程之间的通信,进而实现线程的协同工作?为什么同步访问一般都要放到synchronized方法或者代码块中?

答:

  • 线程的协同工作的关键字:
    (1)wait()和notify()/notifyAll()——只能在被声明为synchronized的方法或代码段中调用
    (2)Condition(使用条件对象)——需要搭配Lock对象来使用
  • 同步访问一般都要放到synchronized方法或者代码块中的原因
    synchronized方法或者代码块相当于对象监视器
    因为存储容量是有限的,当一个线程占满容量时,该线程需要被wait,等到满满的资源被消费掉一些时,该线程需要被notify继续工作;当容量被某线程消费完毕了,此线程需要被wait,等存储中有资源了,此线程将才被notify继续工作。

2. 交替执行

实验总结(不管有没有做出来)
  • 本题考查多线程互斥的综合运用。
    所有的方法都应该用synchronized修饰;并用wait()/notify()进行线程之间的合作。注意使用布尔型来交替两个线程的执行。

3. 互斥访问

3.1 修改TestUnSynchronizedThread.java源代码使其可以同步访问。(关键代码截图,需出现学号)

1109200-20170505212339226-690995143.png

3.2 进一步使用执行器改进相应代码(关键代码截图,需出现学号)

参考Executor执行线程中提到的方法,有以下代码:
1109200-20170506192143195-1933521513.png

参考资料:Java多线程之Executor、ExecutorService、Executors、Callable、Future与FutureTask

4. 线程间的合作:生产者消费者问题

4.1 运行MyProducerConsumerTest.java。正常运行结果应该是仓库还剩0个货物。多运行几次,观察结果,并回答:结果正常吗?哪里不正常?为什么?

答:
(1)结果不正常;
(2)剩余货物总是不确定的;
(3)运行结果与生产者和消费者的存取速度有关。

4.2 使用synchronized, wait, notify解决该问题(关键代码截图,需出现学号)

1109200-20170506193127882-672552430.png
1109200-20170506193238007-776168219.png

4.3 选做:使用Lock与Condition对象解决该问题。

1109200-20170506200013648-55644399.png

5. 查询资料回答:什么是线程安全?(用自己的话与代码总结,写自己看的懂的作业)

答:
多个线程同时运行,得到的结果与预期的结果一致,则说明线程是安全的。如果结果不一致,可以添加方法声明等使其一致。
如本次作业的原来的MyProducerConsumerTest.java,每次运行结果都可能不同,这样的是不安全的。

6. 选做:实验总结

6.1 4-8(CountDownLatch)实验总结

答:本题考查固定线程数线程池与CountDownLatch。
首先设定一个计数器,用来控制唤醒;在线程池中建立固定数线程(执行器);循环要执行的n个任务。

6.2 4-9(集合同步问题)实验总结

答:

6.3 较难:4-10(Callable),并回答为什么有Runnable了还需要Callable?实验总结。

答:

7. 选做:使用其他方法解决题目4的生产者消费者问题。

7.1 使用BlockingQueue解决生产者消费者问题关键代码截图
7.2 说明为什么不需要显示的使用wait、notify就可以解决同步问题。这样解决相比较wait、notify有什么优点吗?
7.3 使用Condition解决生产者、消费者问题。

8. 选做:编写一段代码,证明你会使用ForkJoinPool?

3. 码云上代码提交记录

——题目集:多线程(4-4到4-10)

3.1. 码云代码提交记录

——在码云的项目中,依次选择“统计-Commits历史-设置时间段”, 然后搜索并截图
1109200-20170506200946789-874571251.png

3.2 截图多线程PTA提交列表(没做完的待续)

1109200-20170506200725039-624555218.png

4.选做:课外阅读(待续)

4.1 Questions and Exercises: Concurrency,学习总结。

4.2 Java多线程之Executor、ExecutorService、Executors、Callable、Future与FutureTask

4.3 Java 8 Concurrency Tutorial: Threads and Executors

转载于:https://www.cnblogs.com/whting/p/6810028.html

好的,实现一个简单的抽奖活动需要以下步骤: 1. 定义奖池和参与者列表; 2. 创建多个线程,每个线程负责从参与者列表中随机选出一个人,然后将该人从参与者列表中删除,并将其加入奖池中; 3. 所有线程执行完毕后,输出获奖者列表。 下面是一个使用Python实现的示例代码: ```python import random import threading # 定义奖池和参与者列表 prizes = ['一等奖', '二等奖', '三等奖'] participants = ['张三', '李四', '王五', '赵六', '钱七', '孙八', '九', '吴十'] # 创建多个线程,每个线程负责从参与者列表中随机选出一个人,然后将该人从参与者列表中删除,并将其加入奖池中 class LotteryThread(threading.Thread): def __init__(self, name): threading.Thread.__init__(self) self.name = name def run(self): while len(participants) > 0: participant = random.choice(participants) prizes.append(participant) participants.remove(participant) print('{}: {}中奖了!'.format(self.name, participant)) # 启动多个线程 threads = [] for i in range(3): thread = LotteryThread('线程{}'.format(i+1)) threads.append(thread) thread.start() # 等待所有线程执行完毕 for thread in threads: thread.join() # 输出获奖者列表 print('获奖者列表:') for i, prize in enumerate(prizes): print('第{}个奖品:{}'.format(i+1, prize)) ``` 在上面的代码中,我们创建了3个线程,每个线程随机选出一个参与者,并将其加入奖池中。所有线程执行完毕后,我们输出获奖者列表。注意,线程之间可能会有竞争条件,因此我们需要使用线程锁来保证线程安全。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值