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

相关推荐
Swift社区2 小时前
死锁:线程卡死不是偶然,而是设计问题
java·spring·maven
uup2 小时前
防止短信验证码接口被盗刷问题
java
xxxmine2 小时前
ConcurrentHashMap 和 Hashtable 的区别详解
java·开发语言
凛_Lin~~2 小时前
安卓 面试八股文整理(原理与性能篇)
android·java·面试·安卓
weixin_436525072 小时前
NestJS-TypeORM QueryBuilder 常用 SQL 写法
java·数据库·sql
oioihoii2 小时前
C++虚函数表与多重继承内存布局深度剖析
java·jvm·c++
wangchen_02 小时前
深入理解 C/C++ 强制类型转换:从“暴力”到“优雅”
java·开发语言·jvm
Wang15302 小时前
Java三大核心热点专题笔记
java
潲爺3 小时前
《Java 8-21 高频特性实战(上):5 个场景解决 50% 开发问题(附可运行代码)》
java·开发语言·笔记·学习