201871010131-张兴盼《面向对象程序设计(java)》第十七周学习总结

项目

内容

《面向对象程序设计(java)》

https://home.cnblogs.com/u/nwnu-daizh/

这个作业的要求在哪里

https://www.cnblogs.com/nwnu-daizh/p/12073034.html

作业学习目标

(1) 理解和掌握线程的优先级属性及调度方法;

(2) 掌握线程同步的概念及实现技术;

(3) Java线程综合编程练习

第一部分:总结线程同步技术

1.多线程的概念:

(1)多线程是进程执行过程中产生的多条执行线索。 

(2)多线程意味着一个程序的多行语句可以看上去几 乎在同一时间内同时运行。 

(3)线程不能独立存在,必须存在于进程中,同一进 程的各线程间共享进程空间的数据。 

2.Java实现多线程有两种途径:
创建Thread类的子类
在程序中定义实现Runnable接口的类

3.用Thread类的子类创建线程:

(1)首先需从Thread类派生出一个子类,在该子类中 重写run()方法。 

例: class hand extends Thread {

public void run() {……}

}

(2) 然后用创建该子类的对象 

Lefthand left=new Lefthand();

Righthand right=new Righthand(); 

(3)最后用start()方法启动线程 

left.start();

right.start();

4.用Thread类的子类创建多线程的关键性操作:

(1)定义Thread类的子类并实现用户线程操作,即 run()方法的实现。

(2)在适当的时候启动线程。

由于Java只支持单重继承,用这种方法定义的类不 可再继承其他父类。

用Runnable()接口实现线程

(1)首先设计一个实现Runnable接口的类; 

(2) 然后在类中根据需要重写run方法; 

(3)再创建该类对象,以此对象为参数建立Thread 类的对象; 

(4)调用Thread类对象的start方法启动线程,将 CPU执行权转交到run方法。

5.被阻塞线程和等待线程

 blocked (被阻塞):

阻塞时线程不能进入队列排队,必须等到引起 阻塞的原因消除,才可重新进入排队队列。 

sleep(),wait()是两个常用引起线程阻塞的方法。

6.线程阻塞的三种情况:

(1)等待阻塞 :通过调用线程的wait()方法,让线 程等待某工作的完成。 

(2)同步阻塞 :线程在获取synchronized同步锁失 败(因为锁被其它线程所占用),它会进入同步阻 塞状态。 

(3)其他阻塞 :通过调用线程的sleep()或join() 或发出了I/O请求时,线程会进入到阻塞状态。当 sleep()状态超时、join()等待线程终止或者超 时、或者I/O处理完毕时,线程重新转入就绪状态。

7.被终止的线程

 Terminated (被终止) 线程被终止的原因有二: 

(1)一是run()方法中最后一个语句执行完毕而自 然死亡。 

(2)二是因为一个没有捕获的异常终止了run方法 而意外死亡。 

可以调用线程的stop 方 法 杀 死 一 个 线 程 (thread.stop();),但是,stop方法已过时, 不要在自己的代码中调用它。

8.其他判断和影响线程状态的方法:

(1)join():等待指定线程的终止。 

(2)join(long millis):经过指定时间等待终止指定 的线程。 

(3)isAlive():测试当前线程是否在活动。 

(4)yield():让当前线程由“运行状态”进入到“就 绪状态”,从而让其它具有相同优先级的等待线程 获取执行权。

9.多线程调度

(1)Java提供一个线程调度器来监控程序启动后进入可运行状态的所有线程。线程调度器按照线程的优先级决定应调度哪些线程来执行。

(2)处于可运行状态的线程首先进入就绪队列排队等候处理器资源,同一时刻在就绪队列中的线程可能有多个。Java的多线程系统会给每个线程自动分配一个线程的优先级。

 10.Java 的线程调度采用优先级策略:

(1)优先级高的先执行,优先级低的后执行;

(2)多线程系统会自动为每个线程分配一个优先级,缺省时,继承其父类的优先级;

(3)任务紧急的线程,其优先级较高;

(4)同优先级的线程按“先进先出”的队列原则;

11.守护线程

守护线程的惟一用途是为其他线程提供服务。例 如计时线程。

在一个线程启动之前,调用setDaemon方法可 将线程转换为守护线程(daemon thread)。 例如: setDaemon(true);

