第一章 线程(上)

8个月前 阅读 366 评论 0 赞 0

内容:
线程的使用方法
线程的调度和控制方法

1.1 线程的基本概念

  1. 一个线程是一个程序内部的顺序控制流

线程和进程
每个进程都有独立的代码和数据空间(进程上下文),进程切换的开销大。
线程:轻量级的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换的开销小。
多进程:在操作系统中,能够同时运行多个任务(程序)。
多线程:在同一个应用程序中,有多个顺序流可以同时执行。
线程的概念模型
虚拟的CPU,封装在java.lang.Thread类中。
CPU所执行的代码,传递给Thread类。
CPU所处理的数据,传递给Thread类。
1.1图
线程体
Java的线程是通过java.lang.Thread类来实现的。
每个线程都是通过个某个特定Thread对象的方法run()来完成其操作的,方法run()称为线程体。
构造线程的两种方法

  • 定义一个线程类,它继承类Thread并重写其中的方法run();
  • 提供一个实现接口Runnable的类作为线程的目标对象,在初始化一个Thread子类的线程对象时,把目标对象传递给这个线程实例,由该对象提供线程体run()。

    1.2 通过Thread类创建线程

    如何创建线程。

    方法一:通过Thread类创建线程

    Thread类
  • 直接继承了Object类,并且实现了Runnable接口。位于java.lanb包中。
  • 封装了线程对象需要的属性和方法
  • 继承Thread类–创建多线程的方法之一
    从Thread类派生一个子类,并创建子类的对象
    子类应该重写Thread类的run()方法,写入需要在新线程中执行的语句段。
    调用start方法来启动新线程,自动进入run方法。
    eg:在新线程中完成计算某个整数的阶乘。
    package com.ly.thread;

public class FactorialThreadTester {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(“main thread starts”);
FactorialThread thread = new FactorialThread(10);
thread.start();//将自动进入run()方法
System.out.println(“main thread ends”);
}
}

package com.ly.thread;

public class FactorialThread extends Thread{
private int num;
public FactorialThread(int num){
this.num = num;
}
public void run(){
int i = num;
int result = 1;
System.out.println(“new thread started”);
while(i>0){
result = result*i;i–;
}
System.out.println(“The factorial of “+num+” is “+result);
System.out.println(“new thread ends”);
}
}

运行结果:
main thread starts
main thread ends
new thread started
The factorial of 10 is 3628800
new thread ends
结果说明:

  • main线程已经执行完后,新线程才执行完
  • main方法调用thread.start()方法启动新线程后并不等待其run()方法返回就继续运行,线程的run()方法在一边独自运行,不影响原来的main方法的运行。
    (线程是有线程调度器去调用的,不是start后就一定开始运行)

    1.3 线程的休眠

    修改:延长主线程
    如果启动新线程后希望主线程多持续一会再结束,可在start语句后加上让当前线程(这里是main)休眠1毫秒的语句:
    public class FactorialThreadTester {
    public static void main(String[] args) {
    1. // TODO Auto-generated method stub
    2. System.out.println("main thread starts");
    3. FactorialThread thread = new FactorialThread(10);
    4. thread.start();//将自动进入run()方法
    5. try{
    6. thread.sleep(1);//休息1毫秒
    7. }catch(Exception e){
    8. System.out.println("main thread ends");
    9. }
    10. System.out.println("main thread ends");
    }
    }
    运行结果:
    main thread starts
    new thread started
    The factorial of 10 is 3628800
    new thread ends
    main thread ends
    运行结果说明:
    新线程结束后main线程才结束

Thread类

创建3个新线程,每个线程睡眠一段时间(0~6s),然后结束。
package com.ly.moreThread;
public class ThreadSleepTester {
public static void main(String[] args) {
// TODO Auto-generated method stub
TestThread thread1 = new TestThread(“thread1”);
TestThread thread2 = new TestThread(“thread2”);
TestThread thread3 = new TestThread(“thread3”);
System.out.println(“Starting threads”);
thread1.start();//启动线程1
thread2.start();//启动线程2
thread3.start();//启动线程3
System.out.println(“Thread started main ends\n”);
}
}

