Java Thread类 线程

线程

Thread类在 API文档中的描述

线程是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。

每个线程都有一个优先级,高优先级线程的执行优先于低优先级线程。
每个线程都可以或不可以标记为一个守护程序。

当某个线程中运行的代码创建一个新 Thread 对象时,该新线程的初始优先级被设定为创建线程的优先级,
并且当且仅当创建线程是守护线程时,新线程才是守护程序。
当 Java 虚拟机启动时,通常都会有单个非守护线程(它通常会调用某个指定类的 main 方法)。

Java 虚拟机会继续执行线程,直到下列任一情况出现时为止:
调用了 Runtime 类的 exit 方法,并且安全管理器允许退出操作发生。
非守护线程的所有线程都已停止运行,无论是通过从对 run 方法的调用中返回,
还是通过抛出一个传播到 run 方法之外的异常。

线程调度模型

分时调度模型

所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片

抢占式调用模型

优先让优先级高的线程使用CPU,如果线程的优先级相同,会随机选择一个
优先级高的线程获取的CPU时间片相对多一些

java使用的是抢占式调用模型

创建线程的方法

方法一 继承 Thread

继承 Thread 重写run()方法

public class ThreadDemo {
    public static void main(String args[]) {
        //方便演示直接new ,可以创建类 继承自Thread
        Thread thread = new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(getName()+":"+i);
                }
            }
        };
        thread.setName("线程1");
        thread.start();
        //Thread.currentThread() 可以拿到当前所在线程
        System.out.println("main方法所在线程名字是 :"+Thread.currentThread().getName());
    }
}
运行结果

方法二,构造传入Runnable

可以避免单继承带来的局限性
适合多个相同程序的代码去处理同一个资源

       //Thread 是实现了 Runnable接口
       //Thread 带参构造  Runnable是接口,且只有一个run()方法
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {

            }
        });
        thread.start();

这样看,可能看不出区别,继续看

Thread thread1 = new Thread(new MyRunnable());
Thread thread2 = new Thread(new MyRunnable());
thread1.setName("线程1");
thread2.setName("线程2");
thread1.start();
thread2.start();
        
class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}
运行结果,可能需要多次运行才能达到这种交替的效果

线程的优先级

线程优先级高仅仅表示线程获取的CPU时间片的几率高
因为存在随机性,所以并不是优先级高,就一定先执行

默认优先级是5,可以更改优先级,超过范围(1-10)会抛出异常

/**
     * The minimum priority that a thread can have.
     */
    public final static int MIN_PRIORITY = 1;

   /**
     * The default priority that is assigned to a thread.
     */
    public final static int NORM_PRIORITY = 5;

    /**
     * The maximum priority that a thread can have.
     */
    public final static int MAX_PRIORITY = 10;

  //获取线程优先级
  thread1.getPriority();
  //设置线程优先级
  thread2.setPriority(8);

线程休眠,线程加入,线程礼让

