很多人不知道的进程与线程,送给你!!!

多线程

并发与并行

并行:两个事件在同一时刻都在发生。
并发:两个事件在同一个时间段内都在发生。(交替执行)

进程与线程

进程:正在内存中运行的程序,称为进程。比如安装在计算机硬盘中的软件(QQ、360等),当要双击运行时,会在内存中运行起来。每个软件都有一个属于自己的进程。
线程:进程中用与执行某个功能的执行单位。线程属于某个进程,一个进程可以有多个线程,每个线程运行互不影响。

进程与线程的区别

线程是属于某个进程。
每一个进程有自己独立的内存空间(独立的栈、独立的堆),且至少有一个线程。
每个线程都会跟进程中申请一快独立的栈,共享进程的堆。

线程调度

线程调度是指CPU在不同的进程不同的线程之间进行快速切换
一个单核CPU,在同一时刻只能执行某个进程中的某个线程。

线程调度的分类

分时调度:每个线程平均拥有CPU的执行权。
抢占式调度:每个进程随机分配CPU的执行权(具体的分配和线程的优先级有关)。
我们java程序(java进程)中所有的线程采用抢占式调度。

Thread类的介绍【重点】

我们右键运行的是一个java进程,该进程中默认至少有两个线程。
a、main方法所在的线程,称为主线程。
b、垃圾回收器线程。
我们是否可以再创建一个新的线程?
可以!!因为java已经把代表线程的类封装好了,Thread类

Thread类的构造方法

public Thread();//无参构造,线程会有默认的名字 Thread-0。
public Thread(String name);//带有线程名字的构造。

public Thread(Runnable r);//带有线程任务的构造。
public Thread(Runnable r,String name);//既带有名字又带有任务的构造。

Thread类的成员方法

public String get Name();//获取线程名字
public void setName(String name);//修改线程名字。
public void run();//代表线程要执行的任务,任务有的代码要写在方法此方法中
public void start();//线程只创建并不会执行,必须要调用start开启后才会执行任务

public static void sleep(long millis);//让当前线程休眠多少毫秒
public static Thread currentThread();//获取当前线程对象

创建新的线程的两种方式【重点】

一、继承方式

创建步骤:
1、创建子类继承Thread。
2、子类中重写run方法(在run方法中编写线程要执行的任务代码)。
3、创建子类的对象(实际上就是创建一个线程对象)。
4、调用线程对象的start方法,启动该线程。

代码实现:

MyThread.java

//创建子类继承Thread
public class MyThread  extends Thread{
    //重写run方法,在run中编写要执行的代码
    @Override
    public void run() {
        for (int i = 0; i <50 ; i++) {
            System.out.println("子线程"+i);
        }
    }
}

ThreadTest.java

public class ThreadTest {
    public static void main(String[] args) {
        //创建子线程对象
        MyThread myThread = new MyThread();
        //启动该线程
        myThread.start();
        //主线程 不会等待子线程任务结束
        for (int i = 0; i < 50; i++) {
            System.out.println("主线程" + i);
        }
    }
}

注意:
我们可以给线程起名,也可以使用默认的名字。
我们获取线程的名字的时候,尽量使用通用方式( Thread currentThread().getNmae);如果是子线程内部,可以直接调用getName()。

二、实现方式

步骤:
1、创建实现类,实现Runnable接口(实际上接口中有任务方法,run方法)。
2、实现类重写run()方法(run中编写具体的任务代码)。
3、创建实现类对象(该实现类对象并不是线程对象,我们称为任务对象)。
4、创建Thread对象,同时传入实现类对象。
public Thread(Runnable r);//带有线程任务的构造
5、启动该线程(调用start方法)。

代码实现:

MyRunnable.java

public class MyRunnable  implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i <50 ; i++) {
            System.out.println("子线程"+i);
        }
    }
}

MyRunnableTest.java

public class MyRunnableTest {
    public static void main(String[] args){
        //创建实现类对象(该实现类对象并不是线程对象,我们称为任务对象
        MyRunnable myRunnable = new MyRunnable();
        //创建Thread对象,同时传入实现类对象
        Thread thread = new Thread(myRunnable);
        //启动线程
        thread.start();
        //主线程不会等待子线程结束
        for (int i = 0; i < 50; i++) {
            System.out.println("主线程"+i);
        }
    }
}

三、两种方式的比较

实现方式比继承方式所具有的优势
1、实现方式线程和任务是分开的,是由程序员自己组合。
2、实现方式避免了java单继承的不足。
3、增加程序的健壮性,实现解耦操作。
4、对于线程池来说,我们需要的是Runnable的实现类,不需要Thread的子类。