12.要实现线程同步的原因: 

java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时, 
会产生冲突,使得变量值不唯一,因此我们需要加入同步锁来避免在当前线程的操作未完成前,其它线程改变共享资源值的情况发生,从而保证变量的唯一 
性和准确性

13.方法

a.synchronized 

synchronized可以修饰方法,但是由于同步本身就是一种高开销的操作,因此我们应该尽可能的减少同步的内容,提高性能 
使用同步代码块,下面的方式

 1 synchronized (this){
 2                     if (account.getBalance() > money) {
 3                         System.out.println(Thread.currentThread().getName() + "取钱" + money + "成功");
 4                     try {
 5                         Thread.sleep(1);
 6                     } catch (InterruptedException e) {
 7                         throw new RuntimeException(e);
 8                     }
 9                         account.reduceBalance(money);
10                         System.out.println("\t" + Thread.currentThread().getName() + "成功后的余额: " + account.getBalance());
11                     } else {
12                         System.out.println(Thread.currentThread().getName() + "取钱失败");
13                         System.out.println("\t" + Thread.currentThread().getName() + "失败后的余额: " + account.getBalance());
14                         break;
15                     }
16                 }

b.volatile 

volatile关键字保证了每个线程所取得的共享资源变量的值是一样的,它的原理是被volatile修饰的变量被访问时,都会从内存中读取,而不是从缓存(寄存器)中,这样保证了每个线程访问到的变量值的一致性 
但是他不会保证操作的原子性

1 private volatile double balance;

c.java.util.concurrent包下的ReentrantLock类 
使用的方法

 1 //需要声明这个锁
 2 private Lock lock = new ReentrantLock();
 3 
 4 // 存钱
 5 public void addMoney(int money) {
 6 lock.lock();//上锁
 7 try{
 8 count += money;
 9 System.out.println(System.currentTimeMillis() + "存进:" + money);
10 
11 }finally{
12 lock.unlock();//解锁
13 }
14 }

Lock与Synchronized的对比:
主要相同点:Lock能完成Synchronized所实现的所有功能。
主要不同点:Lock有比Synchronized更好的性能。Synchronized会自动释放锁,但是Lock一定要求程序员手工释放,并且必须在finally从句中释放。
synchronized 修饰方法时 表示同一个对象在不同的线程中 表现为同步队列
如果实例化不同的对象 那么synchronized就不会出现同步效果了。
d.ThreadLocal 

如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。现在明白了吧,原来每个线程运行的都是一个副本,也就是说存钱和取钱是两个账户,知识名字相同而已。
这样的话当操作不一致的时候,就无法解决问题,会出现下面的结果

 1 public class Bank {
 2 
 3 private static ThreadLocal<Integer> count = new ThreadLocal<Integer>(){
 4 @Override
 5 protected Integer initialValue() {
 6 // TODO Auto-generated method stub
 7 return 0;
 8 }
 9 
10 };
11 // 存钱
12 public void addMoney(int money) {
13 count.set(count.get()+money);
14 System.out.println(System.currentTimeMillis() + "存进:" + money);
15 
16 }
17 
18 // 取钱
19 public void subMoney(int money) {
20 if (count.get() - money < 0) {
21 System.out.println("余额不足");
22 return;
23 }
24 count.set(count.get()- money);
25 System.out.println(+System.currentTimeMillis() + "取出:" + money);
26 }
27 
28 // 查询
29 public void lookMoney() {
30 System.out.println("账户余额:" + count.get());
31 }
32 }
 1 package threadTest;
 2 
 3 
 4 public class SyncThreadTest {
 5 
 6 public static void main(String args[]){
 7 final Bank bank=new Bank();
 8 
 9 Thread tadd=new Thread(new Runnable() {
10 
11 @Override
12 public void run() {
13 // TODO Auto-generated method stub
14 while(true){
15 try {
16 Thread.sleep(1000);
17 } catch (InterruptedException e) {
18 // TODO Auto-generated catch block
19 e.printStackTrace();
20 }
21 bank.addMoney(100);
22 bank.lookMoney();
23 System.out.println("\n");
24 
25 }
26 }
27 });
28 
29 Thread tsub = new Thread(new Runnable() {
30 
31 @Override
32 public void run() {
33 // TODO Auto-generated method stub
34 while(true){
35 bank.subMoney(100);
36 bank.lookMoney();
37 System.out.println("\n");
38 try {
39 Thread.sleep(1000);
40 } catch (InterruptedException e) {
41 // TODO Auto-generated catch block
42 e.printStackTrace();
43 }
44 }
45 }
46 });
47 tsub.start();
48 
49 tadd.start();
50 }
51 }

