第1章 Java 多线程技能 标签: Java多线程编程

《Java多线程编程核心技术》 个人笔记

第1章 Java 多线程技能 进程和多线程的概念及线程的优点 使用多线程 currentThread方法 isAlive方法 sleep方法 getId方法 停止线程 停止不了的线程 判断线程是否是停止状态 能停止的线程 异常法 在沉睡中停止 能停止的异常暴力停止 方法stop与javalangThreadDeath异常 释放锁的不良后果 使用return停止线程

暂停线程 suspend与resume方法的使用 suspend与resume方法的缺点独占 suspend与resume方法的缺点不同步

yield方法 线程的优先级 优先级具有继承特性 优先级具有规则性 优先级具有随机性

守护线程

本章关键技术点:

线程如何启动 如何使线程暂停 如何使线程停止 线程的优先级 线程安全的相关问题

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

进程(Process):是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。程序是指令、数据及其组织形式的描述,进程是程序的实体 线程:可以理解为是在进程中独立运行的子任务。 使用多线程(异步)的优点:可以在同一时间内运行更多不同种类的任务。 单任务的特点:排队执行,也就是同步。缺点是CPU利用率低。

使用多线程 public static void main(String[] args) { //获取当前线程的名字 System.out.println(Thread.currentThread().getName()); }

实现多线程的方式主要有两种,一种是继承Thread类,另一种是实现Runnable接口 在使用多线程技术时,代码的运行结果与代码执行顺序或调用顺序是无关的。 Thread.java类中的start()方法通知“线程规划器”此线程已经准备就绪,等待调用线程对象的run()方法,随机异步执行 如果调用thread.run()则是同步执行,此线程是交给main()主线程来调用run()方法,所以必须等run()中的代码执行完才能执行后面的代码 执行的start()的顺序并不代表线程的启动顺序 也就是start()方法是异步,run()则只是像普通方法一样,是同步的

根据Thread.java的构造函数,构造函数支持传入一个Runnable接口的对象,也可以传入一个Thread类的对象,所以完全可以将一个Thread对象中的run()方法交给其他线程进行调用

public static void main(String[] args) {
    Runnable runnable=new MyRunnable();
    Thread thread=new Thread(runnable);
    thread.start();
    System.out.println("main!");
}

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

  1. 不共享数据的情况(每个线程有各自的count变量)

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(); } }

  1. 共享数据的情况(多个线程可以访问同一个变量)

public class MyThread extends Thread { private int count=5; @Override synchronized public void run() { super.run(); count--; System.out.println("由 "+this.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();
}

}

如果有多个线程同时访问,那么一定会出现非线程安全。(输出的顺序不是4,3,2,1,0) 通过在run方法前加入synchronized关键字,使多个线程在执行run方法时,以排队的方式进行处理。 synchronized可以在任意对象及方法上加锁,而加锁的这段代码称为“互斥区”或“临界区”

currentThread()方法

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

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 class Run { public static void main(String[] args) { CountOperate c = new CountOperate(); Thread t1 = new Thread(c); t1.setName("A"); t1.start(); } } /*----结果----- CountOperate---begin Thread.currentThread().getName()=main this.getName()=Thread-0 CountOperate---end run---begin Thread.currentThread().getName()=A this.getName()=Thread-0 run---end */ isAlive()方法

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

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

取得线程的唯一标识

停止线程

Thread.stop()方法是不安全的,已经被废弃。 在java中有以下3中方法可以终止正在运行的线程:

