Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题

在这里插入图片描述

前言

多线程作为编程语言中的难点,虽然初级程序员可能很少遇到线程相关的开发任务,但是作为程序员,持续学习和保持对编程的热爱,要求我们对于线程也需要有一定的认识。

本篇博客介绍Java中创建线程的4种方式,并进行了简单的对比;介绍了线程的生命周期,几个关键方法的作用;然后阐述了线程的三大特性,最后结合Java集合框架分析了线程安全的问题。

其他关于Java线程的文章如下:

  • Java进阶(5)——创建多线程的方法extends Thread和implements Runnable的对比 & 线程池及常用的线程池
  • Java进阶(6)——抢购问题中的数据不安全(非原子性问题)& Java中的synchronize和ReentrantLock锁使用 & 死锁及其产生的条件

文章目录

  • 前言
  • 引出
  • 一、创建多线程的方式
    • 1、继承Thread类
      • 当前线程:Thread.currentThread()
    • 2、实现Runable接口
    • 3、实现Callable接口
    • 4、线程池
  • 二、线程的生命周期
    • join():运行结束再下一个
    • yield():暂时让出cpu的使用权
    • deamon():守护线程,最后结束
    • sleep():如果有锁,不会让出
  • 三、线程的三大特性
    • 原子性:AtomicInteger
      • CAS
    • 可见性:加volatile关键字
    • 有序性:引用有了,对象还没
  • 四、集合中的线程安全问题
    • 1、List集合的问题
      • 用vector解决
      • 用Collections.synchronizedList
    • 2、表格总结
    • 3、关于hash算法
  • 总结

引出


1.线程创建的方式,继承Thread类,实现Runable接口,实现Callable接口,采用线程池;
2.线程生命周期: join():运行结束再下一个, yield():暂时让出cpu的使用权,deamon():守护线程,最后结束,sleep():如果有锁,不会让出;
3.线程3大特性,原子性,可见性,有序性;
4.list集合中线程安全问题,hash算法问题;

一、创建多线程的方式

1、继承Thread类

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第1张图片

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第2张图片

调用的流程:

  • 调用start()
  • 线程处于准备状态,一旦cup有闲的时间片,
  • 让线程调用run()方法

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第3张图片

package com.tianju.threadLearn;

public class ThreadA extends Thread{

    @Override
    public void run(){
        System.out.println("我是线程A:"+this.getName());
    }

    public static void main(String[] args) {
        ThreadA threadA = new ThreadA();
        threadA.setName("线程A");

        threadA.start(); // 我准备好了

        String name = Thread.currentThread().getName();
        System.out.println(name);

        // 获得当前线程的路径
        String path = Thread.currentThread().getContextClassLoader().getResource("").getPath();
        System.out.println(path);

    }
}

当前线程:Thread.currentThread()

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第4张图片

2、实现Runable接口

和继承Thread相比,这个用的更多,因为Java是单继承的,只能继承一个,而可以实现多个接口,所以更加灵活

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第5张图片

package com.tianju.threadLearn;

/**
 * 这个常用,因为java是单继承,如果继承了extends Thread;
 * 就不能继续继承了;
 */
public class ThreadB implements Runnable{

    private String name;
    public ThreadB(String name){
        this.name = name;
    }

    @Override
    public void run() {
        for(int i =0;i<10;i++){
            System.out.println("当前线程:"+this.name);
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) {
        new Thread(new ThreadB("B")).start();
    }
}

3、实现Callable接口

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第6张图片

package com.tianju.threadLearn;


import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * Callable,可以有返回值
 */
public class ThreadC implements Callable<Long> {



    @Override
    public Long call() throws Exception {
        long sum = 0;
        for (int i = 0; i < 500000; i++) {
            sum+=i;
        }
        return sum;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<Long> threadC = new FutureTask<Long>(new ThreadC());
        Thread thread = new Thread(threadC);
        thread.start();
        Long aLong = threadC.get();
        System.out.println(aLong);
    }
}

在这里插入图片描述

在这里插入图片描述

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第7张图片

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第8张图片

4、线程池

池化技术pool【常量池、数据连接池、线程池】

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第9张图片

package com.tianju.threadLearn;


import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class PoolDemo1 {
    public static void main(String[] args) {
        System.out.println("===============缓存线程池================");
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i=0;i<10;i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread());
                }
            });
        }
        System.out.println("===============固定线程池================");
        ExecutorService executorService2 = Executors.newFixedThreadPool(1);
        for (int i=0;i<10;i++) {
            executorService2.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread());
                }
            });
        }
    }
}

