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,结合程序运行结果理解程序;
l 掌握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关键字等。在解读代码的过程中有好多不懂的代码,在程序测试出来后又通过对照运行结果,可以更加直观的体会有些代码的作用,加强对相关知识的理解,在平时要多加强动手能力,多思考。