线程基础,线程之间的共享和协作

1.基础概念

1.1 CPU核心数和线程数的关系

核心数:线程数=1:1 ;使用了超线程技术后---> 1:2

1.2 CPU时间片轮转机制

又称RR调度,会导致上下文切换

1.3 什么是进程和线程

进程:程序运行资源分配的最小单位,进程内部有多个线程,会共享这个进程的资源
线程:CPU调度的最小单位,必须依赖进程而存在。

1.4 澄清并行和并发

并行:同一时刻,可以同时处理事情的能力
并发:与单位时间相关,在单位时间内可以处理事情的能力

1.5 高并发编程的意义、好处和注意事项

好处:充分利用cpu的资源、加快用户响应的时间,程序模块化,异步化
问题:

  1. 线程共享资源,存在冲突;
    解决方法:利用解决线程共享问题
  2. 容易导致死锁;
    解决方法:
  • 在我们写代码的时候要注意,不要写相互竞争资源的代码;
  • 可以将需要获取资源的方式放在一个地方;比方说,A线程需要A锁,然后需要B锁,B线程需要B锁,A锁;这个时候,可以让第三方作为获取这两把锁的地方;
  • 加上过期时间。只有相对时间内可以持有锁、
  1. 启用太多的线程,就有搞垮机器的可能(CPU的时间片轮转机制,频繁地切换上下文,资源浪费)
    解决方法:
    利用线程池的方式,去把控资源的分配;

2.认识Java里的线程

2.1 线程启动的方式

一般来说我们常用的就三种,Runnable,Thread,Callable。

2.2 优雅的让线程停止工作

  1. 当然是自己走完整个生命周期;
  2. stop(),resume(), suspend()。不过这些都已经不使用。因此不展开了。stop()会导致线程不会正确释放资源(线程不安全),suspend()容易导致死锁。
  3. 线程的interrupt() 方法static方法interrupted(),isInterrupted
  • isInterrupted() :判定当前线程是否处于中断状态。
  • static方法interrupted(): 判定当前线程是否处于中断状态,同时中断标志位改为false。【底层都是调用native的isInterrupted】。两者的区别是传参


    image.png
  • interrupt():跟这个线程打个招呼,将线程的中断标志位置为true,线程是否中断,由线程本身决定。
    打断线程运行的DEMO
public class EndThread {
    private static class UserThread extends Thread {

        public UserThread(String name) {
            super(name);
        }
        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            // 判断是否中断
            while (true) {
                if (currentThread().isInterrupted()) {
                    System.out.println("state is " + isInterrupted());
                    return;
                }
                System.out.println(threadName + " is run!");
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        UserThread userThread = new UserThread("thread name");
        userThread.start();
        Thread.sleep(500);
        // 发出中断
        userThread.interrupt();
    }
}
  • 注意:方法里如果抛出InterruptedException,线程的中断标志位会被复位成false,如果确实是需要中断线程,要求我们自己在catch语句块里再次调用interrupt()。

2.3 线程常用方法和线程的状态

2.3.1 线程状态

Java线程中State中有五种状态

// 新建了线程,但是还没有运行
NEW
// 运行中的线程
RUNNABLE
// 阻塞的。一般来说,是锁的等待
BLOCKED
// 调用了wait方法,使当前线程进入等待状态
WAITING
// 等待某段时间
TIMED_WAITING
// 已经执行完该线程
TERMINATED

2.4 run,start区别

run就是普通的方法的调用,start()才是告诉JVM,我这个是多Java才会将线程对象和操作系统中实际的线程进行映射,再来执行run方法。

2.5 线程的优先级

取值为1~10,缺省为5,但线程的优先级不可靠,不建议作为线程开发时候的手段

2.6 守护线程

和主线程共死,finally不能保证一定执行。可以用setDaemon(true);设置为守护线程

3. 线程的共享

我们知道多线程编码下会带来很多问题,比如对同个资源的修改,会导致数据的不一致性;Java,为我们带来很多处理方案;

3.1 synchronized内置锁

synchronized

  • 可以用在代码块,用在方法上;
  • 可以用对象锁,类锁(静态代码)。两者互不干扰。【因为静态方法锁定在Class对象上,非静态方法锁定在该类的对象上。】

3.2 volatile关键字

适合于只有一个线程写,多个线程读的场景,因为它只能确保可见性。【注意,volatile并不具有原子性】

3.3 ThreadLocal

线程变量。可以理解为是个map,类型 Map<Thread,Integer>。每个线程都有自己的备份,因此不存在变量线程安全问题。但是不适合用比较大的对象。

4.线程间协作

线程间的协作(交互),主要三个方法

  • void notify():唤醒在此对象监视器上等待的单个线程。
  • void notifyAll():唤醒在此对象监视器上等待的所有线程。
  • void wait():导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。这里推荐使用notifyAll()

4.1 等待,通知标准范式(公式)

等待方
1、获取对象的锁;
2、循环里判断条件是否满足,不满足调用wait方法,
3、条件满足执行业务逻辑
通知方
1、获取对象的锁;
2、改变条件
3、通知所有等待在对象的线程
DEMO

**
 *
 *类说明:快递实体类
 */
public class Express {
    public final static String CITY = "ShangHai";
    private int km;/*快递运输里程数*/
    private String site;/*快递到达地点*/

    public Express() {
    }

    public Express(int km, String site) {
        this.km = km;
        this.site = site;
    }

    /* (通知方)变化公里数,然后通知处于wait状态并需要处理公里数的线程进行业务处理*/
    public synchronized void changeKm(){
        this.km = 101;
        notifyAll();
        //其他的业务代码

    }

    /* 变化地点,然后通知处于wait状态并需要处理地点的线程进行业务处理*/
    public synchronized void changeSite(){
        this.site = "BeiJing";
        notify();
    }

    /*等待方*/
    public synchronized void waitKm(){
        while(this.km<=100) {
            try {
                wait();
                System.out.println("check km thread["+Thread.currentThread().getId()
                        +"] is be notifed.");
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        System.out.println("the km is"+this.km+",I will change db.");

    }

    public synchronized void waitSite(){
        while(CITY.equals(this.site)) {
            try {
                wait();
                System.out.println("check site thread["+Thread.currentThread().getId()
                        +"] is be notifed.");
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        System.out.println("the site is"+this.site+",I will call user.");
    }
}

TestMain

public class TestWN {
    private static Express express = new Express(0,Express.CITY);

    /*检查里程数变化的线程,不满足条件,线程一直等待*/
    private static class CheckKm extends Thread{
        @Override
        public void run() {
            express.waitKm();
        }
    }

    /*检查地点变化的线程,不满足条件,线程一直等待*/
    private static class CheckSite extends Thread{
        @Override
        public void run() {
            express.waitSite();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        for(int i=0;i<3;i++){//三个线程
            new CheckSite().start();
        }
        for(int i=0;i<3;i++){//里程数的变化
            new CheckKm().start();
        }

        Thread.sleep(1000);
        express.changeKm();//快递地点变化
    }
}

4.2 等待超时模式范式

假设 等待时间时长为T,当前时间now+T以后超时

long overtime = now+T;
long remain = T;//等待的持续时间
while(result不满足条件&& remain>0){
    wait(remain);
    remain = overtime – now;//等待剩下的持续时间
}
return result;

4.3 其他

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