达内广州C++学院|c++培训|广州达内科技C++/C#培训|.NET培训|IT培训|达内广州岗顶中心 达内广州C++学院|c++培训|广州达内科技C++/C#培训|.NET培训|IT培训|达内广州岗顶中心
java程序员
 当前位置:主页 > 高端课程 > java程序员 >

Java中的多线程基础

时间:2019-10-07  来源:未知  作者:广州达内培训

1、线程与进程

进程:

  • 进程是程序运行以及资源分配的基本单位,一个程序至少有一个进程。
  • 如下图所示:

 

线程:

  • 线程是CPU调度和分配的基本单位,一个进程至少有一个线程。
  • 同一个进程中的线程共享进程资源(减少切换,可提高效率),且可以并发执行。

2、并发和并行

并发:

  • 指的是同一时间间隔执行多个事件,强调的是一段时间内可以做多个不同的事。
  • 比如在一分钟内,先吃一口饭,再喝一口水,接着说一句话等等。

并行:

  • 指的是同一时刻执行多个事件,强调的是同一时刻可以做多个不同的事。
  • 比如在吃饭的同时,一边听歌,看电视。

3、线程的上下文切换

基本原理:

  • 一个CPU在任意时刻只能执行一个线程,如果有多个线程(超过CPU个数)存在,CPU将会采用时间片轮转的方式进行线程切换,即给每一个线程分配一个时间片,当一个线程的时间片用完的时候便会处于就绪状态,并让出CPU给其他线程使用,这就是一次上下文切换。
  • 上下文切换时是通过运行时数据区中的程序计数器来存储各个线程的运行状态的,以便于下次运行线程时可以接着上次指令继续运行(比如线程A做了一次计算准备返回数据时,切换到了线程B,然后又切回线程A,是直接返回数据,而不是再去计算一遍)。
  • 程序计数器指的是JVM中的一块内存区域,它可以看作是当前线程所执行字节码的行号指示器,通过它,Java可以知道每个线程执行到了哪一步指令(由此可以看出程序计数器是线程私有的)。
  • 一般而言,上下文切换是比较耗费CPU时间的一种操作。

4、线程的几种状态

基本原理:

  • 创建:指的是生成线程对象,此时并没有调用start方法。
  • 就绪:调用线程的start方法后,线程便进入了就绪状态,此时等待系统调度。
  • 运行:通过系统调度,开始运行线程中的run函数。
  • 等待:调用了Object.wait()会进入等待队列。一般需要等待其他线程做出一些特定的通知或者中断。
  • 超时等待:该状态与等待状态有一点区别就是它会自动返回,比如调用Threa.sleep(long),在超时之后,会自动返回进入就绪状态。
  • 阻塞:指的是去获取锁时(等待进入synchronized方法或块),发现同步锁被占用了,这时线程会被放入锁池,等待获取锁。
  • 死亡:运行完run方法,main方法(main方法指的是主线程)之后正常退出,也有可能出现异常导致死亡;死亡的线程不能重新启用,否则报错。

5、创建线程的几种方式

继承Thread类:

  • 创建一个类继承Thread,重写其中的run方法,即线程执行体。
  • 创建继承了Thread类的子类实例,即线程对象
  • 调用线程对象的start()方法启动线程。
  • 示例代码如下;
public class ThreadTest {
    public static void main(String[] args){
        new TestThread().start();
    }
}

class TestThread extends Thread{
    @Override
    public void run(){
        System.out.println("Test Thread");
    }
}

实现Runnable接口:

  • 创建一个类实现Runnable接口,重写其中的run方法,即线程执行体。
  • 创建实现Runnable接口的类实例,接着将该实例对象作为target传入Thread类的构造器,此时创建的Thread类才是线程对象。
  • 调用线程对象的start()方法启动线程。
  • 示例代码如下;
public class ThreadTest {
    public static void main(String[] args){
        new Thread(new TestRunnable()).start();
    }
}

class TestRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("Test Runnable");
    }
}

实现Callable类:

  • 创建一个类实现Callable接口,重写其中的call方法,即线程执行体,call方法存在返回值。
  • 创建实现Callable接口的类实例,将该实例对象作为callable传入FutureTask类的构造器,此FutureTask对象封装了该Callable对象的call()方法的返回值,可使用get方法获取。
  • 将FutureTask实例对象作为target传入Thread的构造器,此时创建的Thread类才是线程对象。
  • 调用线程对象的start()方法启动线程。
  • 示例代码如下;
public class ThreadTest {
    public static void main(String[] args){
        FutureTask<Integer> task = new FutureTask(new TestCallable());
        new Thread(task).start();
        try {
            System.out.println(task.get());//get方法会得到call执行完之后的值
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

class TestCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int num = 0;
        while(num < 5){
            num++;
        }
        return num;
    }
}

6、sleep() 和 wait()

sleep():

  • sleep()方法是线程类Thread类的一个静态方法,如若是在synchronized中调用,sleep()方法不会释放锁。

wait():