二、线程的生命周期

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第10张图片

join():运行结束再下一个

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第11张图片

package com.tianju.threadLearn;

public class ThreadA1 extends Thread{

    static int c = 0;

    @Override
    public void run(){
        for (int i = 0; i < 20; i++) {

            try {
                System.out.println(this.getName()+": "+i);
                sleep(300);
                c++;
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadA1 a = new ThreadA1();
        a.setName("A");

        ThreadA1 b = new ThreadA1();
        b.setName("B");
        a.start();
        a.join(); // A运行结束,B才能开始运行

        b.start();
        b.join(); // 控制执行顺序

        System.out.println(Thread.currentThread().getName());
        System.out.println("c的结果为: "+c);

    }
}

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第12张图片

yield():暂时让出cpu的使用权

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第13张图片

package com.tianju.threadLearn;

/**
 * yield,让出cpu的使用权
 */
public class ThreadA2 extends Thread{

    @Override
    public void run(){
        for (int i = 0; i < 20; i++) {

            try {
                if (i%2==0){
                    // 让出cpu的使用权,避免一个线程一直占有cpu,防止独占cpu
                    // 重新竞争
                    yield();
                }
                System.out.println(this.getName()+": "+i);
                sleep(300);

            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadA2 a = new ThreadA2();
        a.setName("A");

        ThreadA2 b = new ThreadA2();
        b.setName("B");
        a.start();

        b.start();

        System.out.println(Thread.currentThread().getName());

    }
}

deamon():守护线程,最后结束

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第14张图片

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第15张图片

package com.tianju.threadLearn;

/**
 * 守护线程
 * 用户线程
 *
 */
public class ThreadA3 extends Thread{

    @Override
    public void run(){

        while (true){
            System.out.println("我是守护线程.......");
            try {
                sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {

        ThreadA3 a = new ThreadA3();
        a.setName("A");
        a.setDaemon(true); // 守护线程,用户线程main结束后,他就结束了
        a.start();

        for (int i =0;i<10;i++){
            System.out.println(Thread.currentThread()+":"+i);
            Thread.sleep(200);
        }

    }
}

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第16张图片

sleep():如果有锁,不会让出

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第17张图片

package com.tianju.threadLearn;

public class ThreadSleep extends Thread{



    @Override
    public void run(){

        for (int i=0;i<10;i++){
            System.out.println("我是线程A:"+this.getName());

            try {
                sleep(200); // 不会让出锁
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

    }

    public static void main(String[] args) {
        ThreadSleep threadA = new ThreadSleep();
        threadA.setName("线程A");

        threadA.start(); // 我准备好了

        String name = Thread.currentThread().getName();
        System.out.println(name);

    }
}

三、线程的三大特性

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第18张图片

原子性:AtomicInteger

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第19张图片

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第20张图片

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第21张图片

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第22张图片

package com.tianju.tx;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 解决原子性:AtomicInteger
 */
public class AtomicDemo1 {
    static AtomicInteger x= new AtomicInteger();
    static class A extends Thread{
        @Override
        public void run() {

            for (int i = 0; i < 20; i++) {
                x.incrementAndGet();
                System.out.println(getName()+ "--x:"+x);
                try {
                    sleep(200);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

            }
        }
    }

    public static void main(String[] args) {
        new A().start();
        new A().start();
        new A().start();
    }
}

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第23张图片

CAS

CAS(Compare And Swap)

  • 从主存拷贝到工作内存(线程)
  • 修改,和主存比较,如果如果和取时结果一致,刷新到主存中。

ABA问题:

如果红色线程拿到了主存中的3,进行加1,;

蓝色线程也拿到了主存中的3,进行了加1,然后又减1;

红色线程一直问主存里面是不是3,然后发现还是3,就把4写到主存里面;

但是此时的3已经被蓝色线程动过了;

解决方案是,加一个版本号,每次被操作,就让版本号加1

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第24张图片

可见性:加volatile关键字

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第25张图片

package com.tianju.view;

/**
 * 线程之间不可见
 * volatile 用于解决可见性
 */
public class VisibleDemo2 {
    volatile static boolean f = true; // 处理可见性,解决不了原子性
    static class A extends Thread{

        @Override
        public void run() {
            while (f){

            }
            System.out.println("A的f为:"+f);
        }
    }
    static class B extends Thread{
        @Override
        public void run() {
            f=false;
            System.out.println("B设置f为:"+f);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new A().start();
        Thread.sleep(300);
        new B().start();
    }
}

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第26张图片

有序性:引用有了,对象还没

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第27张图片

假设: 编译器修改顺序 1 , 3, 2

线程A执行到对象初始化阶段,还没有初始化;

线程B先获得的对象的引用,然后调用对象,

但是对象还没有初始化,因此会报错,对象初始化错误

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第28张图片

四、集合中的线程安全问题

1、List集合的问题

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第29张图片

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第30张图片

package com.tianju.collection;

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

public class ListDemo {
    static List<Integer> list = new ArrayList<>();

    // 有序性+原子性保证
    volatile static AtomicInteger index =new AtomicInteger(0);
    static class A extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                list.add(index.incrementAndGet());
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        A a1 = new A();
        A a2 = new A();
        A a3 = new A();
        a1.start();
        Thread.sleep(500);
        a2.start();
        a3.start();
        System.out.println(list);
    }
}

用vector解决

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第31张图片

package com.tianju.collection;

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

public class VectorDemo {
    static Vector<Integer> list = new Vector<>();

    // 有序性+原子性保证
    volatile static AtomicInteger index =new AtomicInteger(0);
    static class A extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                list.add(index.incrementAndGet());
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        A a1 = new A();
        A a2 = new A();
        A a3 = new A();
        a1.start();
        a1.join(); // A运行结束,B才能开始运行
        a2.start();
        a2.join();
        a3.start();
        a3.join();
        System.out.println(list);
    }
}

用Collections.synchronizedList

package com.tianju.collection;

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

public class CollectionDemo {
    static List<Integer> ls = new ArrayList<>();
    static List<Integer> list = Collections.synchronizedList(ls);

    // 有序性+原子性保证
    volatile static AtomicInteger index =new AtomicInteger(0);
    static class A extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                list.add(index.incrementAndGet());
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        A a1 = new A();
        A a2 = new A();
        A a3 = new A();
        a1.start();
        a1.join(); // A运行结束,B才能开始运行
        a2.start();
        a2.join();
        a3.start();
        a3.join();
        System.out.println(list);
    }
}

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第32张图片

2、表格总结

序号 线程不安全 线程安全
1 ArrayList Vector/ Collections.synchronizedList(list)
2 HashMap ConcurentHashMap/HashTable
3 HashSet Collections.synchronizedSet()

Hashset的底层是HashMap

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第33张图片

3、关于hash算法

比如一开始有蓝色点,经过hash算法之后,得到结果3,然后放到相应的位置;

然后又来了一个红色的点,经过hash算法之后,也是3,此时就顺着蓝色的位置去找;

之前是采用头插法,就是新来的放到蓝色的之前,在1.8之后改成尾插法,放到蓝色的后面;

如果又有新来的,就继续往后面加,此时出现了不利的情况,就是越来越多成了一个很长的链表;

所以就采用了树的结果,这样提高的查找的效率;在1.8中采用的红黑树;

Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题_第34张图片


总结

1.线程创建的方式,继承Thread类,实现Runable接口,实现Callable接口,采用线程池;
2.线程生命周期: join():运行结束再下一个, yield():暂时让出cpu的使用权,deamon():守护线程,最后结束,sleep():如果有锁,不会让出;
3.线程3大特性,原子性,可见性,有序性;
4.list集合中线程安全问题,hash算法问题;

你可能感兴趣的:(Java,java,安全,python)