java并发编程1.1线程基础

摘要说明:

本篇文章首先带领大家了解下一些基础概念,接着介绍下java常用的线程启动方式;

步骤:

1.基础概念

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

核心数:线程数=1:1  ;使用了超线程技术后---> 1:2;这里面指的是同一时刻cpu执行线程数

  • CPU时间片轮转机制

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

时间片轮转法(Round-Robin,RR)主要用于分时系统中的进程调度。为了实现轮转调度,系统把所有就绪进程按先入先出的原则排成一个队列。新来的进程加到就绪队列末尾。每当执行进程调度时,进程调度程序总是选出就绪队列的队首进程,让它在CPU上运行一个时间片的时间。时间片是一个小的时间单位,通常为10~100ms数量级。当进程用完分给它的时间片后,系统的计时器发出时钟中断,调度程序便停止该进程的运行,把它放入就绪队列的末尾;然后,把CPU分给就绪队列的队首进程,同样也让它运行一个时间片,如此往复。

上下文切换就是指进程切换时保存数据及引入新进程数据的过程;

  • 什么是进程和线程

进程:程序运行资源分配的最小单位,进程内部有多个线程,会共享这个进程的资源

线程:CPU调度的最小单位,必须依赖进程而存在。

  • 澄清并行和并发

并行:同一时刻,可以同时处理事情的能力

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

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

好处

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

问题

线程共享资源,存在冲突;

容易导致死锁;

启用太多的线程,就有搞垮机器的可能

2.新启线程的三种方式

java新启线程的三种方式分别是:

  • 继承Thread类:无返回值,可直接运行
  • 实现Runnable接口:无返回值,需借助Thread类来运行
  • 实现Callable接口:有返回值,需要用FutureTask来封装,借助Thread类来运行

实践

package pers.cc.curriculum1;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * @模块名:study_1_thread_basic
 * @包名:pers.cc.curriculum1 @类名称: NewThread @类描述:【类描述】 @版本:1.0
 * @创建人:cc
 * @创建时间:2019年1月17日上午10:41:34
 */

public class NewThread {

    /**
     * 
     * @模块名:study_1_thread_basic
     * @包名:pers.cc.curriculum1 @类名称: UseThread
     * @类描述:【类描述】继承Thread类 @版本:1.0
     * @创建人:cc
     * @创建时间:2019年1月17日下午2:01:07
     */
    private static class UseThread extends Thread {
        public void run() {
            super.run();
            System.out.println(Thread.currentThread().getName() + " UseThread is run");
        }
    }

    /**
     * 
     * @模块名:study_1_thread_basic
     * @包名:pers.cc.curriculum1 @类名称: UseRunnable
     * @类描述:【类描述】实现Runnable接口 @版本:1.0
     * @创建人:cc
     * @创建时间:2019年1月17日下午2:01:54
     */
    public static class UseRunnable implements Runnable {
        public void run() {
            System.out.println(Thread.currentThread().getName() + " UseRunnable is run");
        }
    }

    /**
     * 
     * @模块名:study_1_thread_basic
     * @包名:pers.cc.curriculum1 @类名称: UseCallable
     * @类描述:【类描述】实现Callable接口 @版本:1.0
     * @创建人:cc
     * @创建时间:2019年1月17日下午2:02:16
     */
    private static class UseCallable implements Callable < String > {

        @Override
        public String call() throws Exception {
            System.out.println(Thread.currentThread().getName() + " UseCallable is run");
            return "UseCallable";
        }

    }

    public static void main(String[] args) {
        // 继承Thread类可直接运行
        UseThread useThread = new UseThread();
        useThread.start();

        // 实现Runnable接口,需借助Thread运行
        UseRunnable useRunnable = new UseRunnable();
        new Thread(useRunnable).start();

        UseCallable useCallable = new UseCallable();
        // 1.首先运用futureTask包装Callable
        FutureTask < String > futureTask = new FutureTask <>(useCallable);
        // 2.借助Thread运行
        new Thread(futureTask).start();
        try {
            // 3.从FutureTask获取结果
            System.out.println("result:" + futureTask.get());
        }
        catch (InterruptedException | ExecutionException e) {
            System.out.println(e);
        }
    }
}

运行结果如下:

Thread-0 UseThread is run
Thread-1 UseRunnable is run
Thread-2 UseCallable is run
result:UseCallable

3.线程停止

一般线程的run()或call()运行结束或者抛出未处理异常,线程会自然终止;

如何主动终止线程?如何安全的终止线程?

首先我们看下Thread下的几个方法:

  • stop();//不建议使用,强制结束,线程资源可能不会释放
  • suspend();//不建议使用,使线程睡眠,一直占用线程资源,会导致死锁
  • interrupt();//并不是强行关闭这个线程,只是告知线程要关闭,将线程的中断标志位置为true,线程是否中断,由线程本身决定。
  • isInterrupted();//判定线程是否中断,不修改中断标志位
  • interrupted();//判定当前线程是否处于中断状态,同时中断标志位改为false。

上述interrupt()方法很好的提现了java线程的运行方式是协作式的;

实例

package pers.cc.curriculum1;

/**
 * 
 * @模块名:study_1_thread_basic
 * @包名:pers.cc.curriculum1
 * @类名称: EndThread
 * @类描述:【类描述】如何中断线程
 * @版本:1.0
 * @创建人:cc
 * @创建时间:2019年1月17日下午3:08:25
 */
