多线程基础知识

来源:互联网 发布:我的购物车淘宝网登录 编辑:程序博客网 时间:2024/06/15 14:46

java多线程

进程

什么是进程

进程是操作系统结构的基础,是资源分配的基本单位,也是独立运行的基本单位。

进程的两个基本属性

  • 进程是一个拥有资源的独立单位
  • 进程同时是一个可以被处理器独立调度和分配的单位

为什么会有线程?

操作系统为了能让进程并发执行,还需要创建进程、撤销进程和进程切换,进行这些操作时,
操作系统要为进程分配资源及回收资源,为运行进程保留现场信息,这些工作都需要付出较多的时空开销。
所以为了能让程序更好的并发执行,尽量减小操作系统的开销,操作系统设计者引入了线程,让线程去完成第二个基本属性的任务,而进程只完成第一个基本属性的任务。线程是进程中独立运行的子任务。


一个进程正在运行的时候至少会有一个线程在运行。

实现多线程编程的方式

1、继承Thread类,事实上Thread类本身也是实现Runnable接口

2、实现了Runnable接口

运行线程

It is never legal to start a thread more than once.In particular, a thread may not be restarted once it has completedexecution.

使用Thread时需要调用start方法才能让线程开始运行,但是多次调用start方法会产生异常,即使run方法执行结束了也不行!

Exception in thread "main" java.lang.IllegalThreadStateException

多线程是异步的,线程被调用的时机也是随机的。

但是如果调用的是Thread类的run方法,那么就是同步执行的了。因为只有调用start方法才能让线程开始执行。

多线程共享数据的时候可能会产生不同步的问题,那么我们可以使用synchronized关键字,synchronized可以在任意对象及方法上加锁,而加锁的这段代码成为“互斥区”或“临界区”。

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

public static native Thread currentThread();

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

public final native boolean isAlive();

sleep()的作用是让正在执行的线程(Thread.currentThread())休眠(暂停执行)。

getId()方法的作用是取得线程的唯一标识。
计算线程id的方法是同步的

/* For generating thread ID */private static long threadSeqNumber;private static synchronized long nextThreadID() {        return ++threadSeqNumber;}

在java多线程中,停止一个线程有3种方法:

1、让run方法正常执行完毕后线程终止

2、使用stop()方法强行停止,但是该方法已经作废过期,不推荐使用。

因为如果强制让线程停止可能使一些清理性的工作得不到完成。还可能让锁定的对象得不到解锁,导致数据得不到同步的处理,出现数据不一致的问题。

3、使用interrupt方法中断线程

interrupt方法

interrupt方法并不会让线程停止,它只是一个状态标志,需要在run方法内部加上判断才可以。

interrupt方法有两个方法

private native boolean isInterrupted(boolean ClearInterrupted);public boolean isInterrupted() {        return isInterrupted(false);}public static boolean interrupted() {        return currentThread().isInterrupted(true);}

细心的看一下就会发现这是两个不同的方法。

isInterrupted()是测试线程是否已经是中断状态,不会清除中断状态。

interrupted()是测试当前线程是否已经是中断状态,而且会清除中断状态。

线程睡觉的时候调用interrupt会出现异常,并且清除中断状态

java.lang.InterruptedException: sleep interrupted

可以使用异常法或者return方法终止线程,抛出异常,则run方法的代码不再执行。

//return法class MyThread extends Thread {    private int count = 0;    @Override    public void run() {        while (true) {            System.out.println(++count);            System.out.println(this.getName());           if (this.isInterrupted()) {               return;           }        }    }}

异常法是一种不错的方法,因为catch块中可以将异常向上抛,使得线程停止的事件得以传播。


在java多线程中,可以使用suspend方法暂停线程,使用resume方法恢复线程的执行。
但是这两个方法都已经被弃用了,不推荐使用,因为这两个方法容易造成公共的同步对象独占,使得其它线程无法方访问公共同步对象。

package me.mymilkbottles.Study01;/** * Created by Administrator on 2017/07/14 18:54. */public class SuspendResume {    public static void main(String[] args) {        MyThread1 thread = new MyThread1();        thread.start();        try {            Thread.sleep(2000);        } catch (InterruptedException e) {            e.printStackTrace();        }        thread.doSome2();    }}class MyThread1 extends Thread {    @Override    public void run() {        doSome1();    }    synchronized private void doSome1() {        System.out.println("do some 1");        this.suspend();        System.out.println("do some 1 end");    }    synchronized public void doSome2() {        System.out.println("do some 2");    }}