  • wait()方法是线程类Object类的一个方法,如若是在synchronized中调用,wait()方法会释放锁。
  • 示例代码如下(从控制台输出的时间戳结果可以看出sleep没有释放锁,是阻塞进行的,而wait释放了锁):
public class ThreadTest {
    protected static volatile Object lock = "lock";

    public static void main(String[] args){

        for(int i=0;i<5;i++){
            new Thread(() -> {
                synchronized(lock){
                    System.out.println(Thread.currentThread().getName() + ":等待开始当前时间戳:" + System.currentTimeMillis());
                    try {
//                        lock.wait(1000);//让当前线程进入等待池
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
}

7、关于wait() 与 notify(),notifyAll()

notify(),notifyAll():

  • notify()指的是随机唤醒一个wait线程进入对象锁池中竞争锁,而notifyAll()是唤醒所有的wait线程进入对象锁池中竞争锁。
  • 关于wait()与notify()方法示例代码如下:
public class ThreadTest {
    protected static volatile Object lock = "lock";

    public static void main(String[] args){
        new Thread(new TestRunnable(lock)).start();
        try {
            Thread.sleep(1000);
            System.out.println("sleep 1000ms");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new TestThread(lock).start();

    }
}

class TestRunnable implements Runnable{
    private Object lock;
    public TestRunnable(Object lock){
        this.lock = lock;
    }
    @Override
    public void run() {
        try {
            synchronized (lock){
                System.out.println("begin wait:" + System.currentTimeMillis());
                System.out.println("Release lock........");
                lock.wait();//让当前线程进入等待池
                System.out.println("end wait:" + System.currentTimeMillis());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class TestThread extends Thread{
    private Object lock;
    public TestThread(Object lock){
        this.lock = lock;
    }
    @Override
    public void run() {
        synchronized (lock){
            System.out.println("begin notify:" + System.currentTimeMillis());
            System.out.println("do something........");
            lock.notify();//从等待池中随机唤醒一个wait线程进入锁池竞争锁
//            lock.notifyAll();//从等待池中随机唤醒所有wait线程进入锁池竞争锁
            System.out.println("end notify:" + System.currentTimeMillis());
        }
    }
}

8、start() 和 run()

start():

  • 此方法是用来启动一个线程的,执行了start()方法之后,线程将进入就绪状态,之后会自动执行run函数中的内容,无需等待,是真正意义上的实现了多线程。

run():

  • 单独运行此方法,只是将其当做一个普通函数在使用,每次执行必须等待run函数里面的内容完成,才能接着往下执行,无法实现多线程。

9、线程安全性

主要体现在下面三方面:

  • 原子性:同一时刻只允许一个线程对数据进行操作(atomic开头的原子类,synchronized,Lock)。
  • 可见性:一个线程对共享变量的修改,可以及时地被其他线程观察到,(synchronized,volatile,Lock)。
  • 有序性:指的是可以观察到其他线程的指令执行顺序,由于指令重排,一般情况下是无序的(happens-before原则)。

10、线程死锁

死锁:

  • 指的是多个线程在执行过程中,因争夺资源而陷入环路阻塞的一种现象。
  • 示例代码如下(运行之后,两个线程陷入死锁中,如果没有外力中断,将会一直锁定):
public class ThreadTest {
    //共享资源A
    private static Object A = new Object();
    //共享资源B
    private static Object B = new Object();

    public static void main(String[] args){
        new Thread(() -> {
            synchronized (A){
                System.out.println(Thread.currentThread().getName() + ":得到资源---A");
                try {
                    Thread.sleep(1000);//此处休眠是为了让其他线程获得执行机会
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ":去获取资源---B");
                synchronized (B){
                    System.out.println(Thread.currentThread().getName() + ":得到资源---B");
                }
            }
        },"Thread-01").start();

        new Thread(() -> {
            synchronized (B){
                System.out.println(Thread.currentThread().getName() + ":得到资源---B");
                try {
                    Thread.sleep(1000);//此处休眠是为了让其他线程获得执行机会
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ":去获取资源---A");
                synchronized (A){
                    System.out.println(Thread.currentThread().getName() + ":得到资源---A");
                }
            }
        },"Thread-02").start();
    }
}

死锁四个必要条件即如何避免死锁:

  • 互斥条件:一个资源在任意时刻只能被一个线程占用,如果有其他线程请求该资源,需要等待原线程释放资源(此条件不能被破坏,因为锁本身就是为了互斥访问)。
  • 请求和保持条件:一个线程已经获取了一部分资源,又去请求其他资源,如果其他资源正被占用,原线程陷入阻塞,且不会释放自己占用的资源(一次性申请所有资源;或者阻塞时,释放自己占用的资源)。
  • 不可剥夺条件:一个线程已经获得的资源,在自己未使用完之前不能被其他线程剥夺,只能自己使用结束后释放(如果去获取正在被其他线程使用的资源而阻塞时,可以释放自己占用的资源)。
  • 环路等待条件:多个进程之间形成一种头尾相接的循环等待资源关系(按一定的顺序来获取资源)。
  • 针对上述例子而言,可以让现线程1先获取资源AB,执行完之后,再让线程2获取资源AB,改动如下(执行顺序为:线程1先获取资源A,接着释放CPU,线程2执行准备去获取资源A,发现资源A已被占用,此时线程2阻塞,然后一秒之后释放CPU,线程1接着执行,去获取资源B,正常获取,执行完毕,释放CPU;线程2开始获取资源A,由于线程1已经执行完毕释放了锁,所以线程2正常获取资源A,接着休眠一秒释放CPU,然后又去获取资源B,同样正常获取,执行完毕):
public class ThreadTest {
//共享资源A
private static Object A = new Object();
//共享资源B
private static Object B = new Object();

public static void main(String[] args){
new Thread(() -> {
synchronized (A){
System.out.println(Thread.currentThread().getName() + ":得到资源---A");
try {
Thread.sleep(1000);//此处休眠是为了让其他线程获得执行机会
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":去获取资源---B");
synchronized (B){
System.out.println(Thread.currentThread().getName() + ":得到资源---B");
}
}
},"Thread-01").start();

new Thread(() -> {
//线程2去获取A时被阻塞
synchronized (A){
System.out.println(Thread.currentThread().getName() + ":得到资源---A");
try {
Thread.sleep(1000);//此处休眠是为了让其他线程获得执行机会
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":去获取资源---B");
synchronized (B){
System.out.println(Thread.currentThread().getName() + ":得到资源---B");
}
}
},"Thread-02").start();
}
}

  (以上所有内容皆为个人笔记,如有错误之处还望指正。)




上一篇:java基础(5):流程控制语句(switch)、数组
下一篇:没有了

友情链接:
  • 全球最大晶圆代工半导体制造厂,台积电斥资订购艾斯摩尔机器设备
  • 英特尔依然是那个英特尔,且看英特尔的城防体系
  • 支持双 DRAM 内存接口,慧荣企业级 SSD 主控方案披露
  • 在全球被反垄断罚款,冤!高通到底哪里得罪了欧盟?
  • 强强联合!万业、微电子所和芯鑫共同打造全新半导体设备
  • 复旦大学校长称:对于集成电路产业发展,大学应该主动担当
  • 技术再升级!无锡中科芯攻克晶圆级再布线及晶圆级凸点制备关键技
  • 聚力!万业企业设立集成电路装备集团,提供自主可控设备
  • 德州仪器C2000微控制器增强连通性和控制性
  • 英特尔打出降价策略,以免被竞争对手 AMD 打败?
  • 贸易摩擦的闹剧没有赢家,苹果有勇气离开中国吗?
  • 图像信号与视觉处理器的发展趋势
  • 真干快消品定位方案班(第二期)火热开班
  • Java集合 ArrayList原理及使用
  • TDD(测试驱动开发)死了吗?
  • JAVA基础之XML相关
  • javaweb项目搭建ehcache缓存系统
  • 每日一码——字符串统计
  • 一篇文章帮你彻底搞清楚“I/O多路复用”和“异步I/O”的前世今生
  • 九:模板方法模式
  • 十二:命令模式(人员解耦和)
  • Java 转PPT为图片、PDF、SVG、XPS、ODP以及PPT和PPTX互转
  • SpringCloud学习(SPRINGCLOUD微服务实战)一
  • 记一次微信网页授权后获取用户信息并重定向
  • 速途新营销五点实战洞察解码“品效合一”
  • 十一:外观模式详解(Service,action与dao)
  • 手把手教你学会 基于JWT的单点登录
  • mysql锁机制总结,以及优化建议
  • 解决多个版本jar包冲突【jar内包名重命名】
  • 中国首张5G终端电信设备进网许可证 华为Mate 20 X 5G版入网
  • RPC之Thrift
  • 高级Java工程师必备 ----
  • 天猫618期间实物支付GMV增长38.5%
  • 换季了,老板你的库存处理好了吗?
  • 从“618”大数据看中国消费新活力
  • 小米生态链:贵在格局感与收放度
  • CODING 2.0 企业级持续交付解决方案
  • 老铁奇趴“新京济” 快手*京东618战报出炉
  • 中小企业新媒体运营基本技能
  • 上汽大通房车再度携手LINE FRIENDS 魔都巡游顺利开启
  • 华为高端手机国内市场份额超苹果夺得榜首
  • 中国智能制造分析报告
  • iPlus艾加营销助力腾讯广告牵手吴晓波 推进商业IP变现
  • 2019世界新能源汽车大会7月1日将在海南举行
  • 区域酒企如何转型突围
  • 时时彩论坛
  • 五星体育斯诺克
  • 北单比分直播
  • 河北11选5走势图
  • 福建体彩36选7开奖结果
  • 九龙图库下载