设计模式、线程状态、上下文切换、线程安全(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等方法是创建了一个新的字符串

相关推荐
雨中飘荡的记忆1 天前
ElasticJob分布式调度从入门到实战
java·后端
考虑考虑1 天前
JDK25模块导入声明
java·后端·java ee
_小马快跑_2 天前
Java 的 8 大基本数据类型:为何是不可或缺的设计?
java
Re_zero2 天前
线上日志被清空?这段仅10行的 IO 代码里竟然藏着3个毒瘤
java·后端
洋洋技术笔记2 天前
Spring Boot条件注解详解
java·spring boot
程序员清风2 天前
程序员兼职必看:靠谱软件外包平台挑选指南与避坑清单!
java·后端·面试
皮皮林5512 天前
利用闲置 Mac 从零部署 OpenClaw 教程 !
java
华仔啊3 天前
挖到了 1 个 Java 小特性:var,用完就回不去了
java·后端
SimonKing3 天前
SpringBoot整合秘笈:让Mybatis用上Calcite,实现统一SQL查询
java·后端·程序员
日月云棠3 天前
各版本JDK对比:JDK 25 特性详解
java