(一)查看下列程序并运行,掌握如何通过扩展Thread类创建线程。
package case1;
public class MySimpleThread extends Thread {
public void run() {
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 8; j++) {
System.out.print(getName() + "[" + j + "] ");
}
System.out.println();
}
System.out.println("-----" + getName() + " ends-----");
}
public static void main(String[] args) {
Thread thread1 = new MySimpleThread();
thread1.setName("T1");
Thread thread2 = new MySimpleThread();
thread2.setName("T2");
thread1.start();
thread2.start();
System.out.println("=====" + Thread.currentThread().getName() + " ends=====");
}
}
说明:首先,启动一个线程应该调用start()方法,而不是直接调用run()方法,启动start()方法后,具体该线程何时执行,分配多长时间执行都交由操作系统去分配,而不是由程序员来控制。在线程执行过程中,可以调用Thread类的静态方法currentThread()来查看当前哪个线程正在执行。另外,从程序中我们可以看出来,Java程序的入口main()方法其实也是由java虚拟机启动的一个线程来调用的,其默认名字为main。我们多次运行该程序,会得到不同的运行结果,因为每次操作系统为不同线程分配的时间片是不固定的,因此多次运行程序可以看出多线程程序的特点。
![在这里插入图片描述](https://img-blog.csdnimg.cn/b8efe76c35414772acf8468481356ec8.png)
写一个扩展Thread类创建线程的例子并运行。
package case1;
import java.util.Random;
public class ThreadDemo extends Thread {
public void run() {
Random rand = new Random();
for (int i = 0; i < 10; i++) {
System.out.println(getName() + " " + rand.nextInt(10000));
}
System.out.println("====" + getName() + " ends====");
}
public static void main(String[] args) {
Thread thread_1 = new ThreadDemo();
thread_1.setName("T1");
Thread thread_2 = new ThreadDemo();
thread_2.setName("T2");
thread_1.start();
thread_2.start();
}
}
![在这里插入图片描述](https://img-blog.csdnimg.cn/ba18bb26410d4f27b0ffe96fdc07a4e3.png)
(二)查看下列程序并运行,掌握如何通过Runnable接口创建线程。
package case2;
public class MySimpleRunnable implements Runnable {
public void run(){
for(int i=0; i<5; i++){
for(int j=0; j<8; j++){
System.out.print(Thread.currentThread().getName()+"["+j+"] ");
}
System.out.println();
}
System.out.println("-----" + Thread.currentThread().getName() + " ends-----");
}
public static void main(String [] args){
Thread T1 = new Thread(new MySimpleRunnable ());
Thread T2 = new Thread(new MySimpleRunnable ());
T1.start();
T2.start();
System.out.println("====="+Thread.currentThread().getName()+" ends=====");
}
}
![在这里插入图片描述](https://img-blog.csdnimg.cn/1783dde65e4a4bf0aa6ed7ec9e13dd62.png)
对比程序(一),程序(二)有什么不同?
相比程序(一),程序(二)创建线程的方法是:创建Thread类的对象将Runnable接口的子类对象作为参数传递给Thread类的构造函数。
对以上两种创建线程的方式做出总结。
(1) 创建线程的方式和具体步骤
①继承Thread类:
a.定义一个类继承Thread;
b.重写run方法;
c. 创建子类对象(创建线程对象);
d. 调用start方法,开启线程并让线程执行,同时还会告诉jvm去调用run方法
② 实现Runnable接口:
定义类实现Runnable接口;
覆盖接口中的run方法;
创建Thread类的对象;
将Runnable接口的子类对象作为参数传递给Thread类的构造函数;
调用Thread类的start方法开启线程。
(2) 优点和缺点
①采用继承Thread类方式:
a.优点:编写简单,如果需要访问当前线程,无需使用Thread.currentThread()方法,直接使用this,即可获得当前线程。
b.缺点:因为线程类已经继承了Thread类,所以不能再继承其他的父类。
②采用实现Runnable接口方式:
a.优点:线程类只是实现了Runable接口,还可以继承其他的类。在这种方式下,可以多个线程共享同一个目标对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
b.缺点:编程稍微复杂,如果需要访问当前线程,必须使用Thread.currentThread()方法。
写一个通过Runnable接口创建线程的例子并运行。
package case2;
class MyThread implements Runnable {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+" "+Math.random()
);
}
System.out.println("========" + Thread.currentThread().getName() + " ends=======");
}
}
public class Test {
public static void main(String[] args) {
Thread a = new Thread(new MyThread());
Thread b = new Thread(new MyThread());
Thread c = new Thread(new MyThread());
a.start();
b.start();
c.start();
}
}
![在这里插入图片描述](https://img-blog.csdnimg.cn/5c02f0058dbd4f7fabe568926ec6b6c2.png)
(三)下列是一个模拟车票预定的程序。
package case3;
class BookingClerk {
int remainder = 10;
synchronized void booking(int num){
if(num <= remainder){
System.out.println("预定"+num+"张票");
try{
Thread.sleep(1000);
remainder = remainder - num;
} catch(InterruptedException e){
remainder = remainder - num;
}
} else {
System.out.println("剩余票不足,无法接受预定");
}
System.out.println("还剩"+remainder+"张票");
}
}
class BookingTest implements Runnable{
BookingClerk bt;
int num;
BookingTest(BookingClerk bt, int num){
this.bt = bt;
this.num = num;
new Thread(this).start();
}
public void run(){
bt.booking(num);
}
public static void main(String [] args){
BookingClerk bt = new BookingClerk();
new BookingTest(bt, 7);
new BookingTest(bt,5);
}
}
运行此程序,发现有什么问题?
剩余票数为负数。
说明导致出现此问题的原因?
有些资源在同一时刻只被一个线程所利用。
修改上例程序,运用线程同步与互斥的相关知识,使其不会再出现之前的错误?
用synchronized关键字声明booking()方法为同步方法
synchronized void booking(int num)
(四)下列是模拟数据读写的程序。
package case4;
public class QueueOld {
protected Object[] data;
protected int writeIndex;
protected int readIndex;
protected int count;
public QueueOld(int size) {
data = new Object[size];
}
public void write(Object value) {
data[writeIndex++] = value;
System.out.println("write data is: " + value);
writeIndex %= data.length;
count += 1;
}
public void read() {
Object value = data[readIndex++];
System.out.println("read data is: " + value);
readIndex %= data.length;
count -= 1;
}
public static void main(String[] args) {
QueueOld q = new QueueOld(5);
new Writer(q);
new Reader(q);
}
}
class Writer implements Runnable {
QueueOld queue;
Writer(QueueOld target) {
queue = target;
new Thread(this).start();
}
public void run() {
int i = 0;
while (i < 100) {
queue.write(new Integer(i));
i++;
}
}
}
class Reader implements Runnable {
QueueOld queue;
Reader(QueueOld source) {
queue = source;
new Thread(this).start();
}
public void run() {
int i = 0;
while (i < 100) {
queue.read();
i++;
}
}
}
运行此程序,发现有什么问题?
读写数据时发生混乱的情况。
修改上例程序,运用线程同步与互斥的相关知识,使其不会再出现之前的错误?
package case4;
public class Queue {
protected Object[] data;
protected int writeIndex;
protected int readIndex;
protected int count;
a
public Queue(int size) {
data = new Object[size];
}
public synchronized void write(Object value) {
while (count >= data.length) {
try {
wait();
} catch (InterruptedException e) {
}
}
data[writeIndex++] = value;
System.out.println("write data is: " + value);
writeIndex %= data.length;
count += 1;
notify();
}
public synchronized void read() {
while (count <= 0) {
try {
wait();
} catch (InterruptedException e) {
}
}
Object value = data[readIndex++];
System.out.println("read data is: " + value);
readIndex %= data.length;
count -= 1;
notify();
}
public static void main(String[] args) {
Queue q = new Queue(5);
new Writer(q);
new Reader(q);
}
}
class Writer implements Runnable {
Queue queue;
Writer(Queue target) {
queue = target;
new Thread(this).start();
}
public void run() {
int i = 0;
while (i < 100) {
queue.write(new Integer(i));
i++;
}
}
}
class Reader implements Runnable {
Queue queue;
Reader(Queue source) {
queue = source;
new Thread(this).start();
}
public void run() {
int i = 0;
while (i < 100) {
queue.read();
i++;
}
}
}
![在这里插入图片描述](https://img-blog.csdnimg.cn/9f6896ac8223479e9384db11b0c50bd6.png)
(五)使用10个线程,第一个线程完成从1加到10,第2个线程从11加到20,…,第10个线程从91加到100,最后把10个线程结果相加。输出最后的结果。
package case5;
class Addition extends Thread {
private int currNum;
private static int sum;
public Addition(int currNum) {
this.currNum = currNum;
}
public static synchronized void add(int num) {
sum = sum + num;
}
public void run() {
int sum = 0;
for (int i = 0; i < 10; i++) {
sum = sum + currNum + i;
}
add(sum);
}
public int getSum() {
return sum;
}
}
public class Test {
public static void main(String[] a) {
Thread[] threadList = new Thread[10];
for (int i = 0; i < 10; i++) {
threadList[i] = new Addition(10 * i + 1);
threadList[i].start();
}
Addition a1 = new Addition(0);
System.out.println("Sum is : " + a1.getSum());
}
}
(六)线程和进程的区别是什么?
①进程:一个在内存中运行的应用程序;
②线程:进程中的一个执行任务(控制单元),负责当前进程中程序的执行;
③区别:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位。
(七)创建线程的方式有哪两种?如何启动一个线程?
①创建线程的两种方式:继承Thread类和实现Runnable接口; ②启动一个线程的方法:.star()方法和run()方法。
(八)线程有哪几种状态?状态之间是如何转换的?
①线程的五种状态:新建状态(New)、就绪状态(Runnable)、运行状态(Running)、阻塞状态(Blocked)、死亡状态(Dead);
②状态之间的转换方式如下图:
![在这里插入图片描述](https://img-blog.csdnimg.cn/5d4810be21b94a4bbfe5e0afb91a9825.png)
(九)什么叫“临界资源”?什么叫“临界区”?什么叫“同步方法”?
①是一次仅允许一个进程使用的共享资源;
②每个进程中访问临界资源的那段代码称为临界区;
③有synchronized关键字修饰的方法叫做同步方法。
(十)Java中的同步方法是如何处理临界区的互斥问题的?
使用synchronized来给共享区域加锁,确保共享资源安全。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)