设计模式、线程状态、上下文切换、线程安全(JAVA并发第二期)

p.s.这是萌新自己自学总结的笔记,如果想学习得更透彻的话还是请去看大佬的讲解

目录

两阶段终止模式

在线程T1中杀死线程T2,但要给T2释放共享资源的机会

守护线程

默认情况下,java进程需要等待所有线程都运行结束才会结束,但是守护线程是一种特殊线程,只要其他非守护线程运行结束了,即使守护线程的代码没有执行完,也会强制结束

java 复制代码
        Thread t1 = new Thread(){
            @Override
            public void run(){
                System.out.println("运行...");
                try {
                    sleep(3000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("结束...");
            }
        };
        t1.setDaemon(true);
        t1.start();

        sleep(1000);
        System.out.println("主线程结束");

线程状态


上下文切换

问题:两个线程对同一个变量进行自增自减5000次,结果可能为正为负为零都有可能;因为java中对静态变量的自增自减并不是原子操作

因此就会产生这种效果:

类似的也会出现正数的情况

临界区

一个程序运行多个线程本身是没有问题的,问题在于出现多个线程访问共享资源 时,如果多个线程对共享资源的读写操作发生指令交错时,就会出现问题;

一段代码块内如果存在对共享资源的多线程读写操作,称这段代码块为临界区

而多个线程在临界区内执行,由于代码的执行序列不同而导致结果无法预测,称之为发生了竞态条件

synchronized

为了避免临界区的竞争条件发生,有多种手段可以达到目的。

•阻塞式的解决方案:synchronized,Lock

•非阻塞式的解决方案:原子变量

synchronized,即俗称的【对象锁】,它采用互斥的方式让同一时刻至多只有一个线程能持有【对象锁】,其它线程再想获取这个【对象锁】时就会阻塞住。这样就能保证拥有锁的线程可以安全的执行临界区内的代码,不用担心线程上下文切换

注意

虽然java中互斥和同步都可以采用synchronized关键字来完成,但它们还是有区别的:

•互斥是保证临界区的竞争条件发生,同一时刻只能有一个线程执行临界区代码

•同步是由于线程执行的先后、顺序不同、需要一个线程等待其它线程运行到某个点

语法:

java 复制代码
synchronized(对象){
临界区
}

synchronized加在方法上

java 复制代码
class Test1{
    public synchronized  void test(){

    }
    //等价于
    public void test1(){
        synchronized (this){

        }
    }

    public synchronized static void test2(){

    }
    //等价于
    public void test3(){
        synchronized (Test1.class){

        }
    }
}

线程安全

线程八锁

八种场景

两个普通同步方法,一个对象:线程会等待,因为锁的是同一个对象

两个普通同步方法,两个对象:不会等待,因为锁的是不同对象

一个普通同步方法,一个普通方法:不会等待,普通方法不需要锁

两个静态同步方法,一个对象:会等待,因为静态方法锁的是Class对象

两个静态同步方法,两个对象:会等待,因为静态方法锁的是Class对象

一个静态同步方法,一个普通同步方法:不会等待,因为锁的是不同对象(Class对象 vs 实例对象)

一个静态同步方法,一个普通方法:不会等待

一个普通同步方法,一个静态同步方法,两个对象:不会等待

两个普通同步方法,一个对象:线程会等待,因为锁的是同一个对象

java 复制代码
public class Main {
    public static void main(String[] args) {
        Test t = new Test();
        new Thread(()->{
            System.out.println("t1 begin");
            t.a();
        }).start();
        new Thread(()->{
            System.out.println("t2 begin");
            t.b();
        }).start();
    }
}


class Test{
    public synchronized void a(){
        System.out.println("111");
    }
    public synchronized void b(){
        System.out.println("2222");
    }
}

两个普通同步方法,两个对象:不会等待,因为锁的是不同对象

java 复制代码
public class Main {
    public static void main(String[] args) {
        Test t1 = new Test();
        Test t2 = new Test(); //这里<---------------------------------
        new Thread(()->{
            System.out.println("t1 begin");
            t1.a();
        }).start();
        new Thread(()->{
            System.out.println("t2 begin");
            t2.b();
        }).start();
    }
}


class Test{
    public synchronized void a(){
        System.out.print("1");
    }
    public synchronized void b(){
        System.out.print("2");
    }
}

一个普通同步方法,一个普通方法:不会等待,普通方法不需要锁

java 复制代码
public class Main {
    public static void main(String[] args) {
        Test t = new Test();
        new Thread(()->{
            System.out.println("t1 begin");
            t.a();
        }).start();
        new Thread(()->{
            System.out.println("t2 begin");
            t.b();
        }).start();
    }
}


class Test{
    public synchronized void a(){
        System.out.print("1");
    }
    public  void b(){//这里<---------------------------------
        System.out.print("2");
    }
}

两个静态同步方法,一个对象:会等待,因为静态方法锁的是Class对象

java 复制代码
public class Main {
    public static void main(String[] args) {
        new Thread(()->{
            System.out.println("t1 begin");
            Test.a();
        }).start();
        new Thread(()->{
            System.out.println("t2 begin");
            Test.b();
        }).start();
    }
}


class Test{
    public synchronized static void a(){//这里<---------------------------------
        System.out.print("1");
    }
    public synchronized static void b(){//这里<---------------------------------
        System.out.print("2");
    }
}

剩下几种就以此类推了

变量的线程安全性

成员变量和静态变量是否线程安全?

如果它们没有共享,则线程安全

如果它们被共享了,根据它们的状态是否能够改变,又分两种情况

如果只有读操作,则线程安全

如果有读写操作,则这段代码是临界区,需要考虑线程安全
局部变量是否线程安全?

局部变量是线程安全的

但局部变量引用的对象则未必

如果该对象没有逃离方法的作用访问,它是线程安全的

如果该对象逃离方法的作用范围,需要考虑线程安全

线程安全的类

常见线程安全类

String、Integer、StringBuffer、Random、Vector、Hashtable

java.util.concurrent 包下的类

这里说它们是线程安全的是指,多个线程调用它们同一个实例的某个方法时,是线程安全的。也可以理解为

它们的每个方法是原子的,但注意它们多个方法的组合不是原子的
String、Integer等都是不可变类,因为其内部的状态不可以改变,因此它们的方法都是线程安全的

replace,substring等方法是创建了一个新的字符串

相关推荐
寻星探路12 小时前
【深度长文】万字攻克网络原理:从 HTTP 报文解构到 HTTPS 终极加密逻辑
java·开发语言·网络·python·http·ai·https
曹牧14 小时前
Spring Boot:如何测试Java Controller中的POST请求?
java·开发语言
爬山算法15 小时前
Hibernate(90)如何在故障注入测试中使用Hibernate?
java·后端·hibernate
kfyty72515 小时前
集成 spring-ai 2.x 实践中遇到的一些问题及解决方案
java·人工智能·spring-ai
猫头虎15 小时前
如何排查并解决项目启动时报错Error encountered while processing: java.io.IOException: closed 的问题
java·开发语言·jvm·spring boot·python·开源·maven
李少兄15 小时前
在 IntelliJ IDEA 中修改 Git 远程仓库地址
java·git·intellij-idea
忆~遂愿15 小时前
ops-cv 算子库深度解析:面向视觉任务的硬件优化与数据布局(NCHW/NHWC)策略
java·大数据·linux·人工智能
小韩学长yyds15 小时前
Java序列化避坑指南:明确这4种场景,再也不盲目实现Serializable
java·序列化
仟濹15 小时前
【Java基础】多态 | 打卡day2
java·开发语言
Re.不晚15 小时前
JAVA进阶之路——无奖问答挑战2
java·开发语言