多线程编程知识点导航
1 多线程基础
2 Thread和Runnable多线程实现方法
3 线程优先级,睡眠与中断
4 线程结束的原因
5 多线程的同步
6 练习
1、多线程基础
进程:每执行一个程序都会在操作系统中建立一个进程。进程是操作系统中管理程序的最小单位。进程可以分为主进程和子进程。当进程中的所有子进程和子线程关闭结束后,进程才会关闭。
线程:线程是进程的组成单位,一个进程可由多个线程组成。线程可分为一个主线程和一个或多个子线程。一般子线程关闭后,子线程仍可执行一段时间。
一个进程只有一个主线程,但可以有多个子线程。
线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。 说明:使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。
线程的生命周期:新建、就绪、运行、堵塞和销毁。
2、Thread和Runnable多线程实现方法
创建线程的两种方式—线程类
1.继承Thread类 —— java.lang.Thread
2.实现Runnable接口 —— java.lang.Runnable
extends Thread线程开发步骤:
1编写一个类,继承java.lang.Thread类型
2.重写run方法,在run方法中编写程序的功能代码
3.执行时:实例化线程对象,并调用start方法启动线程。
extends Runnable线程开发步骤:
1、编写一个类,实现java.lang.Runnable接口
2、实现线程的run方法
3、运行一个线程:需要把Runnable接口实现类包装成Thread类的对象,才能执行start方法。
4、Runnable接口经常用于内部类编程
extends Thread实例
public class ThreadDemo extends Thread{
private int count = 0;
@Override
public void run() {
while(count<=10){
System.out.println(getId()+":"+count++);
try{
//线程休眠,时间单位是毫秒
Thread.sleep(new Random().nextInt(1000));
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.println("The child thread completes");
}
public static void main(String[] args) {
//1.新建线程
ThreadDemo s = new ThreadDemo();
//线程的run方法可以用非线程方法调用形式进行调用
//s.run();
//2.启动线程调用start方法,线程启动后会进入线程池中等待执行。
//3.线程执行时,执行的是run方法。
//4.线程等待(线程中断)
//5.run方法执行完毕则线程执行完毕。
s.start();
System.out.println("The thread is finished");
}
}
extends Runnable实例
public class RunnableDemo implements Runnable{
private int count=2;
@Override
public void run() {
try {
Thread.sleep(new Random().nextInt(2000));
System.out.println(count++);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
RunnableDemo s = new RunnableDemo();
//s.run();
//s.start();注意:实现Runnable没有start方法
//如果使用Runnable实现接口时,需要把Runnable接口实现类包装成Thread类的对象
//才能执行start方法。
Thread thread = new Thread(s);
thread.start();
}
}
3、线程优先级,睡眠与中断
优先级:
Java线程的优先级用1~10的整数来表示,越小则优先级越低。但是Java的优先级是高度依赖于操作系统的实现的。
getPriority( ) —— 确定线程的优先级
setPriority( ) —— 设置线程的优先级
睡眠:
语句sleep()
让线程中止一段时间的静态方法,Thread.sleep(long millis) — 暂时停止执行millis毫秒 在睡眠期满的瞬间,再次调用该线程不一定会恢复它的执行。
终止:
自动终止 — 一个线程完成执行后,不能再次运行。
可通过使用一个标志指示 run 方法退出,从而终止线程。
4、线程结束的原因
1、run方法执行完成,线程正常结束
2、线程抛出一个未捕获的Exception或者Error
3、直接调用该线程的Stop方法结束线程(已过时,因为容易造成死锁)
注:当一个优先级高的线程进入就绪状态时,它只是有较高的概率能够抢到CPU的执行权,不是一定就能抢到执行权。当前线程调用sleep()方法或者wait()方法时,只是暂时停止了该线程的运行,不是终止线程。当创建一个新的线程时,该线程也加入到了抢占cpu执行权的队伍中,但是是否能抢到,并不清楚。
5、多线程的同步
实现同步的两种方式:
synchronized方法
synchronized void methodA(){};
synchronized语句:
synchronized(Object){};
一旦一个包含同步方法(用synchronized修饰)的线程被CPU调用,其他线程就无法调用相同对象的同步方法。当一个线程在一个同步方法内部,所有试图调用该方法的同实例的其他线程必须等待。
6、练习
1.利用Runnable实现,要求多线程求解某范围素数每个线程负责1000范围:线程1找1-1000;线程 2 找 1001-2000;线程 3 找2001-3000。编程程序将每个线程找到的素数及时打印。
public class PrimeNumber implements Runnable {
private int start;
private int end;
public PrimeNumber() {
super();
}
public PrimeNumber(int start, int end) {
super();
this.start = start;
this.end = end;
}
public boolean isPrime(long start) {
if (start == 2) {
return true;
}
for (int i = 2; i < start; i++) {
if ((start % i) == 0) {
return false;
}
}
return true;
}
@Override
public void run() {
while (true) {
if (start > end) {
return;
}
if (isPrime(start)) {
System.out.println(start);
}
start++;
}
}
public static void main(String[] args) {
PrimeNumber s1 = new PrimeNumber(1, 1000);
Thread a1 = new Thread(s1);
a1.start();
PrimeNumber s2 = new PrimeNumber(1001, 2000);
Thread a2 = new Thread(s2);
a2.start();
PrimeNumber s3 = new PrimeNumber(2001, 3000);
Thread a3 = new Thread(s3);
a3.start();
}
}
2.编写一个Java程序(包括一个主程序类,一个线程类。在主程序类中创建2个线程,将其中一个线程的优先级设为10,另一个线程的优先级设为6。让优先级为10的线程打印200次“线程1正在运行”,优先级为6的线程打印200次“线程2正在运行”。
public class Subject3 extends Thread{
private String name;
public Subject3() {
super();
}
public Subject3(String name) {
super();
this.name = name;
}
@Override
public void run() {
for(int i=0;i<200;i++){
System.out.println("线程"+name+"正在运行");
}
}
public static void main(String[] args) {
Subject3 s1 = new Subject3("1");
s1.setPriority(10);
s1.start();
Subject3 s2 = new Subject3("2");
s2.setPriority(10);
s2.start();
}
}
3.编写一个计时器,每隔一秒钟,在控制台打印出最新时间。
public class Subject4 extends Thread{
public void run(){
int count = 1;
while(count<=60){
SimpleDateFormat s = new SimpleDateFormat("HH:mm:ss");
System.out.println(s.format(new Date()));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count++;
}
}
public static void main(String[] args) {
Subject4 s = new Subject4();
s.start();
}
}
注意:SimpleDateFormat 是线程不安全的类,一般不要定义为static变量,如果定义为static,必须加锁,或者使用DateUtils工具类。
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};