栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

JAVA进程与线程

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JAVA进程与线程

目录

1.概念

2.线程调度

3.线程创建和启动

4.Thread类方法

5.多个线程共享数据和线程安全问题

6.单例模式

7.线程的生命周期

8.释放锁操作与死锁

9.sleep和wait方法的区别


1.概念

1、程序:
    是选择一种编程语言,完成一个功能/任务,而编写的一段代码,这段代码最后被编译/解释为指令。
    程序是一组指令的集合。
    程序是静态的。当我们电脑、手机安装了一个程序之后,只是占用硬盘/存储卡的空间。
2、进程:
    一个程序的一次运行。
    当程序启动后,操作系统都会给这个程序分配一个进程的ID,并且会给他分配一块独立的内存空间。
    如果一个程序被启动了多次,那么会有多个进程。
    多个进程之间是无法共享数据。
    如果两段代码需要进行数据的交互,成本比较高,要么通过一个硬盘的文件,要么通过网络。
    进程之间的切换成本也比较高。现在的操作系统都支持多任务,操作系统在每一个任务之间进行切换,要给整个进程做镜像,要记录当前进程的状态,执行到哪个指令的。
    操作系统分配资源的最小单位是进程。
    每一个进程至少有一个线程。
3、线程:
    进程中的其中一条执行路径。多个线程会同属于一个进程。
    这多个线程会共享同一个进程中的一些资源。比如Java中堆内存的数据,方法区的数据。
    如果同一个进程的两段代码需要进行数据的交互,非常方便,可以直接在内存中共享。当然,同时要考虑安全问题。
    线程之间的切换,需要记录的信息要少很多,因为很多线程之间的数据是共享的,这些数据就不用单独在做镜像了,只需要记录每一个线程要执行的下一条指令。
    CPU调度的最小单位是线程。

Java程序后台有几个线程是默默运行:GC线程、异常的检查和处理线程、类加载器线程。

2.线程调度

1)分时调度

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

2)抢占式调度

        优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随即选择一个

3.线程创建和启动

1.开启线程的方式:

        1)继承Thread类

        2)实现Runnable接口

        3)实现Callable接口

        4)线程池

package com.atguigu.thread;


public class TestCreateThread2 {
    public static void main(String[] args) {
        MyRunnable my = new MyRunnable();
        Thread t = new Thread(my);//让t对象代理my对象
        t.start();






        //在main线程中,打印[1-10]的奇数
        for(int i=1; i<=10; i+=2){
            System.out.println("main:" + i);
        }
    }
}

class MyRunnable implements Runnable{
    @Override
    public void run() {
        //让这个线程打印[1-10]的偶数
        for(int i=2; i<=10; i+=2){
            System.out.println("自定义线程: " + i);
        }
    }
}

4.Thread类方法

        1.线程的优先级只能设置在1-10之间,并且优先级高并不代表一直在执行优先级高的代码。

package com.atguigu.thread;


public class ThreadMethod1 {
    public static void main(String[] args) {
        SubThread s1 = new SubThread();
        SubThread s2 = new SubThread("线程2");
        SubThread s3 = new SubThread();
        s1.start();
        s2.start();
        s3.start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        },"线程5").start();
    }
}
class SubThread extends Thread{
    //子类构造器首行一定会调用父类的构造器,默认调用的就是无参构造

    public SubThread() {
        super();
    }

    public SubThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        System.out.println(getName());//getName()方法从父类Thread继承的
    }
}

5.多个线程共享数据和线程安全问题

1)什么数据可以被多个线程共享?

        a.方法区中的静态数据可以被多个线程共享

        b.堆中的实例变量也可以被多个线程共享,但是要求多个线程使用一个对象

1、使用同步机制
同步就是给某段代码加“锁”。
锁是一个对象,又被称为监视器对象。就是监视使用共享数据的这几个线程的调度执行情况。
Java对象分为3个部分:
(1)对象头:
A:包含当前对象所属类的指针
B:Mark Word:记录了和当前对象有关的GC、锁标记等信息
C:数组的长度,数组对象特有的
(2)对象的实例变量
(3)对齐空白

即每一个对象都有一个标记位,标记现在哪个线程“占有”这个锁对象。
这个标记为会记录这个线程的ID。只有“占有”锁对象的线程才有机会执行被锁的代码,称为同步代码。

2、如何给代码加锁?synchronized
(1)同步方法:锁整个方法
【修饰符】 synchronized 返回值类型 方法名(【形参列表】)【throws 异常列表】{
        方法体
}
(2)同步代码块:锁方法体里面的一小部分代码。
synchronized(锁对象){
    需要被锁起来的代码
}