package com.ly.moreThread;
public class TestThread extends Thread{
private int sleepTime;
public TestThread(String name){
super(name);
sleepTime = (int)(Math.random()*6000);
}
public void run(){
try{
System.out.println(
getName()+”going to sleep for “+sleepTime);
Thread.sleep(sleepTime);//当前调用它的线程休眠
}catch(Exception e){
}
System.out.println(getName()+” finished”);
}
}
运行结果:
Starting threads
Thread started main ends

  1. thread2going to sleep for 3374
  2. thread3going to sleep for 888
  3. thread1going to sleep for 3386
  4. thread3 finished
  5. thread2 finished
  6. thread1 finished

注:Thread.sleep(3000);必须要捕获异常,否则编译出错。

1.4Thread类详解

| 名称 | 说明 |
| public Thread() | 构造一个新的线程对象,默认名为Thread -n,n从0开始递增的整数 |
| public Thread(Runnable target) | 构造一个新的线程对象,以一个实现Runnable接口的类的对象为参数。默认名为Thread-n,n是从0开始递增的整数 |
| public Thread(String name) | 构造一个新的线程对象,并同时指定线程名 |
| public static Thread currentThread() | 返回当前正在运行的线程对象 |
| public static void yield() | 使当前线程对象暂停,允许别的线程开始运行 |
| public static void sleep(long millis) | 使当前线程暂停运行指定毫秒数,但此线程并不失去已获得的锁 |
| public void start() | 启动线程,JVM将调用此线程的run方法,结果是将同时运行线程,当前线程和执行run方法的线程 |
| public void run() | Thread的子类应该重写此方法,内容应为线程应执行的任务 |
| public final void stop() | 停止线程运行,释放该线程占用的对象锁 |
| public void interrupt() | 中端此线程 |
| public final void join() | 如果此前启动了线程A,调用join方法将等待线程A死亡才能继续执行当前线程 |
| public final void join(long mills) | 如果此前启动了线程A,调用join方法等待指定毫秒数或线程A死亡才能继续执行当前线程。 |
| public final void setPriority(int newPriority) | 设置线程的优先级 |
| public final void setDaemon(Boolean on) | 设置是否为后台线程,如果当前运行线程均为后台线程则JVM停止运行。这个方法必须在start()方法前使用 |
| public final void checkAccess() | 判断当前线程是否有权力修改调用此方法的线程 |
| public void setName(String name) | 更改本线程的名称为指定参数 |
| public final boolean isAlive() | 测试线程是否处于活动状态,如果线程被启动并且没有死亡则返回true |

1.5通过Runnable接口创建线程

方法二:通过runnable接口创建线程

runnable接口
只有一个run()
Thread类实现了Runnable接口
便于多个线程共享资源
Java不支持多继承,如果已经继承了某个基类,便需要实现Runnable接口类生成多线程
以实现Runnable的对象为参数建立新的线程。
1、使用Runnable接口实现单一线程的例子
package com.ly.Runnable;
public class FactorialThreadTester {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(“main thread starts” );
FactorialThread t = new FactorialThread(10);//实现了Runnable的类
new Thread(t).start();//运行FactorialThread的run
System.out.println(“new thread started,main thread ends”);
}
}

package com.ly.Runnable;
public class FactorialThread implements Runnable{
private int num;
public FactorialThread(int num){
this.num = num;
}
@Override
public void run() {
// TODO Auto-generated method stub
int i = num;
int result = 1;
while(i>0){
result = result*i;
i-=1;
}
System.out.println(“The factorial of “+num+”is”+result);
System.out.println(“new thread ends”);
}
}
运行结果:
main thread starts
new thread started,main thread ends
The factorial of 10is3628800
new thread ends

2、使用Runnable接口实现多个线程的例子
package com.ly.moreRunnable;
public class ThreadSleepTester {
public static void main(String[] args) {
// TODO Auto-generated method stub
TestThread thread1 = new TestThread();
TestThread thread2 = new TestThread();
TestThread thread3 = new TestThread();
System.out.println(“Starting threads”);

  1. new Thread(thread1,"Thread1").start();
  2. new Thread(thread2,"Thread2").start();
  3. new Thread(thread3,"Thread3").start();
  4. System.out.println("Threads started,main ends\n");
  5. }

}

