Java多线程知识点总结

1、多线程创建方式

(1)继承Thread类

public class MyThread extends Thread{
    @Override
    public void run() {
        super.run();
        System.out.println("hello world");
    }

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
    }
}

(2)实现Runnable接口

public class MyRunnbale implements Runnable{

    @Override
    public void run() {
        System.out.println("hello world");
    }

    public static void main(String[] args) {
        MyRunnbale myRunnbale = new MyRunnbale();
        Thread thread = new Thread(myRunnbale);
        thread.start();
    }
}

(3)实现Callable接口

public class MyCallable implements Callable {

    @Override
    public String call() throws Exception {
        Thread.sleep(3000);
        return "hello world";
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCallable myCallable = new MyCallable();
        FutureTask futureTask = new FutureTask(myCallable);
        Thread thread = new Thread(futureTask);
        thread.start();
        System.out.println(futureTask.get());
    }
}

Callable的call () 方法是有返回值的,运行Callable任务可以拿到一个Future对象,表示异步计算的结果。
FutureTask类同时实现了两个接口,Future和Runnable接口,所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。

调用start () 还是 run () 方法启动线程?

调用start () 方法才是真正的运行线程,调用run()方法只是当作普通方法执行,程序还是要顺序执行。

Thread和Runnable的区别?

(1)由于Java不允许多继承,因此实现了Runnable接口可以再继承其他类,但是Thread不可以
(2)Runnable比Thread更适合共享同一个资源

2、线程的生命周期

调用start()后,线程会被放到等待队列,等待CPU调度,并不一定要马上开始执行,只是将这个线程置于可动行状态。

阻塞的情况又分为:
(1)等待阻塞 -- 通过调用线程的wait()方法,让线程等待某工作的完成。
(2)同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
(3)其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

3、线程管理

(1)线程睡眠 —— sleep ()

sleep是静态方法,最好不要用Thread的实例对象调用它,因为它睡眠的始终是当前正在运行的线程,而不是调用它的线程对象,它只对正在运行状态的线程对象有效。

public static void main(String[] args) throws InterruptedException {
        System.out.println(Thread.currentThread().getName());
        MyThread myThread = new MyThread();
        myThread.start();
        Thread.sleep(1000); //这里休眠的不一定是myThread线程,而是当前运行的线程
        for (int i=0;i<100;i++){
            System.out.println("main:"+i);
        }
    }
(2)线程让步 —— yield ()

yield()方法和sleep()方法有点相似,它也是Thread类提供的一个静态的方法,它也可以让当前正在执行的线程暂停,让出cpu资源给其他的线程。但是和sleep()方法不同的是,它不会进入到阻塞状态,而是进入到就绪状态。yield()方法只是让当前线程暂停一下,重新进入就绪的线程池中,让系统的线程调度器重新调度器重新调度一次,完全可能出现这样的情况:当某个线程调用yield()方法之后,线程调度器又将其调度出来重新进入到运行状态执行。

public class Test01 {
    public static void main(String[] args) {
        new MyThread("低级", 1).start();
        new MyThread("中级", 5).start();
        new MyThread("高级", 10).start();
    }

    static class MyThread extends Thread {
        public MyThread(String name, int pro) {
            super(name);// 设置线程的名称
            this.setPriority(pro);// 设置优先级
        }

        @Override
        public void run() {
            for (int i = 0; i < 30; i++) {
                System.out.println(this.getName() + "线程第" + i + "次执行!");
                if (i % 5 == 0) {
                    Thread.yield();
                }
            }
        }
    }
}
(3)线程让步 —— join ()

join方法其实就是阻塞当前调用它的线程,等待join执行完毕,当前线程继续执行。

public class Test01 {
    public static void main(String[] args) throws InterruptedException {
        MyThread threadA = new MyThread("A");
        MyThread threadB = new MyThread("B");
        threadA.start();
        threadA.join(); // 会阻塞main线程,使得下面一句必须等到threadA线程结束才能执行,所以A、B线程没有交替执行
        threadB.start();
    }

    static class MyThread extends Thread {
        public MyThread(String name) {
            super(name);
        }

        @Override
        public void run() {
            for (int i = 0; i < 10; ++i) {
                System.out.println(this.getName() + ": " + i);
            }
        }
    }
}

join源码中,只会调用wait方法,并没有在结束时调用notify,这是因为线程在die的时候会自动调用自身的notifyAll方法,来释放所有的资源和锁。

(4)设置线程的优先级
  • 1)优先级高的线程可以获得较多的执行机会,而优先级低的线程则获得较少的执行机会。
  • 2)每个线程默认的优先级都与创建它的父线程具有相同的优先级,在默认情况下,main线程具有普通优先级。
  • 3)Thread类提供了setPriority(int newPriority)和getPriority()方法来设置和返回一个指定线程的优先级,其中setPriority方法的参数是一个整数,范围是1~10之间,也可以使用Thread类提供的三个静态常量:
    MAX_PRIORITY =10
    MIN_PRIORITY =1
    NORM_PRIORITY =5