  1. 可以使用退出标识,是线程正常退出,也就是当run方法完成后线程终止
  2. 使用stop(),但该方法不安全,已被废弃,同被废弃的还有suspend,resume
  3. 使用interrupt()方法中断线程

停止不了的线程

interrupt()方法仅仅是在当前线程中打了一个停止的标记,并不是真的停止线程

判断线程是否是停止状态

this.interrupted()方法:测试当前线程是否已经中断,当前线程是指运行this.interrupted()方法的线程。执行后具有将状态标志清除为false的功能 this.isInterrupted():测试线程Thread对象是否已经是中断状态,但不清除状态标识。

能停止的线程 ———— 异常法

可在线程中用for语句来判断一下线程是否是停止状态,如果是则中断for循环,继续执行for后面的语句

public class MyThread extends Thread { @Override public void run() { super.run(); for (int i = 0; i < 500000; i++) { if (this.interrupted()) { System.out.println("已经是停止状态了,我要退出了!"); break; } System.out.println("i=" + (i + 1)); } System.out.println("我被输出,如果此代码是for又继续输出,线程并未停止!"); } }

public static void main(String[] args) {
    try {
        MyThread thread = new MyThread();
        thread.start();
        Thread.sleep(2000);
        thread.interrupt();
    } catch (InterruptedException e) {
        System.out.println("main catch");
        e.printStackTrace();
    }
    System.out.println("end!");
}

如果不想让for后面的语句继续运行,则可以 try catch :

public void run() {
    super.run();
    try {
        for (int i = 0; i < 500000; i++) {
            if (this.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();
    }
}

在沉睡中停止

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

public class MyThread extends Thread { @Override public void run() { super.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) { try { MyThread thread = new MyThread(); thread.start(); Thread.sleep(200); thread.interrupt(); } catch (InterruptedException e) { System.out.println("main catch"); e.printStackTrace(); } System.out.println("end!"); }

如果先interrupte在sleep会报异常

能停止的异常————暴力停止

使用stop()停止线程则是非常暴力的

方法stop()与java.lang.ThreadDeath异常

方法stop()已经作废,因为如果强制停止则有可能使一些清理性的工作得不到完成。另外一个情况就是对锁定的对象进行了“解锁”,导致数据得不到同步的处理

释放锁的不良后果

使用stop()方法释放锁将会给数据造成不一致的结果。

使用return停止线程

将方法interrupt()与return结合也能实现停止线程的效果。 不过还是建议使用“抛异常”的方法来实现线程停止,因为在catch块中还可以将异常向上抛,使线程停止的事件得以传播

public class MyThread 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 {
    MyThread t=new MyThread();
    t.start();
    Thread.sleep(2000);
    t.interrupt();
}

暂停线程 suspend与resume方法的使用

suspend()方法暂停线程,resume()方法回复线程使用

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

使用suspend与resume方法时,如果使用不当,极易造成公共的同步对象的独占,使得其他线程无法访问公共同步对象 还有一种独占锁的情况也要格外注意:当程序运行到println()方法内部时,同步锁未被释放,这导致当前PrintStream对象的println()方法一直呈现“暂停”状态,并且“锁未释放”,而main()方法中的代码System.out.println()迟迟不能打印

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

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

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; 
}

yield方法

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

public void run() {
    long beginTime = System.currentTimeMillis();
    int count = 0;
    for (int i = 0; i < 50000000; i++) {
        //Thread.yield();
        count = count + (i + 1);
    }
    long endTime = System.currentTimeMillis();
    System.out.println("用时" + (endTime - beginTime) + "毫秒");
}

线程的优先级

在操作系统中,线程可以划分优先级,优先级较高的线程得到的CPU资源较多,也就是CPU优先执行优先级较高的线程对象中的任务。 设置线程优先级有助于帮助“线程规划期”确定下一次选择哪一个线程来优先执行。 设置线程的优先级使用setPriority()方法 在java中,线程的优先级分为1~10这十个等级

优先级具有继承特性

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

优先级具有规则性

高优先级的线程总是大部分先执行完

优先级具有随机性

也就是说,优先级较高的线程不一定每一次都先执行完

守护线程

java有两种线程,用户线程和守护线程 守护线程是一种特殊的线程,它的特性有“陪伴”的含义,当进程中不存在非守护线程了,则守护线程自动销毁。(如垃圾回收线程) Daemon的作用是为其线程的运行提供遍历服务,守护线程最典型的应用是GC(垃圾回收器),它就是一个很称职的守护者。

public class MyThread extends Thread { private int i = 0;

@Override
public void run() {
    try {
        while (true) {
            i++;
            System.out.println("i=" + (i));
            Thread.sleep(1000);
        }
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

}

public static void main(String[] args) {
    try {
        MyThread thread = new MyThread();
        thread.setDaemon(true);         //设置守护线程
        thread.start();
        Thread.sleep(5000);
        System.out.println("我离开thread对象也不再打印了");
        //此时main函数运行结束,守护线程也就自动销毁了
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}