博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
第一章 Java多线程技能
阅读量:7233 次
发布时间:2019-06-29

本文共 35274 字,大约阅读时间需要 117 分钟。

hot3.png

进程和多线程的概念及其优点

进程是操作系统结构的基础,是一次程序的执行;是一个程序及其数据在处理机上顺序执行时所发生的活动;是程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。进程是受操作系统管理的基本运行单元。

线程可以理解成是在进程中独立运行的子任务。比如QQ.exe运行时就有很多的子任务在同时运行。再如,好友视频线程、下载文件线程、传输数据线程、发送表情线程等,这些不同的任务或者说功能都可以同时运行,其中每一项任务完全可以理解成是“线程”在工作,传文件、听音乐、发送图片表情等功能都有对应的线程在后台默默地运行。

多线程的优点:可以最大限度的利用CPU的空闲时间来处理其他的任务,而CPU在这些任务之间不停的切换,由于切换的速度非常快,给使用者的感受就是这些任务似乎在同时运行。所以使用多线程技术后,可以在同一时间内运行更多不同种类的任务。

单任务的特点就是排队执行,也就是同步,就像在cmd中输入一条命令后,必须等待这条命令执行完才可以执行下一条命令一样。这就是单任务环境的缺点,即CPU利用率大幅降低。而多线程情况下,CPU完全可以在任务1和任务2之间来回切换,使任务2不必等到10秒再运行,系统的运行效率大大得到提升,这就是多线程技术的优点,使用多线程也就是在使用异步,千万不要把开发环境里代码的顺序当成线程执行的顺序,线程被调用的时机是随机的。

使用多线程

继承Thread 类

在Java 的JDK 开发包中,已经自带了对多线程技术的支持,可以很方便地进行多线程编 程。实现多线程编程的方式主要有两种,一种是继承Thread 类,另一种是实现Runnable 接口。

package com.mythread.www;public class MyThread extends Thread {	@Override	public void run() {			super.run();			System.out.println("MyThread");		}}
package test;import com.mythread.www.MyThread;public class Run {public static void main(String[] args) {		MyThread mythread = new MyThread();		mythread.start();		System.out.println(" 运行结束! ");	}}

输出结果

运行结束!MyThread

MyThread.java 类中的run 方法执行的时间比较晚,这也说 明在使用多线程技术时,代码的运行结果与代码执行顺序或调用顺序是无关的。

实现Runnable 接口

如果欲创建的线程类已经有一个父类了,这时就不能再继承自Thread 类了,因为Java 不支持多继承,所以就需要实现Runnable 接口来应对这样的情况。

package myrunnable;public class MyRunnable implements Runnable {	@Override	public void run() {		System.out.println(" 运行中!");	}	public static void main(String[] args) {		Runnable runnable=new MyRunnable();		Thread thread=new Thread(runnable);		thread.start();		System.out.println(" 运行结束!");	}}

输出结果

运行结束!运行中!

那也就意味着构造函数Thread(Runnable target) 不光可以传入Runnable 接口的对象,还 可以传入一个Thread 类的对象,这样做完全可以将一个Thread 对象中的run() 方法交由其他 的线程进行调用。

实例变量与线程安全

自定义线程类中的实例变量针对其他线程可以有共享与不共享之分,这在多个线程之间 进行交互时是很重要的一个技术点。

不共享数据的情况

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 Run {	public static void main(String[] args) {		MyThread a=new MyThread("A");		MyThread b=new MyThread("B");		MyThread c=new MyThread("C");		a.start();		b.start();		c.start();	}}

一共创建了3 个线程,每个线程都有各自的count 变量,自己减少 自己的count 变量的值。这样的情况就是变量不共享,此示例并不存在多个线程访问同一个实例变量的情况。

共享数据的情况就是多个线程可以访问同一个变量,比如在实现投票功能的软件时,多个线程可以同时处理同一个人的票数。

public class MyThread extends Thread {	 private int count=5;    @Override    public void run() {        super.run();        count--;        System.out.println(" 由 "+Thread.currentThread().getName()+" 计算, count="+count);    }}public class Run {	public static void main(String[] args) {		MyThread mythread=new MyThread();		Thread a=new Thread(mythread,"A");		Thread b=new Thread(mythread,"B");		Thread c=new Thread(mythread,"C");		Thread d=new Thread(mythread,"D");		Thread e=new Thread(mythread,"E");		a.start();		b.start();		c.start();		d.start();		e.start();	}}

输出结果

由 B 计算, count=3 由 A 计算, count=3 由 C 计算, count=2 由 D 计算, count=1 由 E 计算, count=0

线程A 和B 打印出的count 值都是 3,说明A 和B 同时对count 进行处理,产生了“非线程安全”问 题。 在某些JVM 中,i-- 的操作要分成如下3 步:

1 取得原有i 值。2 计算i-1。3 对i 进行赋值。在这3 个步骤中,如果有多个线程同时访问,那么一定会出现非线程安全问题。

在这里占不解决同步问题,读者可以自行思考。