第二部分:实验部分

1、实验目的与要求

(1) 掌握线程同步的概念及实现技术;

(2) 线程综合编程练习

2、实验内容和步骤

实验1:测试程序并进行代码注释。

测试程序1:

l 在Elipse环境下调试教材651页程序14-7,结合程序运行结果理解程序;

l 掌握利用锁对象和条件对象实现的多线程同步技术。

程序代码如下:

 1 package synch;
 2  
 3 import java.util.*;
 4 import java.util.concurrent.locks.*;
 5  
 6 /**
 7 一个银行有许多银行帐户,使用锁序列化访问 * @version 1.30 2004-08-01
 8  * @author Cay Horstmann
 9  */
10 public class Bank
11 {
12    private final double[] accounts;
13    private Lock bankLock;
14    private Condition sufficientFunds;
15  
16    /**
17     * 建设银行。
18     * @param n 账号
19     * @param initialBalance 每个账户的初始余额
20     */
21    public Bank(int n, double initialBalance)
22    {
23       accounts = new double[n];
24       Arrays.fill(accounts, initialBalance);
25       bankLock = new ReentrantLock();
26       sufficientFunds = bankLock.newCondition();//在等待条件前,锁必须由当前线程保持。
27    }
28  
29    /**
30     * 把钱从一个账户转到另一个账户。
31     * @param 从账户转账
32     * @param 转到要转账的账户
33     * @param 请允许我向你转达
34     */
35    public void transfer(int from, int to, double amount) throws InterruptedException
36    {
37       bankLock.lock();//加锁
38       try
39       {//锁对象引用条件对象
40          while (accounts[from] < amount)
41             sufficientFunds.await();//造成当前线程在接到信号或被中断之前一直处于等待状态。
42          System.out.print(Thread.currentThread());
43          accounts[from] -= amount;
44          System.out.printf(" %10.2f from %d to %d", amount, from, to);
45          accounts[to] += amount;
46          System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());
47          sufficientFunds.signalAll();//如果所有的线程都在等待此条件,则唤醒所有线程
48       }
49       finally
50       {
51          bankLock.unlock();//解锁。
52       }
53    }
54  
55    /**
56     * 获取所有帐户余额的总和。
57     * @return 总余额
58     */
59    public double getTotalBalance()
60    {
61       bankLock.lock();
62       try
63       {
64          double sum = 0;
65  
66          for (double a : accounts)
67             sum += a;
68  
69          return sum;
70       }
71       finally
72       {
73          bankLock.unlock();
74       }
75    }
76  
77    /**
78     * 获取银行中的帐户数量。
79     * @return 账号
80     */
81    public int size()
82    {
83       return accounts.length;
84    }
85 }
 1 package synch;
 2  
 3 /**
 4  * 这个程序显示了多个线程如何安全地访问数据结构。
 5  * @version 1.31 2015-06-21
 6  * @author Cay Horstmann
 7  */
 8 public class SynchBankTest
 9 {
10    public static final int NACCOUNTS = 100;
11    public static final double INITIAL_BALANCE = 1000;
12    public static final double MAX_AMOUNT = 1000;
13    public static final int DELAY = 10;
14     
15    public static void main(String[] args)
16    {
17       Bank bank = new Bank(NACCOUNTS, INITIAL_BALANCE);
18       for (int i = 0; i < NACCOUNTS; i++)
19       {
20          int fromAccount = i;
21          Runnable r = () -> {
22             try
23             {
24                while (true)
25                {
26                   int toAccount = (int) (bank.size() * Math.random());
27                   double amount = MAX_AMOUNT * Math.random();
28                   bank.transfer(fromAccount, toAccount, amount);
29                   Thread.sleep((int) (DELAY * Math.random()));//在指定的毫秒数内让当前正在执行的线程休眠
30                }
31             }
32             catch (InterruptedException e)
33             {
34             }           
35          };
36          Thread t = new Thread(r);
37          t.start();//使线程开始执行
38       }
39    }
40 }

