初识java多线程

概述

马老师多线程视频学习总结(好记性不如烂笔头)。

初识多线程

线程的两种创建方式

继承Thread类和实现Runnable接口

package com.test.thread.msb;

public class T1 {

    public static void main(String[] args) {
        new Thread(new MyThread1()).start();
        new Thread(new MyThread2()).start();
    }
    
    // 实现Runnable接口
    static class MyThread1 implements Runnable {

        @Override
        public void run() {
            System.out.println("I am runnable, i am start..." + Thread.currentThread().getName());
        }
    }
    
    // 继承Thread类
    static class MyThread2 extends Thread {

        @Override
        public void run() {
            System.out.println("I am thread, i am start..." + Thread.currentThread().getName());
        }
    }
}

程序执行结果:
代码执行结果
使用继承Thread类和实现Runnable接口的关系和区别:

  • Thread类本身也是实现了Runnable接口,因此也是实现了Runnable接口中的run方法。
  • 当使用继承Thread类去实现线程时,我们需要重写run方法,因为Thread类中的run方法本身什么事情都不干。
  • 当使用实现Runnable接口去实现线程时,我们需要重写run方法,然后使用new Thread(Runnable)这种方式来生成线程对象,这个时候线程对象中的run方法才会去执行我们自己实现的Runnable接口中的run方法。

多线程会出现的线程不安全问题

当多个数据同时操作同一个数据时,可能会产生线程不安全的问题。

package com.wz.code.test.com.wz.code.test.thread;

import java.util.ArrayList;
import java.util.List;

public class T2 {

    private int count = 0;

    void m() {
        for (int i = 0; i < 100000; i++)
            count++;
    }

    public static void main(String[] args) {
        T2 t2 = new T2();
        long start = System.currentTimeMillis();
        List<Thread> threads = new ArrayList<Thread>();
        for (int i = 0; i < 10; i++) {
            threads.add(new Thread(t2::m, "threads:" + i));
        }

        threads.forEach((o)->o.start());
        // 等待线程执行完执行主线程
        threads.forEach((o)->{
            try {
                o.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        System.out.println(t2.count);
        long end = System.currentTimeMillis();
        long time = end - start;
        System.out.println("used time: " + time);
    }
}

程序执行结果:
在这里插入图片描述
我们发现结果并不是100000。这就是线程不安全问题。

认识synchronized和volatile关键字

java线程抽象内存模型

在这里插入图片描述
java的线程抽象内存模型中定义了每个线程都有一份自己的私有内存,里面存放自己私有的数据,其他线程不能直接访问,而一些共享数据则存在主内存中,供所有线程进行访问。
上图中,如果线程A和线程B要进行通信,就要经过主内存,比如线程B要获取线程A修改后的共享变量的值,要经过下面两步:

  1. 线程A修改自己的共享变量副本,并刷新到了主内存中。

  2. 线程B读取主内存中被A更新过的共享变量的值,同步到自己的共享变量副本中。

java多线程中的原子性、可见性、有序性

  • 原子性:是指线程的多个操作是一个整体,不能被分割,要么就不执行,要么就全部执行完,中间不能被打断。

  • 可见性:是指线程之间的可见性,就是一个线程修改后的结果,其他的线程能够立马知道。

  • 有序性:为了提高执行效率,java中的编译器和处理器可以对指令进行重新排序,重新排序会影响多线程并发的正确性,有序性就是要保证不进行重新排序(保证线程操作的执行顺序)。

volatile关键字的作用

其实volatile关键字的作用就是保证了可见性和有序性(不保证原子性),如果一个共享变量被volatile关键字修饰,那么如果一个线程修改了这个共享变量后,其他线程是立马可知的。为什么是这样的呢?比如,线程A修改了自己的共享变量副本,这时如果该共享变量没有被volatile修饰,那么本次修改不一定会马上将修改结果刷新到主存中,如果此时B去主存中读取共享变量的值,那么这个值就是没有被A修改之前的值。如果该共享变量被volatile修饰了,那么本次修改结果会强制立刻刷新到主存中,如果此时B去主存中读取共享变量的值,那么这个值就是被A修改之后的值了。
volatile能禁止指令重新排序,在指令重排序优化时,在volatile变量之前的指令不能在volatile之后执行,在volatile之后的指令也不能在volatile之前执行,所以它保证了有序性。

synchronized关键字的作用

synchronized提供了同步锁的概念,被synchronized修饰的代码段可以防止被多个线程同时执行,必须一个线程把synchronized修饰的代码段都执行完毕了,其他的线程才能开始执行这段代码。
因为synchronized保证了在同一时刻,只能有一个线程执行同步代码块,所以执行同步代码块的时候相当于是单线程操作了,那么线程的可见性、原子性、有序性(线程之间的执行顺序)它都能保证了。

volatile关键字和synchronized关键字的区别

  1. volatile只能作用于变量,使用范围较小。synchronized可以用在变量、方法、类、同步代码块等,使用范围比较广。
  2. volatile只能保证可见性和有序性,不能保证原子性。而可见性、有序性、原子性synchronized都可以包证。
  3. volatile不会造成线程阻塞。synchronized可能会造成线程阻塞。
package com.wz.code.test.thread;

public class T {

    public synchronized void m1() {
        System.out.println("m1 start...");
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("m1 end...");
    }
    
    public void m2() {
        System.out.println("m2 start...");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("m2 end...");
    }
    
    public static void main(String[] args) {
        T t = new T();
        new Thread(t::m1, "t1").start();
        new Thread(new Runnable() {
            
            @Override
            public void run() {
                t.m2();            
            }
        }).start();
    }
}

使用volatile 和synchronized

package com.wz.code.test.thread;

import java.util.ArrayList;
import java.util.List;

public class VolatileTest {

    volatile int count;

    synchronized void m() {
        for (int i = 0; i < 100000; i++)
            count++;
    }

    public static void main(String[] args) {
        VolatileTest vt = new VolatileTest();
        long start = System.currentTimeMillis();
        List<Thread> threads = new ArrayList<Thread>();
        for (int i = 0; i < 10; i++) {
            threads.add(new Thread(vt::m, "thread:" + i));
        }

        threads.forEach((o) -> o.start()); // JDK1.8新特性
        threads.forEach((o) -> { // JDK1.8新特性
            try {
                o.join(); // 等线程执行完毕之后才执行主线程main
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        System.out.println(vt.count);
        long end = System.currentTimeMillis();
        long time = end - start;
        System.out.println("used time: " + time);
    }
}

在这里插入图片描述
使用原子类

package com.wz.code.test.thread;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

public class T3 {

    AtomicInteger count = new AtomicInteger(0);

    void m() {
        for (int i = 0; i < 100000; i++)
            count.incrementAndGet();
    }

    public static void main(String[] args) {
        T3 t3 = new T3();
        long start = System.currentTimeMillis();
        List<Thread> threads = new ArrayList<Thread>();
        for (int i = 0; i < 10; i++) {
            threads.add(new Thread(t3::m, "threads:" + i));
        }

        threads.forEach((o)->o.start());
        // 等待线程执行完执行主线程
        threads.forEach((o)->{
            try {
                o.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        System.out.println(t3.count);
        long end = System.currentTimeMillis();
        long time = end - start;
        System.out.println("used time: " + time);
    }
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/wz122330/article/details/88291983