1、实验目的与要求
(1) 掌握线程概念;
(2) 掌握线程创建的两种技术;
(3) 理解和掌握线程的优先级属性及调度方法;
(4) 掌握线程同步的概念及实现技术;
2、实验内容和步骤
实验1:测试程序并进行代码注释。
测试程序1:
l 在elipse IDE中调试运行ThreadTest,结合程序运行结果理解程序;
l 掌握线程概念;
l 掌握用Thread的扩展类实现线程的方法;
l 利用Runnable接口改造程序,掌握用Runnable接口创建线程的方法。
class Lefthand extends Thread { public void run() { for(int i=0;i<=5;i++) { System.out.println("You are Students!"); try{ sleep(500); } catch(InterruptedException e) { System.out.println("Lefthand error.");} } } } class Righthand extends Thread { public void run() { for(int i=0;i<=5;i++) { System.out.println("I am a Teacher!"); try{ sleep(300); } catch(InterruptedException e) { System.out.println("Righthand error.");} } } } public class ThreadTest { static Lefthand left; static Righthand right; public static void main(String[] args) { left=new Lefthand(); right=new Righthand(); left.start(); right.start(); } } |
测试结果:
利用Runnable接口改造程序,掌握用Runnable接口创建线程的方法。源代码如下
测试程序2:
l 在Elipse环境下调试教材625页程序14-1、14-2 、14-3,结合程序运行结果理解程序;
l 在Elipse环境下调试教材631页程序14-4,结合程序运行结果理解程序;
l 对比两个程序,理解线程的概念和用途;
l 掌握线程创建的两种技术。
程序运行结果:
运行程序14-4
测试程序3:分析以下程序运行结果并理解程序。
class Race extends Thread { public static void main(String args[]) { Race[] runner=new Race[4]; for(int i=0;i<4;i++) runner[i]=new Race( ); for(int i=0;i<4;i++) runner[i].start( ); runner[1].setPriority(MIN_PRIORITY); runner[3].setPriority(MAX_PRIORITY);} public void run( ) { for(int i=0; i<1000000; i++); System.out.println(getName()+"线程的优先级是"+getPriority()+"已计算完毕!"); } } |
测试程序4
l 教材642页程序模拟一个有若干账户的银行,随机地生成在这些账户之间转移钱款的交易。每一个账户有一个线程。在每一笔交易中,会从线程所服务的账户中随机转移一定数目的钱款到另一个随机账户。
l 在Elipse环境下调试教材642页程序14-5、14-6,结合程序运行结果理解程序;
综合编程练习
编程练习1
- 设计一个用户信息采集程序,要求如下:
(1) 用户信息输入界面如下图所示:
(2) 用户点击提交按钮时,用户输入信息显示控制台界面;
(3) 用户点击重置按钮后,清空用户已输入信息;
(4) 点击窗口关闭,程序退出。
2.创建两个线程,每个线程按顺序输出5次“你好”,每个“你好”要标明来自哪个线程及其顺序号。
3. 完善实验十五 GUI综合编程练习程序。
1.实验源代码:
Main:
WinCenter:
demo:
实验运行结果:
2.实验源代码
实验运行结果:
实验总结:
实现多线程的方法
继承Thread类(不支持多继承)
实现Runnable接口(支持多继承)
停止线程
使用interrupt()方法,给线程打上停止标记,在线程内部通过isInterrupted()判断线程是否是停止状态,通过throw new InterruptedException()抛出异常,停止线程。
synchronized
线程A先持有object对象的lock锁,线程B可以异步调用object对象中的非synchronized类型方法,如果调用synchronized方法,则需等待A释放锁。
锁重入,在一个synchronized方法/块的内部调用本类的其他synchronized方法/块,是永远可以得到锁的。
synchronized声明方法,可能会让其他线程等待比较长的时间,可以通过synchronized同步语句块解决。
synchronized方法是对当前对象加锁,而synchronized代码块,则是对某一对象加锁,根据传参判断。
volatile
主要作用是使变量在多个线程间可见。强制从公共堆栈取得变量值而不是从线程私有数据栈中取得变量值。
join
使用场景:主线程创建子线程,如果子线程要进行大量的耗时运算,主线程往往早于子线程之前结束。使用join()可以等待线程对象销毁。
使所属线程对象x正常执行run()方法中的任务,而当前线程z进行无限期的阻塞,等待线程x销毁后再继续执行线程z之后的代码。
join(long)的底层是使用wait(long)实现,会释放锁,而sleep(long)不会释放锁。
ThreadLocal
主要解决变量在不同线程之间隔离性,即让不同的线程拥有自己的值。
Lock
调用lock.lock()代码的线程就持有了“对象监视器”,其他线程只有等待锁被释放时再次争抢,效果同synchronized。
Condition
一个Lock对象里可以创建多个Condition(即对象监视器)实例。线程对象可以注册在指定的Condition中,实现有选择性的线程通知。
使用notify()/notifyAll()方法,被通知的线程是由JVM随机选择/全部通知。
Condition通过await()和signal()/signalAll()方法实现等待和通知。
公平锁与非公平锁
公平锁是线程获取锁的顺序是按照线程加锁的顺序来分配。
非公平锁是一种获取锁的抢占机制,随机获得锁,可能造成某些线程一直拿不到锁。
读写锁
共享锁,读操作相关
排他锁,写操作相关
多个读锁之间不互斥,读锁和写锁互斥,写锁和写锁互斥。