java多线程系列(一)---多线程技能

java多线程技能

前言:本系列将从零开始讲解java多线程相关的技术,内容参考于《java多线程核心技术》与《java并发编程实战》等相关资料,但愿站在巨人的肩膀上,再经过个人理解能让知识更加简单易懂。html

目录

并发历史

  • 在没有操做系统的时候,一台计算机只执行一个程序,在那个时候,对珍贵的计算机资源来讲是一种浪费
  • 为了提升资源利用率(好比在等待输入的时候,能够执行其余程序),为了提升公平性(不一样用户和程序对计算机上的资源有平等的使用权),为了提升便利性(实现多个任务的时候,能够经过多个程序,而不用一个程序实现多个任务)计算机加入了操做系统
  • 一样,相同的缘由,线程诞生了。线程能够共享进程的资源。

线程优点

发挥多处理器的强大功能

  • 随着技术的发展,多处理器系统愈来愈普及。在一个双处理器系统上,若是只用一个线程,那么无疑浪费了资源。

线程状态

  • 新建(New):建立后还没有启动的线程
  • 运行(Runanle):包括了操做系统线程中的Running和Ready,处于此状态的线程可能正在执行或者等待cpu分配时间片
  • 无限期等待(Waiting):等待被其余线程显式唤醒,执行wait或者join方法或者LockSupport的park方法
  • 限期等待(Timed Waiting):必定时间后会由系统自动唤醒
  • 阻塞(Blocked):等待获取到一个排它锁
  • 结束(Terminated):线程执行结束

多线程编程的两种方式

  • 继承Thread
  • 实现Runnable接口

经过继承Thread

代码的执行顺序与代码的顺序无关

public class T1 {
    public static void main(String[] args) {
        MyThread myThread=new MyThread();
        myThread.start();
        System.out.println("代码的执行结果与代码的顺序无关");
    }
}
class MyThread extends Thread
{
    public void run()
    {
        System.out.println("建立的线程");
    }
}

若是直接执行run方法是同步(主线程调用),start方法是让系统来找一个时间来调用run方法(子线程调用),

public class T1 {
    public static void main(String[] args) {
        MyThread myThread=new MyThread();
        myThread.run();
        System.out.println("若是是直接执行run方法,确定是按代码顺序执行的,由于是经过主线程调用的");
    }
}
class MyThread extends Thread
{
    public void run()
    {
        System.out.println("建立的线程");
    }
}

经过实现Runnable接口

比继承Thread的方式更有优点

  • java不能多继承,因此若是线程类已经有一个父类,那么没法再继承Thread类
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("运行中!");
    }
}
public class Run {

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

}

线程数据非共享

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();
        }
        
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);
        }
    }
}
  • 这里的i并不共享,每个线程维护本身的i变量

线程数据共享

public static void main(String[] args) {
    MyThread mythread=new MyThread();
    //线程a b c启动的时候,执行的是myThread的方法,此时数据共享
    Thread a=new Thread(mythread,"A");
    Thread b=new Thread(mythread,"B");
    Thread c=new Thread(mythread,"C");

    a.start();
    b.start();
    c.start();

}
  • 因为i++不是原子操做(先获取i的值,让后再加一,再把结果赋给i),因此输出的值会有重复的状况,好比4 4 2

synchronized关键字让i++同步执行

public synchronized void run() {
        super.run();
            count--;
            System.out.println("由 "+this.currentThread().getName()+" 计算,count="+count);//输出的必定是4 3 2
    }
  • synchronized 关键字,给方法加上锁,多个线程尝试拿到锁,拿到锁的线程执行方法,拿不到的不断的尝试拿到锁

Thread方法

  • currentThread(),得到当前线程
  • isLive() 线程是否活跃状态(启动还未运行或者启动了正在运行即为活跃状态)
  • sleep()方法,让线程休眠
  • getId()方法 得到该线程的惟一标识
  • suspeend()方法,让线程暂停(已报废)
  • ressume()方法,让线程恢复(已报废)
  • stop()方法,让线程终止(已报废)

中止线程的方法

  • 线程本身执行完后自动终止
  • stop强制终止,不安全
  • 使用interrupt方法

interrupt方法

  • 线程对象有一个boolean变量表明是否有中断请求,interrupt方法将线程的中断状态设置会true,可是并无马上终止线程,就像告诉你儿子要好好学习同样,可是你儿子怎么样关键看的是你儿子。
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!");
    }
    class MyThread extends Thread {
        @Override
        public void run() {
            super.run();
            for (int i = 0; i < 500000; i++) {
                System.out.println("i=" + (i + 1));
            }
        }
    }
  • 上面的代码虽然执行了interrupt方法,可是并无中断run方法里面的程序,而且run方法所有执行完,也就是一直执行到500000

判断线程是否中断

  • interrupted方法判断当前线程是否中断,清除中断标志
  • isInterrupt 方法判断线程是否中断,不清除中断标志

interrupted方法