public class EndThread {

    /**
     * 
     * @模块名:study_1_thread_basic
     * @包名:pers.cc.curriculum1
     * @类名称: UseThread
     * @类描述:【类描述】使用interrupt()安全中断线程
     * @版本:1.0
     * @创建人:cc
     * @创建时间:2019年1月17日下午3:55:23
     */
    private static class UseThread extends Thread {
        public UseThread(String name) {
            super(name);
        }

        public void run() {
            // 判定当前线程是否处于中断状态,同时中断标志位改为false。
            while (!isInterrupted()) {
                System.out
                        .println(Thread.currentThread().getName() + " is run");
            }
            System.out.println(Thread.currentThread().getName()
                    + " isInterrupted is " + isInterrupted());
        }
    }

    // stop();//不建议使用,强制结束,线程资源可能不会释放
    // suspend();//不建议使用,使线程睡眠,一直占用线程资源,会导致死锁
    // interrupt();//并不是强行关闭这个线程,只是跟这个线程打个招呼,将线程的中断标志位置为true,线程是否中断,由线程本身决定。
    // isInterrupted();//判定线程是否中断
    // interrupted();//判定当前线程是否处于中断状态,同时中断标志位改为false。
    public static void main(String[] args) {

        // 继承Thread类可直接运行
        UseThread useThread = new UseThread("testThread");
        useThread.start();
        try {
            Thread.sleep(500);
        }
        catch (InterruptedException e) {
            System.out.println(e);
        }
        useThread.interrupt();// 中断线程

    }
}

useThread判断是否中断,进行打印,若不中断该线程会一直打印下去;故main线程休眠一段时间后手动调用interrupt()方法告知useThread线程中断,useThread才停止;

上述运行后我们可以看到如下:

testThread is run
testThread is run
testThread is run
testThread is run
.....
testThread is run
testThread is run
testThread is run
testThread isInterrupted is true

若我们修改主线程代码如下:

        public void run() {
            // 判定线程是否中断,不修改中断标志位
            while (!isInterrupted()) {
                try {
                    //调用sleep线程抛出异常
                    Thread.sleep(100);
                }
                catch (InterruptedException e) {
                    System.out.println(Thread.currentThread().getName()
                            + " isInterrupted is " + isInterrupted());
                    e.printStackTrace();
                    //interrupt();
                }
                System.out
                        .println(Thread.currentThread().getName() + " is run");
            }
            System.out.println(Thread.currentThread().getName()
                    + " isInterrupted is " + isInterrupted());
        }

 我们通过调用sleep()抛出异常,并进行捕捉;

运行结果如下

testThread is run
testThread is run
testThread is run
testThread is run
testThread isInterrupted is false
java.lang.InterruptedException: sleep interrupted
testThread is run
	at java.lang.Thread.sleep(Native Method)
	at pers.cc.curriculum1.EndThread$UseThread.run(EndThread.java:34)
testThread is run
testThread is run
testThread is run
testThread is run
testThread is run
testThread is run

 运行后我们可以看到,异常抛出后中断标志位并没有置成true;所以如果进行了异常捕捉需手动调用interrupt();此时再运行就能很好的终止线程:

testThread is run
testThread is run
testThread is run
testThread is run
testThread is run
testThread isInterrupted is false
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at pers.cc.curriculum1.EndThread$UseThread.run(EndThread.java:34)
testThread is run
testThread isInterrupted is true

 4.线程常用方法及状态

首先我们通过下图可以很直观的说明问题: 

解析: 

线程新建后调用start()进入就绪状态;

进入就绪状态根据时间片的状态在就绪状态和运行状态中切换或变更成为其他状态;

在运行状态下调用sleep()方法可进去阻塞状态,时间到后再次进入就绪状态;

同样的调用wait()方法也可进入阻塞状态,但需要手动进行结束阻塞进入就绪状态;

线程死亡即结束有多种方式,可参考上节;

部分方法解析

yield() :让出cpu的执行权,将线程从运行转到可运行状态,但是下个时间片,该线程依然有可能被再次选中运行。

run()和start() :run方法就是普通对象的普通方法,只有调用了start()后,Java才会将线程对象和操作系统中实际的线程进行映射,再来执行run方法。

.setPriority(newPriority):设置线程优先级;1-10;默认为5,不建议使用;

setDaemon(true):可设置线程为守护线程,守护线程和主线程共死,且线程中的finally不能保证一定执行,如下列:


public class DaemonThread {
    /**
     * 守护线程和主线程共死,finally不能保证一定执行
     */
    private static class UseThread extends Thread {
        public void run() {
            // 判定线程是否中断,不修改中断标志位
            try {

                while (!isInterrupted()) {
                    System.out.println(Thread.currentThread().getName()
                            + " is run");
                }
                // 守护线程
            }
            finally {
                System.out.println("finally is run");
            }
            System.out.println(Thread.currentThread().getName()
                    + " isInterrupted is " + isInterrupted());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        // 继承Thread类可直接运行
        UseThread useThread = new UseThread();
        useThread.setDaemon(true);// 设置为守护线程,一定要在start()之前调用
        useThread.start();
        Thread.sleep(5);
    }
}

5.源码地址

https://github.com/cc6688211/concurrent-study.git

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值