package com.ly.moreRunnable;
public class TestThread implements Runnable{
private int sleepTime;
public TestThread(){
sleepTime = (int)Math.random()*6000;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+”going to sleep for “+sleepTime);
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+”finished”);
}
}

运行结果:

两种线程构造方式的比较

  • 使用Runnable接口
    1. 可以将CPU,代码和数据分开,形成清晰的模型;还可以从其他类继承。
  • 直接继承Thread类
    1. 编写简单,直接继承,重写run方法,不能再从其他类继承。

    1.6线程内部的数据共享

    内容:如何实现线程数据的内部共享
  • 用同一个实现了Runnable接口的对象作为参数创建多个线程
  • 多个线程共享同一对象种的相同的数据。
    (同一个类对象构成的三个线程,他们是可以共享代码和数据。)
  1. 6.1、数据共享例子一
    package com.ly.shareRunnable;
    public class ShareTargetTester {

    public static void main(String[] args) {

    1. TestThread threadobj = new TestThread();
    2. System.out.println("Starting threads");
    3. new Thread(threadobj,"thread1").start();
    4. new Thread(threadobj,"thread2").start();
    5. new Thread(threadobj,"thread3").start();
    6. System.out.println("Threads started,main ends\n");

    }
    }

package com.ly.shareRunnable;
public class TestThread implements Runnable{
private int sleepTime;
public TestThread(){
sleepTime = (int)(Math.random()*6000);
}
@Override
public void run() {
try{
System.out.println(
Thread.currentThread().getName()+” going to sleep for “+sleepTime);
Thread.sleep(sleepTime);
}catch(InterruptedException exception){ }
System.out.println(Thread.currentThread().getName()+” finished”);

  1. }

}
运行结果:
Starting threads
Threads started,main ends

thread1 going to sleep for 4427
thread2 going to sleep for 4427
thread3 going to sleep for 4427
thread2 finished
thread3 finished
thread1 finished

结果说明:

  • 因为我们使用同一个Runnable类型对象创建的3个新的线程,这三个线程就共享了这个对象的私有成员sleepTime,在本次运行中,三个线程都休眠了同样的时间(4427)
  • 独立且同时运行的线程有时需要共享一些数据并且考虑到彼此的状态和动作。
  1. 6.2、数据共享例子二
    用三个线程模拟三个售票口,总共出售10张票
  • 用3个线程模仿3个售票口的售票行为
  • 这3个线程应该共享10张票的数据。
    package com.ly.sellTickets;
    public class SellTicketsTester {

    public static void main(String[] args) {

    1. SellTickets t = new SellTickets();
    2. new Thread(t,"1").start();
    3. new Thread(t,"2").start();
    4. new Thread(t,"3").start();

    }
    }

package com.ly.sellTickets;
public class SellTickets implements Runnable{
private int tickets = 10;
@Override
public void run() {
while(tickets>0){
System.out.println(Thread.currentThread().getName()+” is selling ticket “+tickets–);
}
}
}
运行结果:
3 is selling ticket 8
3 is selling ticket 7
3 is selling ticket 6
1 is selling ticket 10
1 is selling ticket 4
2 is selling ticket 9
2 is selling ticket 2
2 is selling ticket 1
1 is selling ticket 3
3 is selling ticket 5

结果说明:

  • 在这个例子中,创建了3个线程,每个线程调用的是同一个SellTickets对象中run()方法,访问的是同一个对象中的变量(tickets)
    如果是通过创建Thread类的子类来模拟售票过程,再创建3个新线程,则每个线程都会有各自的方法和变量,虽然方法是相同的但变量却是各有10张票,结果就和原来设想的不一样了。

    1.7 本章小结

    本章内容
    线程基础知识
    线程间的数据共享
    本章要求
    了解线程的概念
    学会如何通过Thread类和Runnable接口创建线程,如何实现多线程的数据共享
你的支持将鼓励作者继续创作

评论(0)

(无)