多线程-线程状态和线程安全(加锁-synchronized 关键字)

目录

1.线程状态

 示例:

1.1线程状态和状态转移的意义

2.线程安全

2.1观察线程不安全

2.2线程不安全的原因

3.synchronized 关键字 - 监视器锁 monitor lock

3.1synchronized 的特性

1. 互斥

2.可重⼊

 应用示例:

3.2synchronized 使⽤⽰例  

1. 修饰代码块: 明确指定锁哪个对象.

2.直接修饰普通⽅法: 锁的 SynchronizedDemo 对象

3.修饰静态⽅法: 锁的 SynchronizedDemo 类的对象


1.线程状态

      在Java中,线程有几种不同的状态,可以通过Thread类的getState()方法获取线程的当前状态。

      线程的状态是⼀个枚举类型 Thread.State
public class ThreadState {
 public static void main(String[] args) {
 for (Thread.State state : Thread.State.values()) {
 System.out.println(state);
         }
     }
}
  1. NEW(新建):新创建的线程尚未启动。
  2. RUNNABLE(可运行):正在Java虚拟机中执行的线程,可能正在执行,也可能正在等待CPU时间片。
  3. BLOCKED(阻塞):被阻塞并等待监视器锁定的线程。当线程试图进入一个同步代码块,而该块已经被其他线程持有时,该线程将进入阻塞状态。
  4. WAITING(等待):无限期等待另一个线程执行特定操作的线程。线程可以通过调用Object类的wait()方法、Thread类的join()方法或LockSupport类的park()方法进入等待状态。
  5. TIMED_WAITING(计时等待):在等待一段时间后自动恢复运行的线程。线程可以通过调用Thread类的sleep()方法、Object类的wait方法、Thread类的join方法进入计时等待状态。
  6. TERMINATED(终止):已经执行完毕的线程,不再运行。

 示例:

我们用getState()来获取线程的状态。

package 多线程;

public class ThreadDemo13 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            for (int i = 0; i < 5; i++) {
                System.out.println("线程运行中");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        //启动之前状态是new状态
        System.out.println(t.getState());
        t.start();
        System.out.println(t.getState());
        t.join();
        System.out.println(t.getState());
        System.out.println("t线程结束");
    }
}

       我们查看结果一开始线程的状态是NEW,当我们t.start后线程的状态变成了RUNNABLE,之后线程开始运行。我们使用t.join()提前结束线程。线程状态改变成了TERMINTED。

 多线程-线程状态和线程安全(加锁-synchronized 关键字)_第1张图片

1.1线程状态和状态转移的意义

多线程-线程状态和线程安全(加锁-synchronized 关键字)_第2张图片

 

2.线程安全

        线程安全是指在多线程环境下,多个线程同时访问共享资源时,不会出现数据不一致、竞态条件和死锁等问题。在并发编程中,如果多个线程同时访问共享的可变数据,可能会导致数据不一致的情况。例如,一个线程在读取一个共享变量的同时,另一个线程正在修改该变量,这就可能导致读取到的数据是脏数据(脏数据是指在并发环境下,一个线程正在修改某个共享变量的同时,另一个线程正在读取同一个变量的值,从而导致读取到的值不正确或者不符合预期。这种情况也被称为“读写冲突”。)或者不符合预期的结果。为了保证线程安全,需要采取相应的措施来避免这类问题。

2.1观察线程不安全

我们写一个程序来观察

// 此处定义⼀个 int 类型的变量
private static int count = 0;
public static void main(String[] args) throws InterruptedException {
 Thread t1 = new Thread(() -> {
// 对 count 变量进⾏⾃增 5w 次
 for (int i = 0; i < 50000; i++) {
 count++;
 }
 });
 Thread t2 = new Thread(() -> {
 // 对 count 变量进⾏⾃增 5w 次
 for (int i = 0; i < 50000; i++) {
 count++;
 }
 });
 t1.start();
 t2.start();
 // 如果没有这俩 join, 肯定不⾏的. 线程还没⾃增完, 就开始打印了. 很可能打印出来的 cou
 t1.join();
 t2.join();
 // 预期结果应该是 10w
 System.out.println("count: " + count);
}

       我们运行结果会发现每次运行的结果都是不一样的,这就是因为线程不安全所以我们的结果不正确。上⾯的线程不安全的代码中, 涉及到多个线程针对 count 变量进⾏修改. 此时这个 count 是⼀个多个线程都能访问到的 "共享数据"

2.2线程不安全的原因

1.根本原因:操作系统上的线程是“抢占式执行”“随机调度”=>线程之间执行的顺序带来了很多变数

2.代码结构:代码中多个线程,同时修改同一个变量

一个线程修改一个变量,没事

多个线程读取同一个变量,没事

多个线程修改不同变量,没事

3.直接原因:上述多线程修改操作,本身不是“原子的”(原子性是指一个操作是不可中断的,在执行过程中不能被其他线程或事件打断,要么全部执行成功,要么全部不执行。如果一个操作具有原子性,那么多个线程同时执行这个操作时,不会出现数据不一致的问题。)

3.synchronized 关键字 - 监视器锁 monitor lock

         synchronized是Java中用于实现同步的关键字,可以将代码块或方法声明为同步代码块或同步方法。在多线程环境下,使用synchronized可以确保同一时间只有一个线程能够访问共享资源,从而避免数据不一致的问题。

3.1synchronized 的特性

1. 互斥

        synchronized 会起到互斥效果, 某个线程执⾏到某个对象的 synchronized 中时, 其他线程如果也执⾏到同⼀个对象 synchronized 就会阻塞等待.
进⼊ synchronized 修饰的代码块, 相当于 加锁
退出 synchronized 修饰的代码块, 相当于 解锁

2.可重⼊

         synchronized 同步块对同⼀条线程来说是可重⼊的,不会出现⾃⼰把⾃⼰锁死的问题;

 应用示例:

我们对count进行加锁

package 多线程;

public class ThreadDemo14 {
    private static int count = 0;

    public static void main(String[] args) throws InterruptedException {
        //创建一个对象
        Object locker = new Object();

        Thread t1 = new Thread(()->{
            for (int i = 0; i < 5000; i++) {
                synchronized (locker) {//进程如{}就会加锁
                    count++;
                }//出了{}就会解锁
            }
        });
        Thread t2 = new Thread(()->{
            for (int i = 0; i < 5000; i++) {
                synchronized (locker) {
                    count++;
                }
            }
        });
        t1.start();
        t2.start();
        // 如果没有这俩 join, 肯定不⾏的. 线程还没⾃增完, 就开始打印了. 
        t1.join();
        t2.join();
        // 预期结果应该是 10w
        System.out.println("count: " + count);
    }

 这样我们的结果就正确了。

多线程-线程状态和线程安全(加锁-synchronized 关键字)_第3张图片

3.2synchronized 使⽤⽰例  

1. 修饰代码块: 明确指定锁哪个对象.

锁任意对象
public class SynchronizedDemo {
 private Object locker = new Object();
 
 public void method() {
 synchronized (locker) {
 
      }
   }
}
锁当前对象
public class SynchronizedDemo {
 public void method() {
 synchronized (this) {
 
      }
   }
}

2.直接修饰普通⽅法: 锁的 SynchronizedDemo 对象

public class SynchronizedDemo {
 public synchronized void methond() {
     }
}

3.修饰静态⽅法: 锁的 SynchronizedDemo 类的对象

public class SynchronizedDemo {
 public synchronized static void method() {
    }
}

                                                                   希望大家多多支持!

你可能感兴趣的:(java,java,开发语言)