public class MyThread extends Thread {
    @Override
    public void run() {
        super.run();
        for (int i = 0; i < 500000; i++) {
            System.out.println("i=" + (i + 1));
        }
    }
}
public class Run {
    public static void main(String[] args) {
        try {
            MyThread thread = new MyThread();
            thread.start();
            Thread.sleep(1000);
            thread.interrupt();
            //Thread.currentThread().interrupt();
            System.out.println("是否中止1?="+thread.interrupted());//false
            System.out.println("是否中止2?="+thread.interrupted());//false main线程没有被中断!!!
      //......
  • 这里thread线程执行了interrupt方法,按到里thread的中断状态应该为true,可是由于interrupted方法判断的是当前线程的中断状态,也就是main线程(main线程执行thread.interrupted方法),因此他的中断状态是false
public class Run {
    public static void main(String[] args) {
        try {
            Thread.currentThread().interrupt();
            System.out.println("是否中止1?="+Thread.interrupted());//true
            System.out.println("是否中止2?="+Thread.interrupted());//false 
      //......
  • 主线程执行interrupt方法,第一次执行interrupted方法的时候,中断状态为true,可是interrupted方法有清除中断标志的做用,因此再执行的时候输出的是false

isInterrupt方法

public static void main(String[] args) {
        MyThread thread=new MyThread();
        thread.start();
        thread.interrupt();
        System.out.println(thread.isInterrupted());//true
        System.out.println(thread.isInterrupted());//true
    }
  • 这里也有判断线程中断的做用,而判断的是他的调用者的中断状态,并且没有清除中断标志的做用,因此两次都是true

中止线程

  • 在上面的代码中,咱们虽然执行了interrupt方法,可是并无中断进程,那么咱们若是来中断呢?咱们能够在run方法中进行判断,判断中断状态,状态为true,那么就中止run方法。
import exthread.MyThread;

import exthread.MyThread;

public class Run {

    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!");
    }

}

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("666");
    }
}
  • 还有一个问题,咱们要中断进程,经过上面的的操做咱们终止了for循环,可是后面的输出666仍然执行,这并非咱们想要的中断。因而咱们能够顺便抛出异常,而后直接捕获,这样的话后面的代码就不执行了。

异常法中止线程

public class MyThread extends Thread {
    @Override
    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();
        }
    }
}
  • 固然咱们也能够直接return,可是抛出异常比较好,由于后面能够继续将异常抛出,让线程中断事件获得传播

return 中止线程

for (int i = 0; i < 500000; i++) {
                if (this.interrupted()) {
                    System.out.println("已是中止状态了!我要退出了!");
                    return;
                }
                System.out.println("i=" + (i + 1));
            }

sleep与interrupt

  • 中断状态,进入sleep抛出异常
  • 睡眠进入中断状态,抛出异常
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!");
    }
}
    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();
            }
        }
    }*/

暂停线程

  • suspend (做废)会让同步方法直接锁住
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();
        }
    }

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

    }
  • 当thread1执行了suspend方法后,printString方法直接就被锁住了,也就是thread1把这个锁占住了,可是却不工做
public void println(String x) {
        synchronized (this) {
            print(x);
            newLine();
        }
    }
  • 用System.out.println() 方法替换pringString方法一样也会锁住,由于这个方法也加锁了,当thread1暂停以后,一样占住了这个锁

yield方法

  • 放弃当前cpu资源,让其余任务去占用,可是何时放弃不知道,由于放弃后,可能又开始得到时间片

线程优先级

  • cpu先执行优先级高的线程的对象任务, setPriority方法能够设置线程的优先级
  • 线程优先级具备继承性,也就是说A线程启动B线程,两者优先级同样,一样main主线程启动线程A,main和A的优先级也是同样的
public static void main(String[] args) {
        System.out.println("main thread begin priority="
                + Thread.currentThread().getPriority());
        Thread.currentThread().setPriority(6);
        System.out.println("main thread end   priority="
                + Thread.currentThread().getPriority());
        MyThread1 thread1 = new MyThread1();
        thread1.start();
    }


    class MyThread1 extends Thread {
        @Override
        public void run() {
            System.out.println("MyThread1 run priority=" + this.getPriority());
            MyThread2 thread2 = new MyThread2();
            thread2.start();
        }
    }

优先级特性

  • 规则性,cpu尽可能将资源给优先级高的
  • 随机性,优先级较高的不必定先执行完run方法

守护线程

  • 线程有两种一种是用户线程,一种是守护线程
  • 垃圾回收线程是典型的守护线程,当jvm中还有非守护线程,守护线程就一直还在,知道非守护线程不存在了,守护线程才销毁

总结

  • 线程提升了资源利用率
  • 线程的实现能够经过继承Thread类,也能够经过实Runnable接口
  • 线程中断方式有3种,经常使用的是interrupt方法,该方法并无当即中断线程,只是作了一个中断标志
  • interrupted和isInterrupt两种方法均可以查看线程中断状态,第一种查看的是当前线程的中断状态,第二种查看的该方法调用者的中断状态
  • interrupted方法会清除中断状态,isInterrupt不会清除中断状态
  • interrupt方法没有真正中断线程,因此能够在run方法里面判断中断状态,而后经过抛出异常或者return来中断线程
  • 当线中断状态为true,再进入sleep会抛出异常,反之同样,sleep状态执行interrupt方法,一样会抛出异常
  • 线程暂停容易将锁占住
  • 线程具备优先级,能够经过方法设置线程优先级,cpu会将资源尽可能给优先级高的线程,可是当优先级差异不大的时候,优先级高的不必定先执行完run方法
  • 线程有两种,一种用户线程,一种守护线程,直到用户线程都销毁,守护线程才销毁

我以为分享是一种精神,分享是个人乐趣所在,不是说我以为我讲得必定是对的,我讲得可能不少是不对的,可是我但愿我讲的东西是我人生的体验和思考,是给不少人反思,也许给你一秒钟、半秒钟,哪怕说一句话有点道理,引起本身心里的感触,这就是我最大的价值。(这是我喜欢的一句话,也是我写博客的初衷)

做者:jiajun 出处: http://www.cnblogs.com/-new/
本文版权归做者和博客园共有,欢迎转载,但未经做者赞成必须保留此段声明,且在文章页面明显位置给出原文链接,不然保留追究法律责任的权利。若是以为还有帮助的话,能够点一下右下角的【推荐】,但愿可以持续的为你们带来好的技术文章!想跟我一块儿进步么?那就【关注】我吧。java

相关文章
相关标签/搜索