1.进程和多线程的概念及线程的优点
进程:进程是操作系统结构的基础,是一次程序的执行,是一个程序及其数据在处理机上顺序执行时所发生的活动;是程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。
线程:可以理解成是在进程中独立运行的子任务。
多线程的优点:同一时间内运行更多不同种类的任务,提高CPU利用率,是系统效率大大得到提升。
2.使用多线程
一个进程在运行时至少有一个线程在运行。
2.1 继承Thread类
创建一个线程的方式有两种,一种是继承Thread类,一种是实现Runnable接口。使用Thread类时最大的局限就是不支持多继承,因为Java语言时单继承的。所以为了实现多线继承,完全可以实现Runnable接口,二者没有什么本质的区别。
Thread类结构
public class Thread implements Runnable {
}
Thread类实现了Runnable接口,它们之间具有多态关系。
public class MyThread extends Thread {
@Override
public void run(){
super.run();
System.out.println("MyThread is running!");
}
}
2.2 实现Runnable接口
public class MyRunnable implements Runnable {
public void run() {
System.out.println("my thread is running ...");
}
}
Thread类也支持传入一个Ruunable接口的对象
public class Main {
public static void main(String[] args) {
MyRunnable thread = new MyRunnable();
thread.run();
Runnable runnable = new MyRunnable();
Thread thread1 = new Thread(runnable);
thread1.start();
}
}
2.3 实例变量与线程安全
自定义线程类中的实例变量针对其他线程可以有共享和不共享之分,这在多线程之间进行交互时是一个很重要的技术点。
(1)不共享数据的情况
public class MyThread extends Thread {
private int count = 5;
public MyThread (String name){
super();
this.setName(name);
}
@Override
public void run() {
super.run();
while (count > 0){
count--;
System.out.println("由"+ this.currentThread().getName() + "计算, count=" + count);
}
}
}
public class Main {
public static void main(String[] args) {
MyThread thread = new MyThread("A");
MyThread thread1 = new MyThread("B");
MyThread thread2 = new MyThread("C");
thread.start();
thread1.start();
thread2.start();
}
}
每个线程有各自的count变量,自己减少自己的count变量。这就是变量不共享。
(2)共享数据的情况
public class Section4Thread extends Thread {
private int count = 5;
@Override
public void run() {
super.run();
System.out.println("由" + Thread.currentThread().getName() + "计算count:" + count);
count--;
}
}
public class Run {
public static void main(String[] args) {
Section4Thread thread = new Section4Thread();
Thread a = new Thread(thread, "A");
Thread b = new Thread(thread, "B");
Thread c = new Thread(thread, "C");
Thread d = new Thread(thread, "D");
Thread e = new Thread(thread, "E");
a.start();
b.start();
c.start();
d.start();
e.start();
}
}
也可以用synchronized关键字来同步方法实现。
3. currentThread()方法
currentThread()方法返回当前代码段正在被哪个线程调用的信息。
4. isAlive()方法
isAlive()方法判断当前的线程是否处于活动状态。
Thread.currentThread 和 this的差异
public class Section5Thread extends Thread {
@Override
public void run() {
System.out.println("Thread.currentThread(): " + Thread.currentThread().getName() + Thread.currentThread().isAlive());
System.out.println("this : "+this.getName() + this.isAlive() );
}
}
public class Main {
public static void main(String[] args) {
Section5Thread t = new Section5Thread();
Thread thread = new Thread(t);
thread.setName("AAA");
thread.start();
}
}
5. sleep()方法
在指定的毫秒数内让当前“正在执行的线程”休眠(暂停执行),这个“正在执行的线程”指的是this.currentThread()返回的线程。
思考:线程的run()方法和start()方法的差别?
6. getId()方法
getId()方法的作用是取得线程的唯一标识。
7. 停止线程
停止一个线程意味着在线程处理完任务之前停掉正在做的操作,也就是放弃当前的操作。 介绍三种方式停止线程
1.使用退出标志,使线程正常退出,也就是当run方法完成后线程停止。
2.使用stop方法强行终止线程,但不推荐,以为stop()和suspend()及resume()一样,都是作废过期的方法,使用它们可能产生不可预料的后果。
3.使用Interrupt方法中断线程
7.1 停止不了的线程
public class MyThread extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 50000; i++) {
System.out.println("i = " + (i + 1));
}
}
}
public class Main {
public static void main(String[] args) {
MyThread thread = new MyThread();
try {
thread.start();
Thread.sleep(2000);
thread.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
执行后,日志确实是5000行,线程并没有中断,也就是说调用interrupt()方法并没有中断线程。那如何停止线程呢?
7.2 判断线程是否是停止状态
this.interrupted():测试当前线程是否已经中断。—> 测试当前线程是否已经是中断状态,执行后具有将清除状态标志为false的功能。
ths.isInterrupted():测试线程是否已经中断。—> 测试线程Thread对象是否已经是中断状态,但不清楚状态标志。
总结:interrupted()告诉当前线程,执行完之后,就要被中断。isInterrupted()询问线程是否中断了?
7.3 能停止的线程——异常法
靠throw出InterruptException()然后捕获该异常来停止线程。
7.4 沉睡中停止
7.5 stop()暴力停止
直接调用stop()方法。这样会抛出ThreadDeath()异常,通常情况不需要显示捕捉。
stop()方法已经作废,如果强制让线程停止有可能会造成一些请理性工作得不到完成,以及对锁定的对象就行了“解锁”,导致数据得不到同步处理,出现数据不一致的情况。使用stop()方法释放锁将会给数据造成不一致的后果。
7.6 使用return停止线程
public class MyThread extends Thread {
@Override
public void run() {
while (true) {
if(this.isInterrupted()) {
System.out.println("thread was stopped !");
return;
}
}
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(2000);
thread.interrupt();
}
}
8. 暂停线程
暂停线程意味着可以恢复运行,使用Thread.suspend()和Thread.resume()方法来是暂停和恢复线程。(注:这两方法都被标记为过期作废的方法,因此不推荐使用)
它们的缺点——独占,使用不当时,极易造成公共的同步对象的独占,使得其他线程无法访问公共的同步对象。
9.yield方法
yield()方法的作用是放弃当前的CPU资源,将它让给其他的任务去占用CPU执行时间,但放弃时间不一定,有可能刚刚放弃,马上又获得CPU时间片。
10.线程的优先级
设置线程优先级使用setPriority()方法。在Java中,线程的优先级分为1~10这10个等级。如果大于10或者小于1,则JDK抛出异常throw new IllegalArgumengException()。
setPriority()方法源代码如下
public final void setPriority(int newPriority) {
ThreadGroup g;
checkAccess();
if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY){
throw new IllegalArgumentException();
}
if((g = getThreadGroup()) != null) {
if(newPriority > g.getMaxPriority()){
newPriority = g.getMaxPriority();
}
setPriority0(priority = newPriority);
}
}
1.线程优先级的继承特性
A线程启动B线程,则B线程的优先级与A线程的优先级是一致的。
2.线程优先级的具有规则性
高优先级的线程总是大部分先执行完,但不代表高优先级的线程全部先执行完。也就是说线程的优先级具有一定的规则性的,CPU尽量将执行资源分配给优先级高的线程。
3.优先级具有随机性
优先级高的线程并不一定每一次都先执行完run()方法中的任务。
11.守护线程
Thread.setDaemon(true); //设置线程为守护线程。
当线程中存在非守护线程时,守护线程就存在。
概念: 只要当前JVM实例中存在任何一个非守护线程没有结束,守护线程就在工作,只有当最后一个非守护线程结束时,守护线程才随着JVM一同结束工作。它的作用就是为线程提供便利服务。守护线程最典型的应用就是GC(垃圾回收器)。