上面这段代码,永远也无法进入doSome2方法了。

还有一种独占锁的情况:

package me.mymilkbottles.Study01;/** * Created by Administrator on 2017/07/14 18:54. */public class SuspendResumeLock2 {    public static void main(String[] args) throws Exception {        MyThread2 thread = new MyThread2();        thread.start();        Thread.sleep(200);        thread.suspend();        System.out.println("ok");    }}class MyThread2 extends Thread {    private int count = 0;    @Override    public void run() {        while (true) {            System.out.println(++count);        }    }}

这段代码永远也无法打印ok,因为println的内部是同步的

public void println(String x) {    synchronized (this) {        print(x);        newLine();    }}

但是因为suspend导致无法释放锁,导致ok无法打印。

suspend和resume方法还容易因为线程的暂停导致数据不同步。

yield()方法的作用是放弃当前cpu的资源,将它让给其它的任务去占用cpu,但是放弃的时间不确定,可能刚放弃又重新获得了cpu时间片。

package me.mymilkbottles.Study01;import java.sql.Time;/** * Created by Administrator on 2017/07/14 19:26. */public class Yield {    public static void main(String[] args) {        outputTime(new MyThread3());        outputTime(new MyThread3Yield());    }    private static void outputTime(Thread thread) {        long time1 = System.currentTimeMillis();        thread.start();        try {            thread.join();        } catch (InterruptedException e) {            e.printStackTrace();        }        long time2 = System.currentTimeMillis();        System.out.println((time2 - time1) + "ms");    }}class MyThread3 extends Thread {    private int count = 0;    private static final int TIMES = 1000000;    @Override    public void run() {       for (int i = 0; i <= TIMES; ++i) {           count += i;       }        System.out.println(count);    }}class MyThread3Yield extends Thread {    private int count = 0;    private static final int TIMES = 1000000;    @Override    public void run() {        for (int i = 0; i <= TIMES; ++i) {            this.yield();            count += i;        }        System.out.println(count);    }}/*执行结果:178429366423ms1784293664219ms */

因为使用了yield,所以MyThread3Yield类的run方法执行时间慢于MyThread3类的run方法。

线程的优先级
线程可以划分优先级,优先级高的线程得到的cpu资源较多,也就是cpu优先执行优先级高的线程对象中的任务。
(PS:但是这种东西最后还是要看操作系统的意思,只是说优先级高的可以优先执行,没说一定先执行)

package me.mymilkbottles.Study01;/** * Created by Administrator on 2017/07/14 19:42. */public class ThreadPriority {    public static void main(String[] args) {        Thread[] threads = new Thread[10];        for (int i = 0; i < 10; ++i) {            threads[i] = new Thread(new Runnable() {                @Override                public void run() {                    for (int j = 1; j < 5; ++j) {                        System.out.println(Thread.currentThread().getName() + " : " + j);                    }                }            });            threads[i].setPriority(i + 1);            threads[i].setName(String.valueOf(i));        }        for (Thread thread : threads) {            thread.start();        }    }}/*执行结果:0 : 15 : 12 : 11 : 16 : 11 : 22 : 22 : 32 : 44 : 14 : 24 : 34 : 40 : 25 : 25 : 35 : 49 : 10 : 38 : 11 : 36 : 21 : 43 : 13 : 23 : 33 : 40 : 48 : 27 : 19 : 27 : 28 : 36 : 38 : 47 : 37 : 49 : 36 : 49 : 4*/

线程的优先级具有继承性,如果A线程启动B线程,则B线程的优先级和A一样。

守护线程
java线程中有两种线程,一种是用户线程,另外一种是守护线程。

当线程中不存在非守护线程的时候,守护线程自动销毁,典型的守护线程就是垃圾回收线程。

Daemonde的作用就是为其它线程的运行提供便利服务。


线程的几种状态:

Thread.State.TERMINATED;Thread.State.BLOCKED;Thread.State.NEW;Thread.State.RUNNABLE;Thread.State.TIMED_WAITING;

线程组:可以批量的管理线程或线程组对象,有效地对线程或线程组对象进行组织。

线程自动归属特性:自动归属就是自动归到当前线程组中。

使用ThreadGroup的interrupt方法时,可以将组中的所有正在运行的线程批量停止。