3、什么时候释放锁?
(1)当synchronized锁的代码全部执行完,才会释放锁。

4、锁对象的选择问题?
(1)任意类型的对象都可以当做监视线程的锁对象。即类型不限制。
(2)必须保证使用共享数据的多个线程(具有竞争关系的多个线程)使用
同一个锁对象。
(3)同步方法的锁对象是不能自由选择的,是默认的。
非静态方法的锁对象是this对象
静态方法的锁对象是当前类的Class对象。 只要是同一个类,那么Class就一定是同一个。

6.单例模式
package com.atguigu.single;

import org.junit.Test;


public class TestSingle {
    public static void main(String[] args) {
        Runtime runtime1 = Runtime.getRuntime();//得到当前JVM的运行环境
        Runtime runtime2 = Runtime.getRuntime();//得到当前JVM的运行环境
        System.out.println(runtime1 == runtime2);
    }

    @Test
    public void test01(){
        One o1 = One.INSTANCE;
        One o2 = One.INSTANCE;
        System.out.println(o1 == o2);
    }

    @Test
    public void test02(){
        Two o1 = Two.INSTANCE;
        Two o2 = Two.INSTANCE;
        System.out.println(o1 == o2);
    }

    @Test
    public void test03(){
        Three o1 = Three.getInstance();
        Three o2 = Three.getInstance();
        System.out.println(o1 == o2);
    }

    @Test
    public void test04(){
        Four o1 = Four.getInstance();
        Four o2 = Four.getInstance();
        System.out.println(o1 == o2);
    }

    Four o1;
    Four o2 ;
    @Test
    public void test05(){
        Thread t1 = new Thread(){
            public void run(){
                o1 = Four.getInstance();
            }
        };
        t1.start();
        Thread t2 = new Thread(){
            public void run(){
                o2 = Four.getInstance();
            }
        };
        t2.start();

        try {
            t1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        try {
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("o1 = " + o1);
        System.out.println("o2 = " + o2);


        System.out.println(o1 == o2);
    }

    @Test
    public void test06(){
//        Five.method();

        Five o1 = Five.getInstance();
        Five o2 = Five.getInstance();
        System.out.println(o1 == o2);
    }
}
//懒汉式


class Five{
    private Five(){}

    private static class Inner{
 
        static Five instance = new Five();
    }

    public static Five getInstance(){
        return Inner.instance;
    }

   
}


class Four{
    private static  Four instance;//不着急创建的对象
    private Four(){};

    public static synchronized Four getInstance(){
        if(instance == null) {
            instance = new Four();
        }
        return instance;
    }
}



//饿汉式
class One{
    public static final One INSTANCE = new One();
    private One(){}
}
enum Two{
    INSTANCE
}
class Three{
    private static Three instance = new Three();
    private Three(){}
    public static Three getInstance(){
        return instance;
    }
}

7.线程的生命周期

1.在JDK1.5之前,线程生命周期有五种状态:新建,就绪,运行,阻塞,死亡。

2.在JDK1.6之后,线程生命周期有6种状态:

        a.新建:即new一个线程

        b.runnable:是1.5的就绪状态和运行状态的和合并

        c.blocked:

        d.wating:

        e.TIMED_WATING:

        f.TERMINATED:是1.5中对应的死亡状态

为什么要区分这么多阻塞状态?实际在运行过程中,线程会很多,如果所有阻塞状态全部放在一个队列中,那么这个队列会很长,如果要合适的线程唤醒,那么找这个线程比较难找。分为三个状态,就好找很多。 

8.释放锁操作与死锁

1.释放锁操作:

当线程的同步方法、同步代码块执行完成

当线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致当前线程异常结束。

当线程在同步代码块、同步方法中执行了带锁对象的wait方法,当前线程被挂起,并释放锁。

2.不会释放锁的操作:

线程执行同步代码块或者同步方法的时候,程序调用Thread.sleep()、thread.yield()方法暂停当前线程。

线程执行同步代码块的时候,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁。应尽量避免使用suspend和resume这样的过时来控制线程。

9.sleep和wait方法的区别

1)sleep不释放锁,wait释放锁

2)sleep指定休眠的时间,wait可以指定时间也可以无限等待直到notify/notifyall

3)sleep在thread类中声明的静态方法,wait方法在Object类中声明,因为调用wait方法是锁对象调用,而锁对象的类型是任意的对象,那么希望任意类型的对象都要有的方法,只能在object类中。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/944583.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号