留意i-- 与System.out.println() 的异常

本节将通过程序案例细化一下println() 方法与i++ 联合使用时“有可能”出现的另外一种异常情况,并说明其中的原因。

package extthread;public class MyThread extends Thread {	private int i = 5;	@Override	public void run() {		System.out.println("i=" + (i--) + " threadName="		+ Thread.currentThread().getName());		// 注意:代码i-- 由前面项目中单独一行运行改成在当前项目中在println() 方法中直接进行打印	}}
package test;import extthread.MyThread;public class Run {	public static void main(String[] args) {		MyThread run = new MyThread();		Thread t1 = new Thread(run);		Thread t2 = new Thread(run);		Thread t3 = new Thread(run);		Thread t4 = new Thread(run);		Thread t5 = new Thread(run);		t1.start();		t2.start();		t3.start();		t4.start();		t5.start();	}}

输出结果

i=5 threadName=Bi=4 threadName=Ci=5 threadName=Ai=2 threadName=Di=3 threadName=E

虽然println() 方法在内部是同步的,但i-- 的操作却是在进入 println() 之前发生的,所以有发生非线程安全问题的概率。

/**     * Prints a String and then terminate the line.  This method behaves as     * though it invokes {@link #print(String)} and then     * {@link #println()}.     *     * @param x  The String to be printed.     */    public void println(String x) {        synchronized (this) {            print(x);            newLine();        }    }

currentThread()

currentThread() 方法可返回代码段正在被哪个线程调用的信息。

/** * Created by Lee on 2018/7/25. */public class MyThread extends  Thread {    public MyThread () {        System.out.println("构造方法打印的是:" + Thread.currentThread().getName());    }    @Override    public void run() {        System.out.println("run方法打印的是" + Thread.currentThread().getName());    }    public static void main(String[] args) {        MyThread thread = new MyThread();        thread.start();		//thread.start();    }}

输出结果

构造方法打印的是:mainrun方法打印的是Thread-0//run方法打印的是main

再来一个比较复杂的

package mythread;public class CountOperate extends Thread {	public CountOperate() {		System.out.println("CountOperate---begin");		System.out.println("Thread.currentThread().getName()="		+ Thread.currentThread().getName());		System.out.println("this.getName()=" + this.getName());		System.out.println("CountOperate---end");	}	@Override	public void run() {		System.out.println("run---begin");		System.out.println("Thread.currentThread().getName()="		+ Thread.currentThread().getName());		System.out.println("this.getName()=" + this.getName());		System.out.println("run---end");	}	public static void main(String[] args) {		CountOperate c = new CountOperate();		Thread t1 = new Thread(c);		t1.setName("A");		t1.start();	}}

输出结果

CountOperate---beginThread.currentThread().getName()=mainthis.getName()=Thread-0CountOperate---endrun---beginThread.currentThread().getName()=Athis.getName()=Thread-0run---end

因为Thread.currentThread()是当前代码段正在那个线程调用,CountOperate的构造函数是有main主线程调用的,run是由Thread线程调用。同时线程的默认名称是Thread-(No),这点可以有Thread类的构造函数看出。其中一个构造函数如下:

public Thread(Runnable target) {     init(null, target, "Thread-" + nextThreadNum(), 0);} /* For autonumbering anonymous threads. */ private static int threadInitNumber; private static synchronized int nextThreadNum() {     return threadInitNumber++;}

重点也就是最后一个this.getName() 为什么是Thread-0? 由上面的Thread构造函数可以看出当使用一个Thread对象作为参数去实例化一个Thread对象时,实现Thread的线程类被缓存进了target对象,而当调用run()方法时,Thread类是这样实现的

/**     * If this thread was constructed using a separate     * Runnable run object, then that     * Runnable object's run method is called;     * otherwise, this method does nothing and returns.     * 

* Subclasses of Thread should override this method. * * @see #start() * @see #stop() * @see #Thread(ThreadGroup, Runnable, String) */ @Override public void run() { if (target != null) { target.run(); } }

 isAlive() 方法

方法isAlive() 的功能是判断当前的线程是否处于活动状态。

/** * @program: Demo * @description: java基础知识测试 * @author: lee * @create: 2018-07-18 09:45 **/public class Demo  extends Thread{    @Override    public void run() {        System.out.println("run=" + this.isAlive());    }    public static void main(String[] args) {        Demo mythread = new Demo();        System.out.println("begin ==" + mythread.isAlive());        mythread.start();        System.out.println("end ==" + mythread.isAlive());    }}

输出结果

begin ==falseend ==truerun=true

方法isAlive() 的作用是测试线程是否处于活动状态。什么是活动状态呢?活动状态就是线程已经启动且尚未终止。线程处于正在运行或准备开始运行的状态,就认为线程是“存活”的。

需要说明一下,如以下代码:

System.out.println("end ==" + mythread.isAlive()); 虽然在上面的示例中打印的值是true,但此值是不确定的。打印true 值是因为mythread线程还未执行完毕,所以输出true。如果代码更改如下:

public static void main(String[] args) throws InterruptedException {	MyThread mythread = new MyThread();	System.out.println("begin ==" + mythread.isAlive());	mythread.start();	Thread.sleep(1000);	System.out.println("end ==" + mythread.isAlive());}

输出结果

begin ==falserun=trueend ==false

另外,在使用isAlive() 方法时,如果将线程对象以构造参数的方式传递给Thread 对象进行start() 启动时,运行的结果和前面示例是有差异的。造成这样的差异的原因还是来自于Thread.currentThread() 和this 的差异。

/** * @program: CountOperate * @description: java基础知识测试 * @author: lee * @create: 2018-07-18 09:45 */public class CountOperate extends Thread {    public CountOperate() {        System.out.println("CountOperate---begin");        System.out.println("Thread.currentThread().getName()="                + Thread.currentThread().getName());        System.out.println("Thread.currentThread().isAlive()="                + Thread.currentThread().isAlive());        System.out.println("this.getName()=" + this.getName());        System.out.println("this.isAlive()=" + this.isAlive());        System.out.println("CountOperate---end");    }    @Override    public void run() {        System.out.println("run---begin");        System.out.println("Thread.currentThread().getName()="                + Thread.currentThread().getName());        System.out.println("Thread.currentThread().isAlive()="                + Thread.currentThread().isAlive());        System.out.println("this.getName()=" + this.getName());        System.out.println("this.isAlive()=" + this.isAlive());        System.out.println("run---end");    }    public static void main(String[] args) {        CountOperate c = new CountOperate();        Thread t1 = new Thread(c);        t1.setName("A");        t1.start();    }}

输出结果

CountOperate---beginThread.currentThread().getName()=mainThread.currentThread().isAlive()=truethis.getName()=Thread-0this.isAlive()=falseCountOperate---endrun---beginThread.currentThread().getName()=AThread.currentThread().isAlive()=truethis.getName()=Thread-0this.isAlive()=falserun---end

另一种方式

/** * @program: CountOperate * @description: java基础知识测试 * @author: lee * @create: 2018-07-18 09:45 */public class CountOperate extends Thread {    public CountOperate() {        System.out.println("CountOperate---begin");        System.out.println("Thread.currentThread().getName()="                + Thread.currentThread().getName());        System.out.println("Thread.currentThread().isAlive()="                + Thread.currentThread().isAlive());        System.out.println("this.getName()=" + this.getName());        System.out.println("this.isAlive()=" + this.isAlive());        System.out.println("CountOperate---end");    }    @Override    public void run() {        System.out.println("run---begin");        System.out.println("Thread.currentThread().getName()="                + Thread.currentThread().getName());        System.out.println("Thread.currentThread().isAlive()="                + Thread.currentThread().isAlive());        System.out.println("this.getName()=" + this.getName());        System.out.println("this.isAlive()=" + this.isAlive());        System.out.println("run---end");    }    public static void main(String[] args) {        CountOperate c = new CountOperate();       c.start();    }}

输出结果

CountOperate---beginThread.currentThread().getName()=mainThread.currentThread().isAlive()=truethis.getName()=Thread-0this.isAlive()=falseCountOperate---endrun---beginThread.currentThread().getName()=Thread-0Thread.currentThread().isAlive()=truethis.getName()=Thread-0this.isAlive()=truerun---end

sleep() 方法

方法sleep() 的作用是在指定的毫秒数内让当前“正在执行的线程”休眠(暂停执行)。这个“正在执行的线程”是指this.currentThread() 返回的线程。

/** * @program: CountOperate * @description: java基础知识测试 * @author: lee * @create: 2018-07-18 09:45 */public class CountOperate extends Thread {    @Override    public void run() {        try {            System.out.println("run threadName="                    + Thread.currentThread().getName() + " begin");            Thread.sleep(2000);            System.out.println("run threadName="                    + Thread.currentThread().getName() + " end");        } catch (InterruptedException e) {// TODO Auto-generated catch block            e.printStackTrace();        }    }    public static void main(String[] args) {        CountOperate c = new CountOperate();        c.start();    }}

getId() 方法

getId() 方法的作用是取得线程的唯一标识

/** * @program: CountOperate * @description: java基础知识测试 * @author: lee * @create: 2018-07-18 09:45 */public class CountOperate extends Thread {    public static void main(String[] args) {        Thread runThread = Thread.currentThread();        System.out.println(runThread.getName() + " " + runThread.getId());    }}# 输出结果 main 1

停止线程

停止一个线程可以使用Thread.stop() 方法,但最好不用它。虽然它确 实可以停止一个正在运行的线程,但是这个方法是不安全的(unsafe),而且是已被弃用作废的(deprecated),在将来的Java 版本中,这个方法将不可用或不被支持。

大多数停止一个线程的操作使用Thread.interrupt() 方法,尽管方法的名称是“停止,中止”的意思,但这个方法不会终止一个正在运行的线程,还需要加入一个判断才可以完成线程的停止。

在Java 中有以下3 种方法可以终止正在运行的线程:

  • 使用退出标志,使线程正常退出,也就是当run 方法完成后线程终止。
  • 使用stop 方法强行终止线程,但是不推荐使用这个方法,因为stop 和suspend 及resume 一样,都是作废过期的方法,使用它们可能产生不可预料的结果。
  • 使用interrupt 方法中断线程。

停止不了的线程

本示例将调用interrupt() 方法来停止线程, 但interrupt() 方法的使用效果并不像for+break 语句那样,马上就停止循环。调用interrupt() 方法仅仅是在当前线程中打了一个停止的标记,并不是真的停止线程。

/** * @program: MyThread * @description: java基础知识测试 * @author: lee * @create: 2018-07-18 09:45 */public class MyThread extends Thread {    @Override    public void run() {        for (int i = 0; i < 500000; i++) {            System.out.println("i=" + (i + 1));        }    }    public static void main(String[] args) {        try {            MyThread thread = new MyThread();            thread.start();            thread.interrupt();        } catch (Exception e) {            System.out.println("main catch");            e.printStackTrace();        }    }}

输出结果

......i=499995i=499996i=499997i=499998i=499999i=500000

从运行的结果来看,调用interrupt 方法并没有停止线程。 如何停止线程呢,请继续向下读。

判断线程是否是停止状态

在介绍如何停止线程的知识点前,先来看一下如何判断线程的状态是不是停止的。在Java 的SDK 中,Thread.java 类里提供了两种方法。

this.interrupted():测试当前线程是否已经中断。

this.isInterrupted():测试线程是否已经中断。

/** * @program: demo * @description: java基础知识测试 * @author: lee * @create: 2018-07-18 09:45 **/public class Demo  extends Thread{    @Override    public void run() {        for (int i = 0; i < 50; i++) {            System.out.println("i=" + (i + 1));        }    }    public static void main(String[] args) throws InterruptedException {        try {            Demo thread = new Demo();            thread.start();            thread.interrupt();//Thread.currentThread().interrupt();            System.out.println(" 是否停止1 ? ="+thread.isInterrupted());            System.out.println(" 是否停止2 ? ="+thread.interrupted());        } catch (Exception e) {            System.out.println("main catch");            e.printStackTrace();        }        System.out.println("end!");    }}

输出结果

是否停止1 ? =true 是否停止2 ? =falseend!i=1...i=45i=46i=47i=48i=49i=50

判断thread 对象所代表的线程是否停止,但从控制台打印的结果来看,线程并未停止,这也就证明了interrupted() 方法的解释:测试当前线程是否已经中断。这个“当前线程”是main,它从未中断过,所以打印的结果是两个false。

第二种情况

/** * @program: demo * @description: java基础知识测试 * @author: lee * @create: 2018-07-18 09:45 **/public class Demo  extends Thread{    @Override    public void run() {        for (int i = 0; i < 50; i++) {            System.out.println("i=" + (i + 1));        }    }    public static void main(String[] args) throws InterruptedException {        try {            Demo thread = new Demo();            thread.start();            Thread.sleep(1000);            thread.interrupt();            System.out.println(" 是否停止1 ? ="+thread.isInterrupted());            System.out.println(" 是否停止2 ? ="+thread.interrupted());        } catch (InterruptedException e) {            System.out.println("main catch");            e.printStackTrace();        }        System.out.println("end!");    }}

输出结果

i=1...i=47i=48i=49i=50 是否停止1 ? =false 是否停止2 ? =falseend!

为什么两种结果不一样呢,因为休息一秒后,线程早已经结束了,所以不能中断了,interrupted() 方法的解释:测试当前线程是否已经中断。等价于

/** * @program: demo * @description: java基础知识测试 * @author: lee * @create: 2018-07-18 09:45 **/public class Demo  extends Thread{    @Override    public void run() {        for (int i = 0; i < 50; i++) {            System.out.println("i=" + (i + 1));        }    }    public static void main(String[] args) throws InterruptedException {        try {            Demo thread = new Demo();            thread.start();            Thread.sleep(1000);            thread.interrupt();            System.out.println(" 是否停止1 ? ="+thread.isInterrupted());            System.out.println(" 是否停止2 ? ="+Thread.interrupted());        } catch (InterruptedException e) {            System.out.println("main catch");            e.printStackTrace();        }        System.out.println("end!");    }}

最后,再来看一下这两个方法的解释。

this.interrupted() :测试当前线程是否已经是中断状态, 执行后具有将状态标志置清除为false 的功能。

this.isInterrupted() :测试线程Thread 对象是否已经 是中断状态,但不清除状态标志。

能停止的线程——异常法

直接上代码

/** * @program: demo * @description: java基础知识测试 * @author: lee * @create: 2018-07-18 09:45 **/public class Demo extends Thread {    @Override    public void run() {        super.run();        for (int i = 0; i < 5000; i++) {            if (Thread.interrupted()) {                System.out.println(" 已经是停止状态了! 我要退出了!");                break;            }            System.out.println("i=" + (i + 1));        }		//System.out.println(" 我被输出,如果此代码是for 又继续运行,线程并未停止! ");    }    public static void main(String[] args) throws InterruptedException {        Demo thread = new Demo();        thread.start();        thread.interrupt();        System.out.println(" 是否停止1 ? =" + thread.isInterrupted());        System.out.println(" 是否停止2 ? =" + Thread.interrupted());        System.out.println("end!");    }}

输出结果

是否停止1 ? =true 是否停止2 ? =falseend! 已经是停止状态了! 我要退出了!

上面的示例虽然停止了线程,但如果for 语句下面还有语句,还是会继续运行的。

/** * @program: demo * @description: java基础知识测试 * @author: lee * @create: 2018-07-18 09:45 **/public class Demo extends Thread {    @Override    public void run() {        try {            for (int i = 0; i < 500000; i++) {                if (Thread.interrupted()) {                    System.out.println(" 已经是停止状态了! 我要退出了!");                    throw new InterruptedException();                }                System.out.println("i=" + (i + 1));            }            System.out.println(" 我在for 下面");        } catch (InterruptedException e) {            System.out.println(" 进MyThread.java 类run 方法中的catch 了! ");            e.printStackTrace();        }    }    public static void main(String[] args) throws InterruptedException {        Demo thread = new Demo();        thread.start();        thread.interrupt();        System.out.println(" 是否停止1 ? =" + thread.isInterrupted());        System.out.println(" 是否停止2 ? =" + Thread.interrupted());        System.out.println("end!");    }}

输出结果

是否停止1 ? =true是否停止2 ? =falseend!已经是停止状态了! 我要退出了!进MyThread.java 类run 方法中的catch 了! java.lang.InterruptedException	at Demo.run(Demo.java:14)

在沉睡中停止

如果在sleep状态下停止某一线程,会进入catch语句,并且清除停止状态值,使之变成false.

/** * @program: demo * @description: java基础知识测试 * @author: lee * @create: 2018-07-18 09:45 **/public class Demo extends Thread {    @Override    public void run() {        try {            System.out.println("run begin");            Thread.sleep(200000);            System.out.println("run end");        } catch (InterruptedException e) {            System.out.println("在沉睡中被停止! 进入catch!"+this.isInterrupted());            e.printStackTrace();        }    }    public static void main(String[] args) throws InterruptedException {        try {            Demo thread = new Demo();            thread.start();            Thread.sleep(200);            thread.interrupt();        } catch (InterruptedException e) {            System.out.println("main catch");            e.printStackTrace();        }        System.out.println("end!");    }}

输出结果

run beginend!在沉睡中被停止! 进入catch!falsejava.lang.InterruptedException: sleep interrupted	at java.lang.Thread.sleep(Native Method)	at Demo.run(Demo.java:12)

另一种情况

/** * @program: demo * @description: java基础知识测试 * @author: lee * @create: 2018-07-18 09:45 **/public class Demo extends Thread {    @Override    public void run() {        try {            for(int i=0;i<100000;i++){                System.out.println("i="+(i+1));            }            System.out.println("run begin");            Thread.sleep(200000);            System.out.println("run end");        } catch (InterruptedException e) {            System.out.println(" 先停止,再遇到了sleep! 进入catch!");            e.printStackTrace();        }    }    public static void main(String[] args) throws InterruptedException {        Demo thread = new Demo();        thread.start();        thread.interrupt();        System.out.println("end!");    }}

输出结果

...i=99998i=99999i=100000run begin 先停止,再遇到了sleep! 进入catch!

能停止的线程——暴力停止

使用stop() 方法停止线程则是非常暴力的,调用stop() 方法时会抛出java.lang.ThreadDeath 异常,但在通常的情况下,此异常不需 要显式地捕捉,方法stop() 已经被作废,因为如果强 制让线程停止则有可能使一些清理性的工 作得不到完成。另外一个情况就是对锁定 的对象进行了“解锁”,导致数据得不到 同步的处理,出现数据不一致的问题。

/** * @program: demo * @description: java基础知识测试 * @author: lee * @create: 2018-07-18 09:45 **/public class Demo extends Thread {    @Override    public void run() {        try {            for(int i=0;i<100000;i++){                System.out.println("i="+(i+1));                Thread.sleep(2000);            }        } catch (InterruptedException e) {            e.printStackTrace();        }    }    public static void main(String[] args) throws InterruptedException {        Demo thread = new Demo();        thread.start();        Thread.sleep(10000);        thread.stop();        System.out.println("end!");    }}

输出结果

i=1i=2i=3i=4i=5end!

stop停止导致数据不一致

/** * Created by Lee on 2018/7/28. */public class StopSynchronizedObject {    private String username = "a";    private String password = "aa";    public String getUsername() {        return username;    }    public void setUsername(String username) {        this.username = username;    }    public String getPassword() {        return password;    }    public void setPassword(String password) {        this.password = password;    }    synchronized public void printString(String username, String password) {        try {            this.username = username;            Thread.sleep(8000);            this.password = password;        } catch (InterruptedException ex) {            ex.printStackTrace();        }    }}/** * Created by Lee on 2018/7/28. */public class StopSynchronizedThread extends Thread {    private StopSynchronizedObject object;    public  StopSynchronizedThread(StopSynchronizedObject object) {        this.object=object;    }    @Override    public void run() {        object.printString("b","bb");    }}/** * Created by Lee on 2018/7/28. */public class StopSynchronizedRun {    public static void main(String[] args) {        try {            StopSynchronizedObject object = new StopSynchronizedObject();            StopSynchronizedThread thread = new StopSynchronizedThread(object);            thread.start();            Thread.sleep(500);			thread.stop();            System.out.println(object.getUsername());            System.out.println(object.getPassword());        } catch (InterruptedException e) {            e.printStackTrace();        }    }}

输出结果

baa

再看一个例子

/** * Created by Lee on 2018/7/28. */public class StopSynchronizedRun {    public static void main(String[] args) {        try {            StopSynchronizedObject object = new StopSynchronizedObject();            StopSynchronizedThread thread = new StopSynchronizedThread(object);            thread.start();            //Thread.sleep(500);            thread.stop();            System.out.println(object.getUsername());            System.out.println(object.getPassword());        } catch (Exception e) {            e.printStackTrace();        }    }}

输出结果

aaa

使用return 停止线程

/** * @program: demo * @description: java基础知识测试 * @author: lee * @create: 2018-07-18 09:45 **/public class Demo extends Thread {    @Override    public void run() {        while (true) {            if (this.isInterrupted()) {                System.out.println("停止了!");                return;            }            System.out.println("timer=" + System.currentTimeMillis());        }    }    public static void main(String[] args) throws InterruptedException {        Demo thread = new Demo();        thread.start();        Thread.sleep(1000);        thread.interrupt();        System.out.println("end!");    }}

输出结果

timer=1532932687078timer=1532932687078end!停止了!

暂停线程

暂停线程意味着此线程还可以恢复运行。在Java 多线程中,可以使用suspend() 方法暂停线程,使用resume() 方法恢复线程的执行。

/** * @program: demo * @description: demo * @author: lee * @create: 2018-07-29 11:05 **/public class SuspendThread extends Thread {    private long i = 0;    public long getI() {        return i;    }    public void setI(long i) {        this.i = i;    }    @Override    public void run(){        while (true) {            i++;        }    }}    /** * Created by Lee on 2018/7/28. */public class SuspendThreadRun {    public static void main(String[] args) {        try {            SuspendThread thread = new SuspendThread();            thread.start();            Thread.sleep(1000);            //第一段            thread.suspend();            System.out.println("I="+thread.getI());            Thread.sleep(5000);            System.out.println("I="+thread.getI());            //第二段            thread.resume();            Thread.sleep(1000);            //第三段            thread.suspend();            System.out.println("I="+thread.getI());            Thread.sleep(5000);            System.out.println("I="+thread.getI());            thread.stop();        } catch (InterruptedException ex) {            ex.printStackTrace();        }    }}

输出结果

I=483701768I=483701768I=1001747085I=1001747085

suspend 与resume 方法的缺点——独占

在使用suspend 与resume 方法时,如果使用不当,极易造成公共的同步对象的独占,使得其他线程无法访问公共同步对象。

public class SynchronizedObject {    synchronized public void printString() {        System.out.println("begin");        if (Thread.currentThread().getName().equals("a")) {            System.out.println("a 线程永远 suspend 了! ");            Thread.currentThread().suspend();        }        System.out.println("end");    }}public class Run {    public static void main(String[] args) {        try {            final SynchronizedObject object = new SynchronizedObject();            Thread thread1 = new Thread() {                @Override                public void run() {                    object.printString();                }            };            thread1.setName("a");            thread1.start();            Thread.sleep(1000);            Thread thread2 = new Thread() {                @Override                public void run() {                    System.out.println("thread2 启动了,但进入不了printString() 方法! 只打印1 个begin");                    System.out.println(" 因为printString() 方法被a 线程锁定并且永远 suspend 暂停了! ");                    object.printString();                }            };            thread2.start();        } catch (InterruptedException e) {// TODO Auto-generated catch block            e.printStackTrace();        }    }}

还有另外一种独占锁的情况也要格外注意,稍有不慎,就会掉进“坑”里

public class MyThread extends Thread {    private long i = 0;    @Override    public void run() {        while (true) {            i++;        }    }}    public class Run {    public static void main(String[] args) {        try {            MyThread thread = new MyThread();            thread.start();            Thread.sleep(1000);            thread.suspend();            System.out.println("main end!");        } catch (InterruptedException e) {            e.printStackTrace();        }    }}

输出结果

main end!

如果更改程序如下

/** * @program: demo * @description: 2 * @author: lee * @create: 2018-07-30 14:52 **/public class test extends Thread{    private long i = 0;    @Override    public void run() {        while (true) {            i++;            System.out.println(i);        }    }    public static void main(String[] args) {        try {            test thread = new test();            thread.start();            Thread.sleep(1000);            thread.suspend();            System.out.println("main end!");        } catch (InterruptedException e) {            e.printStackTrace();        }    }}

输出结果

...113720113721113722113723

不再会有main end!输出,出现这样情况的原因是,当程序运行到println() 方法内部停止时,同步锁未被释放。

/**     * Prints a String and then terminate the line.  This method behaves as     * though it invokes {@link #print(String)} and then     * {@link #println()}.     *     * @param x  The String to be printed.     */    public void println(String x) {        synchronized (this) {            print(x);            newLine();        }    }

suspend 与resume 方法的缺点——不同步

在使用suspend 与resume 方法时也容易出现因为线程的暂停而导致数据不同步的情况。

public class MyObject {    private String username = "1";    private String password = "11";    public void setValue(String u, String p) {        this.username = u;        if (Thread.currentThread().getName().equals("a")) {            System.out.println(" 停止a 线程! ");            Thread.currentThread().suspend();        }        this.password = p;    }    public void printUsernamePassword() {        System.out.println(username + " " + password);    }}public class Run {    public static void main(String[] args) throws InterruptedException {        final MyObject myobject = new MyObject();        Thread thread1 = new Thread() {            public void run() {                myobject.setValue("a", "aa");            };        };        thread1.setName("a");        thread1.start();        Thread.sleep(500);        Thread thread2 = new Thread() {            public void run() {                myobject.printUsernamePassword();            };        };        thread2.start();    }}

输出结果

停止a线程a 11

yield 方法

yield() 方法的作用是放弃当前的CPU 资源,将它让给其他的任务去占用CPU 执行时间。但放弃的时间不确定,有可能刚刚放弃,马上又获得CPU 时间片。

/** * @program: demo * @description: demo * @author: lee * @create: 2018-07-29 11:21 **/public class YieldThread extends Thread {    @Override    public void run(){        long beginTime = System.currentTimeMillis();        int count = 0;        for (int i = 0; i < 5000000; i++) {           // Thread.yield();            count = count + (i+1);        }        long endTime=System.currentTimeMillis();        System.out.println("用时:" + (endTime-beginTime) + "毫秒!");    }    public static void main(String[] args) {        YieldThread thread = new YieldThread();        thread.start();    }}    # 输出结果 用时:2毫秒!# 没有注释Thread.yield();输出结果 用时:1809毫秒!

线程的优先级

在操作系统中,线程可以划分优先级,优先级较高的线程得到的CPU 资源较多,也就是CPU 优先执行优先级较高的线程对象中的任务,在Java 中,线程的优先级分为1 ~ 10 这10 个等级,如果小于1 或大于10,则JDK 抛出异常throw new IllegalArgumentException()。

JDK 中使用3 个常量来预置定义优先级的值,代码如下:

public final static int MIN_PRIORITY = 1;public final static int NORM_PRIORITY = 5;public final static int MAX_PRIORITY = 10;

线程优先级的继承特性

在Java 中,线程的优先级具有继承性,比如A 线程启动B 线程,则B 线程的优先级与A 是一样的。

/** * @program: demo * @description: MyThread1 * @author: lee * @create: 2018-07-30 15:38 **/public class MyThread1 extends Thread {    @Override    public void run() {        System.out.println("MyThread1 run priority=" + this.getPriority());        MyThread2 thread2 = new MyThread2();        thread2.start();    }}/** * @program: demo * @description: MyThread2 * @author: lee * @create: 2018-07-30 15:38 **/public class MyThread2 extends Thread {    @Override    public void run() {        System.out.println("MyThread2 run priority=" + this.getPriority());    }}/** * @program: demo * @description: demo * @author: lee * @create: 2018-07-30 15:39 **/public class RunDemo {    public static void main(String[] args) {        MyThread1 thread = new MyThread1();        thread.start();    }}

输出结果

MyThread1 run priority=5MyThread2 run priority=5

优先级具有规则性

虽然使用setPriority() 方法可以设置线程的优先级,但还没有看到设置优先级所带来的效果。

import java.util.Random;/** * @program: demo * @description: 线程优先级测试 * @author: lee * @create: 2018-07-29 11:37 **/public class PropertyThread1 extends Thread{    @Override    public void run() {        long beginTime = System.currentTimeMillis();        long addResult = 0;        for (int j = 0;j < 100; j++) {            Random random = new Random();            random.nextInt();            addResult = addResult +j;            long endTime = System.currentTimeMillis();            System.out.println("***************  PropertyThread1 use time= " +(endTime-beginTime));        }    }}import java.util.Random;/** * @program: demo * @description: 线程优先级测试 * @author: lee * @create: 2018-07-29 11:37 **/public class PropertyThread2 extends Thread{    @Override    public void run() {        long beginTime = System.currentTimeMillis();        long addResult = 0;        for (int j = 0;j < 100; j++) {            Random random = new Random();            random.nextInt();            addResult = addResult +j;            long endTime = System.currentTimeMillis();            System.out.println("#########  PropertyThread2 use time= " +(endTime-beginTime));        }    }}/** * @program: demo * @description: property测试 * @author: lee * @create: 2018-07-29 11:43 **/public class PropertyRun {    public static void main(String[] args) {        PropertyThread1 thread1 = new PropertyThread1();        thread1.setPriority(2);        thread1.start();        PropertyThread2 thread2 = new PropertyThread2();        thread2.start();        thread2.setPriority(6);    }}

输出结果

...#########  PropertyThread2 use time= 1***************  PropertyThread1 use time= 2#########  PropertyThread2 use time= 2***************  PropertyThread1 use time= 3#########  PropertyThread2 use time= 2...***************  PropertyThread1 use time= 11***************  PropertyThread1 use time= 11***************  PropertyThread1 use time= 11***************  PropertyThread1 use time= 11***************  PropertyThread1 use time= 11

高优先级的线程总是大部分先执行完,但不代表高优先级的线 程全部先执行完。另外,不要以为thread1 线程先被main 线程所调用就会先执行完,当线程优先级的等级差距很大时,谁先执行完和代码的调用顺序无关,线程的优先级具有一定的规则性,也就是CPU 尽量将执行资源让给优先级比较高的线程。

优先级具有随机性

线程的优先级还具有“随机性”,也就是优先级较高的线程不一定每一次 都先执行完,当优先级的差距不大的时候最能体现出随机性。

看谁运行得快

线程的优先级越高,CPU分配得到的机会越大,所以执行的时间越多。

守护线程

在Java 线程中有两种线程,一种是用户线程,另一种是守护线程。

守护线程是一种特殊的线程,它的特性有“陪伴”的含义,当进程中不存在非守护线程了,则守护线程自动销毁。典型的守护线程就是垃圾回收线程,当进程中没有非守护线程了,则垃圾回收线程也就没有存在的必要了,自动销毁。

用个比较通俗的比喻来解释一下 “守护线程”:任何一个守护线程都是整个JVM 中所有非守护线程的“保姆”,只要当前JVM 实例中存在任何一个非守护线程没有结束,守护线程就在工作,只有当最后一个非守护线程 结束时,守护线程才随着JVM 一同结束工作。 注意点:

thread.setDaemon(true)必须在thread.start()之前设置。在Daemon线程中产生的新线程也是Daemon的。不是所有的应用都可以分配给Daemon线程来进行服务,比如读写操作或者计算逻辑。因为Daemon Thread还没来得及进行操作,虚拟机可能已经退出了。
/** * @program: demo * @description: 守护线程 * @author: lee * @create: 2018-07-29 12:04 **/public class DaemonThread extends Thread{    private int  i = 0;    @Override    public void run() {        while (true) {            try {                System.out.println(i++);                Thread.sleep(1000);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}/** * @program: demo * @description: 守护进程 * @author: lee * @create: 2018-07-29 12:06 **/public class DaemonRun {    public static void main(String[] args) throws InterruptedException {        DaemonThread thread = new DaemonThread();        thread.setDaemon(true);        thread.start();        Thread.sleep(5000);   System.out.println("***************************************************************再也不会打印东西了。。。。");    }}

输出结果

01234***************************************************************再也不会打印东西了。。。。

如果多线程类改变一下

/** * @program: demo * @description: 守护线程 * @author: lee * @create: 2018-07-29 12:04 **/public class DaemonThread extends Thread{    private int  i = 0;    @Override    public void run() {        while (true) {            System.out.println(i++);        }    }}

输出结果

...106250106251106252***************************************************************再也不会打印东西了。。。。106253106254106255...106341Process finished with exit code 0

转载于:https://my.oschina.net/jiansin/blog/1919774

你可能感兴趣的文章
winform程序textbox滚动条保持在最下面 内容不闪烁
查看>>
2013年中国区Skyline软件价格体系
查看>>
解决错误提示: 未找到 Oracle 客户端和网络组件。
查看>>
架构系列:逻辑分层总结
查看>>
Scala 深入浅出实战经典 第41讲:List继承体系实现内幕和方法操作源码揭秘
查看>>
设计模式(Abstract Factory)抽象工厂
查看>>
修改工程名称
查看>>
使用intellij idea搭建MAVEN+springmvc+mybatis框架
查看>>
core dump + LINUX 内核系列博客
查看>>
curl使用例子
查看>>
【面试】二维数组中找数字
查看>>
享元模式
查看>>
macOS 升级到了10.12.1
查看>>
解决 uuid.h找不到的问题
查看>>
ubuntu 编译运行 opencv C++ 项目
查看>>
Node入门教程(10)第八章:Node 的事件处理
查看>>
C++和java中构造函数与析构函数的调用顺序
查看>>
第一届《FineUI 你写博客,我送书》活动开始了!
查看>>
超酷!纯CSS3烧烤动画实现教程
查看>>
看日记学git摘要~灰常用心的教程
查看>>