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;

/*
第二种方式:实现Runnable接口
步骤:
(1)编写线程类,实现Runnable接口
(2)重写接口的抽象方法public void run()
(3)创建自定义线程类的对象
(4)创建一个Thread类的对象,同时让Thread对象代理我们的自定义线程对象
创建它的目的是为了调用start方法
(5)启动线程

线程调度器会调用t对象的run方法,因为这里启动的是t线程。(t.start())
Thread类的run()
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

 这里的target对象就是创建Thread类对象时传入的Runnable接口的实现类对象,即被代理对象。
 当my实参给target赋值后,target就不会为null,就会执行my对象的run方法。
 */
public class TestCreateThread2 {
    public static void main(String[] args) {
        MyRunnable my = new MyRunnable();
        Thread t = new Thread(my);//让t对象代理my对象
        t.start();

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

/*        Thread t = new Thread(new Runnable(){
            @Override
            public void run() {
                //让这个线程打印[1-10]的偶数
                for(int i=2; i<=10; i+=2){
                    System.out.println("自定义线程: " + i);
                }
            }
        });
        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;

/*
java.lang.Thread类有哪些方法?
系列方法一:构造方法
- public Thread() :分配一个新的线程对象。
- public Thread(String name) :分配一个指定名字的新的线程对象。
- public Thread(Runnable target) :分配一个带有指定目标新的线程对象。
- public Thread(Runnable target,String name) :分配一个带有指定目标新的线程对象并指定名字。

String getName():获取线程的名称
    如果没有手动指定线程名称,默认是Thread-编号,从0开始。
    如果需要手动指定线程名称,可以通过构造器,或者setName(String name)方法设置线程名称。

static Thread currentThread():获取执行当前语句的线程对象。
 */
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继承的
    }
}

/*
和线程的状态有关的方法:
public static void sleep(long millis)throws InterruptedException:线程休眠,单位毫米
public static void yield():让当前线程暂停一下
    当前线程暂停下,让出CPU,但是下一次CPU有可能还是调用它。
void join() throws InterruptedException :等待该线程终止。  该线程是调用join方法的线程。
void join(long millis) :等待该线程终止的时间最长为 millis 毫秒。如果millis时间到,将不再等待。
void join(long millis, int nanos) :等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。
 */

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;

/*
1、什么是单例设计模式?

设计模式:是前面的很多程序员根据自己的经验,总结出来的代码的套路,代码的模板。
常见的经典的设计模式一共有23种。设计模式不分语言。

单例设计模式是指某个类的对象在整个应用程序中只有唯一的一个。
例如:手机系统中  时钟
     Java的JVM运行环境。
     后期的Spring框架中容器管理对象....

2、如何实现单例?

(1)构造器私有化
(2)在类的内部创建好这个类的唯一对象

3、单例设计模式的形式
(1)饿汉式
饿:很着急,无论你是否要用到这个类的对象,在类初始化的时候,就直接创建了它对象。
(2)懒汉式
懒:不着急,等你需要这个对象的时候,再创建这个对象。

面试题:请写出单例设计模式的示例代码。
面试题:请写出懒汉式单例设计模式的示例代码。
面试题:请写出饿汉式单例设计模式的示例代码。
 */
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 {
            System.out.println("内部类的静态代码块");
        }*/
        static Five instance = new Five();
    }

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

   /* static {
        System.out.println("外部类的静态代码块");
    }

    public static void method(){
        System.out.println("外部类的method方法");
    }*/
}


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类中。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值