public class Test01 {
    public static void main(String[] args) {
        MyThread threadA = new MyThread("A", Thread.MAX_PRIORITY);
        MyThread threadB = new MyThread("B", Thread.MIN_PRIORITY);
        threadA.start();
        threadB.start();
    }

    static class MyThread extends Thread {
        public MyThread(String name, int pro) {
            super(name); //设置线程名称
            setPriority(pro); // 设置线程优先级
        }

        @Override
        public void run() {
            for (int i = 0; i < 10; ++i) {
                System.out.println(this.getName() + ": " + i);
            }
        }
    }
}
(5)守护线程

只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。

public class Test01 {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.setDaemon(true); // 守护线程随着所有用户线程结束,跟随JVM一起结束
        myThread.start();
        System.out.println(Thread.currentThread().getName()+"停止运行");
    }

    static class MyThread extends Thread {
        @Override
        public void run() {
            while (true){
                System.out.println("守护线程 正在运行!");
            }
        }
    }
}
(6)停止线程

1)使用标志位终止线程

public class Test01 {

    // volatile修饰符用来保证其它线程读取的总是该变量的最新的值
    public static volatile boolean interrupt = true;

    public static void main(String[] args) throws InterruptedException {

        MyThread myThread = new MyThread();
        myThread.start();
        Thread.sleep(1000);
        interrupt = false; //修改标志位,退出线程
        System.out.println(Thread.currentThread().getName() + "停止运行");
    }

    static class MyThread extends Thread {
        @Override
        public void run() {
            while (interrupt) {
                System.out.println(Thread.currentThread().getName() + ":正在运行");
            }
        }
    }
}

2)使用interrupt()方法来终止线程
nterrupt()方法用于中断线程,调用该方法的线程状态将会被置为“中断状态”。
线程中断仅仅是置线程的中断状态位,不会停止线程。需要用户自己去监视线程的状态为并做处理。
调用这种方式去终止的线程存在两种情况:

  • 第一种情况:该线程循环执行不会存在阻塞,这样的话调用interrupt()会立即终止该线程
  • 第二种情况:该线程循环执行存在阻塞状态,比如,在线程中存在sleep()、await()、wait(long) 这种能够抛出:interruptedException 异常的方法,这种情况调用interrupt()方法后将会触发这些异常,可以选择在触发异常后调用break来终止线程
// 第一种情况
public class Test01 {

    public static void main(String[] args) throws InterruptedException {

        MyThread myThread = new MyThread();
        myThread.start();
        Thread.sleep(1000);
        myThread.interrupt();
        System.out.println(Thread.currentThread().getName() + "停止运行");
    }

    static class MyThread extends Thread {
        @Override
        public void run() {
            while (!isInterrupted()) {
                System.out.println(Thread.currentThread().getName() + ":正在运行");
            }
        }
    }
}

//第二种情况
public class Test01 {

    public static void main(String[] args) throws InterruptedException {

        MyThread myThread = new MyThread();
        myThread.start();
        Thread.sleep(1000);
        myThread.interrupt();
        System.out.println(Thread.currentThread().getName() + "停止运行");
    }

    static class MyThread extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 100; ++i) {
                try {
                    Thread.sleep(100);
                    System.out.println(i);
                } catch (InterruptedException e) {
                    System.out.println(Thread.currentThread().getName()+"停止了");
                    break;
                }
            }
        }
    }
}
sleep()与wait()的区别?

(1)sleep是Thread的静态类方法,wait来自Object类
(2)sleep方法没有释放锁,而wait方法释放了锁
(3)wait,notify和notifyAll只能在同步方法或者同步代码块里面使用,而sleep可以在任何地方使用
(4)sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,588评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,456评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,146评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,387评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,481评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,510评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,522评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,296评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,745评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,039评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,202评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,901评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,538评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,165评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,415评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,081评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,085评论 2 352

推荐阅读更多精彩内容

  • 一、进程和线程的区别:进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包...
    困火阅读 418评论 0 0
  • 1. 多线程编程的好处 程序中启用多个线程并发执行以提高程序的效率,多个线程共享heap memory,创建多个线...
    天渊hyominnLover阅读 215评论 0 0
  • 线程基本知识 什么是线程安全性?当多个线程访问某个类时,这个类始终都能表现出正确的行为,那么可以认为这个类是线程安...
    Cheava阅读 1,022评论 0 1
  • Java多线程并发 一、 java多线程创建方式 继承Tread类将自己的类继承Tread类,并重写run()方法...
    呼语阅读 251评论 0 0
  • Java 多线程 多线程:指的是这个程序(一个进程)运行时产生了不止一个线程 并行与并发:并行:多个cpu实例或者...
    LarryLeo_9605阅读 299评论 0 3