有部分方法是静态方法,实际使用可能与下面示例不一致

        try {
            //线程休眠1秒
            thread1.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        try {
            //线程加入 等待该线程终止,才执行其他线程
            thread1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //线程礼让 暂停当前正在执行的线程对象,并执行其他线程
        thread1.yield();

守护线程

将该线程标记为守护线程或用户线程
当正在运行的线程都是守护线程时,Java 虚拟机退出。
该方法必须在启动线程前调用

有个例子,坦克大战,如果老窝被攻击,那就都死了
如果坦克都死了,老窝可以还活着

        //设置线程为守护线程
        thread1.setDaemon(true);
        //设置线程为守护线程
        thread2.setDaemon(true);
        thread1.start();
        thread2.start();
        //判断线程是否为守护线程
        thread1.isDaemon();
        thread2.isDaemon();

线程中断

stop()方法已经过时,因为具有不安全性
interrupt 中断线程,把线程设置为终止状态,线程在调用其他方法时抛出异常 InterruptedException
通过抛出异常来中断线程

        //中断线程
        thread1.interrupt();
        thread2.interrupt();

线程的生命周期

新建 创建线程对象
就绪 有执行资格,资源准备好,但没有执行权
运行 拿到执行权
阻塞:由于一些操作,让线程处于该状态,没有执行资格,没有执行权
而由于另一些操作却可以把它激活,激活后处于就绪状态
死亡 线程对象变成垃圾,等待被回收

线程的生命周期

线程安全问题

哪些原因会导致线程安全问题

  1. 是否多线程环境
  2. 是否有共享资源
  3. 是否有多条语句操作共享数据

可以通过同步代码块,同步方法解决,但会降低运行效率

同步代码块的锁可以是--任意对象

同步方法的锁是--this,静态同步方法的锁是--class

 //同步代码块
 synchronized (new Object()){
            ...
        }

集合想要线程安全,可以直接通过集合工具类的Collections的方法


获取线程安全的集合

JDK5之后可以用Lock锁

Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作,此实现允许更灵活的结构

java.util.concurrent.locks  接口Lock

所有已知实现类: 
ReentrantLock, ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock 
//锁定和取消锁定出现在不同作用范围中时,
//必须谨慎地确保保持锁定时所执行的所有代码用 try-finally 或 try-catch 加以保护,以确保在必要时释放锁。 
 Lock l = ...; 
     l.lock();
     try {
         // access the resource protected by this lock
     } finally {
         l.unlock();
     }

等待唤醒机制

Object类中的方法:
wait(): 唤醒在此对象监视器上等待的单个线程
notify(): |在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。 |

为啥是在object类中,因为锁可以是任意对象

线程状态转换图

线程状态转换图

线程组 ThreadGroup

线程组表示一个线程的集合
线程默认情况下属于main线程组,和main线程都属于同一个组

public ThreadGroup(String name)
 //获取线程所在的线程组
 thread1.getThreadGroup();

第三种方式实现线程 Callable

public interface Callable<V>返回结果并且可能抛出异常的任务。
实现者定义了一个不带任何参数的叫做 call 的方法。

Callable 接口类似于 Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。
但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。

Executors 类包含一些从其他普通形式转换成 Callable 类的实用方法。

配合线程池,Callable接口,创建带返回值的线程

线程常见的面试题

1:多线程有几种实现方案,分别是哪几种?
继承Thread类
实现Runnable接口
扩展一种:实现Callable接口。这个得和线程池结合。
2:同步有几种方式,分别是什么?
同步代码块
同步方法

3:启动一个线程是run()还是start()?它们的区别?
start();
run():封装了被线程执行的代码,直接调用仅仅是普通方法的调用
start():启动线程,并由JVM自动调用run()方法

4:sleep()和wait()方法的区别
sleep():必须指时间;不释放锁。
wait():可以不指定时间,也可以指定时间;释放锁。

5:为什么wait(),notify(),notifyAll()等方法都定义在Object类中
因为这些方法的调用是依赖于锁对象的,而同步代码块的锁对象是任意锁。
而Object代码任意的对象,所以,定义在这里面。

同步异步,并行串行

  • 同步:同步很好理解,就是一段代码执行是按照顺序执行的
    上一行代码有返回结果才会执行下一行
int i = 0;
int k = functionA(i); // 同步执行 是在functionA返回结果后才执行下面的functionB方法
functionB();
  • 异步:在发起异步调用后不会马上得到结果,就执行下面一行代码

  • 串行:简单理解为单个线程执行同一时间执行单个任务,好比单行道

  • 并行:多CPU,同一时间可以执行多个任务,多行道

举例:下载一张图片耗时2秒, 如果要下载十张图片,串行的话就是一张图片一张图片的下载,
总共耗时20秒,而并行的话,如果2个线程并行,那么相当于一次同时下载两张图片,时间减半只需要10秒

并发并行

举例:
你吃饭吃到一半,电话来了,你一直到吃完了以后才去接,这就说明你不支持并发也不支持并行。你吃饭吃到一半,电话来了,你停了下来接了电话,接完后继续吃饭,这说明你支持并发。你吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并行。并发的关键是你有处理多个任务的能力,不一定要同时。并行的关键是你有同时处理多个任务的能力。所以我认为它们最关键的点就是:是否是『同时』

链接:https://www.zhihu.com/question/33515481/answer/58849148
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

在现实机器中:
单核的机器,都是并发 concurrence 执行的。
多核的机器,都是并行 parallel 中嵌套着并发 concurrence 运行的。
如果存在无限核心的机器,则所有任务都可以是并行运行的。

一个4核心的机器,同时运行4个长时间Thread,每个核心跑一个,【对于进程来说】,这就是并行运行。

一个4核心的机器,运行5个Thread,有一个核心跑了2个线程,他们在轮替要CPU执行上下文切换。 其余核心都是跑一个线程,【对于这个进程来说】,就是并行 parallel 中夹杂并发concurrence。

一个单核心的机器,无论运行多少个线程,都是concurrence,即使用了.NET中的TPL,也是concurrence。


image.png

并发:两个队列交替使用一台咖啡机
并行:两个队列同时使用两台咖啡机互不干扰
串行:一个队列使用一台咖啡机

并行和并发都可以是多个线程,就看这些线程能不能同时被多个CPU执行,
如果能,就是并行,如果不能就是并发,多个线程被一个CPU轮流执行

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

推荐阅读更多精彩内容

  • 为什么使用线程池 当我们在使用线程时,如果每次需要一个线程时都去创建一个线程,这样实现起来很简单,但是会有一个问题...
    闽越布衣阅读 4,302评论 10 45
  • 单任务 单任务的特点是排队执行,也就是同步,就像再cmd输入一条命令后,必须等待这条命令执行完才可以执行下一条命令...
    Steven1997阅读 1,194评论 0 6
  • 【JAVA 线程】 线程 进程:是一个正在执行中的程序。每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者...
    Rtia阅读 2,773评论 2 20
  • 1 在《三生三世十里桃花》原著中,少辛出场并不多,可以说作者留给她的笔墨非常少。作为一个配角,少辛最大的作用就是推...
    恍若漂凌阅读 3,120评论 4 3
  • 2018年3月20日 星期二 晴 203篇 早起,今天有点冷。幸亏昨天就让坤坤多穿了点衣服。“春捂”,就是...
    我是快乐的老爸阅读 80评论 0 2