匿名内部类简化线程创建方式【重点】

作用:可以快速创建一个类的子类对象或者一个接口的实现类对象。

代码实现:

TestDemo.java

public class TestDemo {
    public static void main(String[] args) {
        //1、继承方式创建线程
        new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 50; i++) {
                    System.out.println(Thread.currentThread().getName() + i);
                }
            }
        }.start();
        //2、实现方式创建线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 50; i++) {
                    System.out.println(Thread.currentThread().getName() + i);
                }
            }
        }).start();
        //主线程不会等待子线程结束
        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread().getName() + i);
        }
    }
}

高并发及线程安全

高并发:是指在某个时间点上,有大量的用户(线程)同时访问统一资源。
线程安全:在某个时间点上,发生高并发后,访问的数据出现”不符合实际的数据“,称为线程安全有问题。

多线程的运行机制

当一个线程启动后,JVM虚拟机会为其分配一个独立的”线程栈区“,该线程会在这个独立的栈区运行。

代码实现:

MyThread.java

public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("i=" + i);
        }
    }
}

Demo.java

public class Demo {
    public static void main(String[] args) {
        //创建两个线程对象
        MyThread myThread1 = new MyThread();
        MyThread myThread2 = new MyThread();
        //启动线程
        myThread1.start();
        myThread2.start();
    }
}

多线程安全问题——可见性

当一个共享的变量被多个线程使用时,其中某个线程对共性变量进行了修改,对于其他线程来说并不是立刻可见的。
先启动一个线程,在线程中将变量的值更改,而主线程却一直无法获得此变量的新值。

多线程安全问题——有序性

我们在写程序时,有时候”编译器“在编译代码的时候,会对代码进行重新排列,但是在”多线程“情况下,代码重新排列,可能会对另一个线程的访问的结果产生影响。

多线程安全问题——原子性

线程对一个共享变量,进行++时,这个++分两步操作,先取出值+1,然后给共享变量赋值。如果取出值+1后,还没有来得及赋值,被其他线程抢走CPU时,此时我们称为++操作不具有原子性。

volatile关键字

用来修饰成员变量(静态变量),被修饰的变量具有可见性和有序性。

volatile解决可见性

代码实现:

public class MyThread extends Thread {
    //无论创建多个MyThread对象,他们共性一个静态变量a
    public volatile static int a = 0;
    @Override
    public void run() {
        System.out.println("线程启动,休息2秒...");
        try { Thread.sleep(1000 * 2); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println("将a的值改为1");
        a = 1;
        System.out.println("线程结束...");
    }
}

public class TestSafaDemo01 {
    public static void main(String[] args) {
        //1.启动线程
        MyThread t = new MyThread();
        t.start();
        //2.主线程继续
        while (true) {
            if (MyThread.a == 1) {
                System.out.println("主线程读到了a = 1");
            }
        }
    }
}

volatile解决有序性

volatile不能解决原子性

volatile关键字小结

1、解决变量的可见性,一旦变量发生改变,所有使用到该变量的线程都会取得最新值。
2、解决变量的有序性,一旦变量加上volatile,那么编译器不会改变代码的重排。
3、无法解决变量操作过程张原子性,对变量的操作还是有可能被其他线程打断。

原子类

是对普通类型(int、Integer)的原子类封装,使其操作成员原子操作。

作用

对原子类的增加或减少操作,保证是原子性,保证中间不会被其他线程打断。
注意:原子类既可以解决原子性,也可以解决有序性和可见性。

AtomicInteger类示例

1、是对int类型变量进行操作的原子类。
2、构造方法。

public AtomicInteger(int num);

3、成员方法。

public int getAndIncrement();//就相当于 变量++
public int incrementAndGet();//就相当于 ++变量 

4、代码实现:

 public class MyThread extends Thread {
        public static AtomicInteger a = new AtomicInteger(0);
        @Override
        public void run() {
            for (int i = 0; i < 10000; i++) {
                a.getAndIncrement();//相当于 a++
            }
            System.out.println("修改完毕!");
        }
    }

	public class TestSafeDemo {
        public static void main(String[] args) throws InterruptedException {
            MyThread t1 = new MyThread();
            MyThread t2 = new MyThread();

            t1.start(); //线程1 对a加了10000次
            t2.start(); // 线程2 对a加了 10000次
            Thread.sleep(1000);
            System.out.println("获取a最终值:" + MyThread.a);
            //总是不准确的。原因:两个线程访问a 的步骤不具有:原子性
        }
    }

路就在前方,加油!!!

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值