运行结果如下:

测试程序2:

l 在Elipse环境下调试教材655页程序14-8,结合程序运行结果理解程序;

掌握synchronized在多线程同步中的应用。

程序代码如下:

 1 package synch2;
 2 
 3 import java.util.*;
 4 
 5 /**
 6  * A bank with a number of bank accounts that uses synchronization primitives.
 7  * @version 1.30 2004-08-01
 8  * @author Cay Horstmann
 9  */
10 public class Bank
11 {
12    private final double[] accounts;
13 
14    /**
15     * Constructs the bank.
16     * @param n the number of accounts
17     * @param initialBalance the initial balance for each account
18     */
19    public Bank(int n, double initialBalance)
20    {
21       accounts = new double[n];
22       Arrays.fill(accounts, initialBalance);
23    }
24 
25    /**
26     * Transfers money from one account to another.
27     * @param from the account to transfer from
28     * @param to the account to transfer to
29     * @param amount the amount to transfer
30     */
31    public synchronized void transfer(int from, int to, double amount) throws InterruptedException//sysynchronized关键字修饰方法实现加锁操作
32    {
33       while (accounts[from] < amount)
34          wait();  //wait()方法导致线程进入等待状态直到它被通知,该方法只能在一个同步方法中调用
35       System.out.print(Thread.currentThread());//Thread.currentThread()返回当前执行线程的Thread对象
36       accounts[from] -= amount;
37       System.out.printf(" %10.2f from %d to %d", amount, from, to);
38       accounts[to] += amount;
39       System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());
40       notifyAll(); //解除那些在该对象上调用wait方法的线程的阻塞状态
41    }
42 
43    /**
44     * Gets the sum of all account balances.
45     * @return the total balance
46     */
47    public synchronized double getTotalBalance()
48    {
49       double sum = 0;
50 
51       for (double a : accounts)
52          sum += a;
53 
54       return sum;
55    }
56 
57    /**
58     * Gets the number of accounts in the bank.
59     * @return the number of accounts
60     */
61    public int size()
62    {
63       return accounts.length;
64    }
65 }

 

 1 package synch2;
 2 
 3 /**
 4  * This program shows how multiple threads can safely access a data structure,
 5  * using synchronized methods.
 6  * @version 1.31 2015-06-21
 7  * @author Cay Horstmann
 8  */
 9 public class SynchBankTest2
10 {
11    public static final int NACCOUNTS = 100;
12    public static final double INITIAL_BALANCE = 1000;
13    public static final double MAX_AMOUNT = 1000;
14    public static final int DELAY = 10;
15 
16    public static void main(String[] args)
17    {
18       Bank bank = new Bank(NACCOUNTS, INITIAL_BALANCE);
19       for (int i = 0; i < NACCOUNTS; i++)
20       {
21          int fromAccount = i;
22          /**lamber表达式*/
23          /*Runnable r = () -> {
24             try
25             {
26                while (true)
27                {
28                   int toAccount = (int) (bank.size() * Math.random());
29                   double amount = MAX_AMOUNT * Math.random();
30                   bank.transfer(fromAccount, toAccount, amount);
31                   Thread.sleep((int) (DELAY * Math.random()));
32                }
33             }
34             catch (InterruptedException e)
35             {
36             }
37          };*/
38         /**直接声明
39          *  Runnable r=new Runnable() {
40             
41             @Override
42             public void run() {
43                 try
44                 {
45                    while (true)
46                    {
47                       int toAccount = (int) (bank.size() * Math.random());
48                       double amount = MAX_AMOUNT * Math.random();
49                       bank.transfer(fromAccount, toAccount, amount);
50                       Thread.sleep((int) (DELAY * Math.random()));
51                    }
52                 }
53                 catch (InterruptedException e)
54                 {
55                 }
56              };
57                 
58             
59         };*/
60          
61          /**
62                                   *  匿名内部类
63                       */
64          Thread t = new Thread(new Runnable(){
65              
66              @Override
67              public void run() {
68                  try
69                  {
70                     while (true)
71                     {
72                        int toAccount = (int) (bank.size() * Math.random());
73                        double amount = MAX_AMOUNT * Math.random();
74                        bank.transfer(fromAccount, toAccount, amount);
75                        Thread.sleep((int) (DELAY * Math.random()));
76                     }
77                  }
78                  catch (InterruptedException e)
79                  {
80                  }
81               };
82                  
83              
84          });
85          t.start();
86       }
87    }
88 }

