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

相关推荐
历程里程碑4 小时前
二叉树---二叉树的中序遍历
java·大数据·开发语言·elasticsearch·链表·搜索引擎·lua
小信丶4 小时前
Spring Cloud Stream EnableBinding注解详解:定义、应用场景与示例代码
java·spring boot·后端·spring
无限进步_4 小时前
【C++】验证回文字符串:高效算法详解与优化
java·开发语言·c++·git·算法·github·visual studio
亚历克斯神4 小时前
Spring Cloud 2026 架构演进
java·spring·微服务
七夜zippoe4 小时前
Spring Cloud与Dubbo架构哲学对决
java·spring cloud·架构·dubbo·配置中心
海派程序猿4 小时前
Spring Cloud Config拉取配置过慢导致服务启动延迟的优化技巧
java
阿维的博客日记5 小时前
为什么不逃逸代表不需要锁,JIT会直接删掉锁
java
William Dawson5 小时前
CAS的底层实现
java
九英里路5 小时前
cpp容器——string模拟实现
java·前端·数据结构·c++·算法·容器·字符串
YDS8295 小时前
大营销平台 —— 抽奖前置规则过滤
java·spring boot·ddd