运行结果如下:

测试程序3:

l 在Elipse环境下运行以下程序,结合程序运行结果分析程序存在问题;

l 尝试解决程序中存在问题。

程序代码如下:

 1 class Cbank
 2 {
 3      private static int s=2000;
 4      public   static void sub(int m)
 5      {
 6            int temp=s;
 7            temp=temp-m;
 8           try {
 9               Thread.sleep((int)(1000*Math.random()));
10             }
11            catch (InterruptedException e)  {              }
12               s=temp;
13               System.out.println("s="+s);
14         }
15     }
16  
17  
18 class Customer extends Thread
19 {
20   public void run()
21   {
22    for( int i=1; i<=4; i++)
23      Cbank.sub(100);
24     }
25  }
26 public class Thread3
27 {
28  public static void main(String args[])
29   {
30    Customer customer1 = new Customer();
31    Customer customer2 = new Customer();
32    customer1.start();
33    customer2.start();
34   }
35 }  

运行结果如下:

 改进后代码如下:

 1 class Cbank
 2 {
 3      private static int s=2000;
 4      public  synchronized static void sub(int m)
 5      {
 6            int temp=s;
 7            temp=temp-m;
 8           try {
 9                  Thread.sleep((int)(1000*Math.random()));
10                }
11            catch (InterruptedException e)  {              }
12               s=temp;
13               System.out.println("s="+s);
14           }
15     }
16  
17  
18 class Customer extends Thread
19 {
20   public void run()
21   {
22    for( int i=1; i<=4; i++)
23      Cbank.sub(100);
24     }
25  }
26  
27 public class Thread3
28 {
29  public static void main(String args[])
30   {
31    Customer customer1 = new Customer();
32    
33    Customer customer2 = new Customer();
34    customer1.start();
35    customer2.start();
36   }
37 }

运行截图如下:

实验2:结对编程练习包含以下4部分

结对伙伴:杨丽霞

1)   程序设计思路简述;

    程序的主要设计思路没有太繁琐,只是新建三个售票口,在thread类中创建线程并开启线程,然后在run方法中定义线程任务,进行异常处理后设计在10张票数内时将售票情况进行打印,票数超过10张时程序结束;

2)   符合编程规范的程序代码;

 1 package math;
 2  
 3 public class Demo {
 4     public static void main(String[] args) {
 5         Mythread mythread = new Mythread();
 6         Thread ticket1 = new Thread(mythread);
 7         Thread ticket2 = new Thread(mythread);
 8         Thread ticket3 = new Thread(mythread);//新建三个Thread类对象
 9         ticket1.start();
10         ticket2.start();
11         ticket3.start();//调用thread类的start方法来开启线程
12     }
13 }
14  
15 class Mythread implements Runnable {//实现runnable接口进行线程的创建
16     int ticket = 1;
17     boolean flag = true;
18  
19     @Override
20     public void run() {//将线程任务代码定义到run方法中
21         while (flag) {
22             try {
23                 Thread.sleep(500);
24             } catch (InterruptedException e) {
25                 // TODO Auto-generated catch block
26                 e.printStackTrace();
27             }
28  
29             synchronized (this) {
30                 if (ticket <= 10) {
31                     System.out.println(Thread.currentThread().getName() + "窗口售:第" + ticket + "张票");
32                     ticket++;//票数在10张之内时,进行打印直到售出10张票时停止
33                 }
34                 if (ticket > 10) {
35                     flag = false;
36                 }
37             }
38         }
39     }
40  
41 }

3)   程序运行功能界面截图;

三、实验总结:

        通过这次实验,我主要掌握了同步线程的相关问题,也学习了并发多线程,学习了锁对象、synchronized关键字等。在解读代码的过程中有好多不懂的代码,在程序测试出来后又通过对照运行结果,可以更加直观的体会有些代码的作用,加强对相关知识的理解,在平时要多加强动手能力,多思考。

posted @ 2019-12-23 21:20  201871010131-张兴盼  阅读(229)  评论(1编辑  收藏  举报