0
点赞
收藏
分享

微信扫一扫

【JavaEE网络】TCP套接字编程详解:从概念到实现

梦为马 2024-04-28 阅读 6

1.线程的状态

1.1 观察线程的所有状态

线程的状态是一个枚举类型 Thread.State

public class ThreadState {
    public static void main(String[] args) {
        for (Thread.State state : Thread.State.values()) {
            System.out.println(state);
       }
   }
}

 

2. 多线程带来的的风险-线程安全 (重点)

2.1 观察线程不安全

static class Counter {
    public int count = 0;
    void increase() {
        count++;
   }
}
public static void main(String[] args) throws InterruptedException {
    final Counter counter = new Counter();
    Thread t1 = new Thread(() -> {
        for (int i = 0; i < 50000; i++) {
            counter.increase();
       }
   });
    Thread t2 = new Thread(() -> {
        for (int i = 0; i < 50000; i++) {
            counter.increase();
       }
   });
    t1.start();
    t2.start();
    t1.join();
    t2.join();
    System.out.println(counter.count);
}

2.2 线程安全的概念

如果多线程环境下代码运行的结果是符合我们预期的,即在单线程环境应该的结果,则说这个程序是线程安全的。 

2.3 线程不安全的原因

1.线程的随机调度

操作系统中, 线程的调度顺序是随机的 (抢占式执行).罪魁祸首,万恶之源,

如果是多个线程执行上述代码,由于线程之间的调度顺序,是“随机"的,就会导致在有些调度顺序下,上述的逻辑就会出现问题

在多线程程序中,最困难的一点:线程的随机调度,使两个线程执行逻辑的先后顺序,存在诸多可能,我们必须要保证在所有可能的情况下,代码都是正确的!

以下是正确的执行顺序

不是按照此顺序,最终结果一定有bug,且最终值小于10w。 

2.两个线程,针对同一个变量进行修改

1)一个线程针对一个变量修改.ok

2)两个线程针对不同变量修改.ok

3)两个线程针对一个变量读取.ok 

3.修改操作,不是原子的.

此处给定的 count++ 就属于是 非原子 的操作.(先读,再修改)类似的,如果一段逻辑中,需要根据一定的条件来决定是否修改,也是存在类似的问题
假设 count++ 是原子的(比如有一个 cpu 指令,一次完成上述的三步)

4.内存可见性问题.

5.指令重排序

要想解决线程安全问题,就是要从上述方面入手。

1.系统内核里实现的->最初搞多任务操作系统的人,制定了"抢占式执行大的基调.在这个基调下,想做出调整是非常困难的。

2.有些情况下,可以通过调整代码结构,规避上述问题但是也有很多情况,调整不了。

3.通过加锁!!!

通过加锁, 就能解决上述问题.
如何给 java 中的代码加锁呢?
其中最常用的办法, 就是使用 synchronized 关键字!

// 线程安全
public class Demo13 {
    // 此处定义一个 int 类型的变量
    private static int count = 0;

    public static void main(String[] args) throws InterruptedException {
        Object locker = new Object();
        Object locker2 = new Object();

        Thread t1 = new Thread(() -> {
            // 对 count 变量进行自增 5w 次
            for (int i = 0; i < 50000; i++) {
                synchronized (locker) {
                    count++;
                }
            }
        });
        Thread t2 = new Thread(() -> {
            // 对 count 变量进行自增 5w 次
            for (int i = 0; i < 50000; i++) {
                synchronized (locker) {
                    count++;
                }
            }
        });

        t1.start();
        t2.start();

        // 如果没有这俩 join, 肯定不行的. 线程还没自增完, 就开始打印了. 很可能打印出来的 count 就是个 0
        t1.join();
        t2.join();

        // 预期结果应该是 10w
        System.out.println("count: " + count);
    }
}
class Counter {
    public int count;

    synchronized public void increase() {
        count++;
    }

    public void increase2() {
        synchronized (this) {
            count++;
        }
    }

    synchronized public static void increase3() {

    }

    public static void increase4() {
        synchronized (Counter.class) {

        }
    }
}

// synchronized 使用方法
public class Demo14 {
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                counter.increase();
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                counter.increase();
            }
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();

        System.out.println(counter.count);
    }
}

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

3.1 synchronized 的特性

4.volatile 关键字  

1)volatile 能保证内存可见性

volatile 修饰的变量 , 能够保证 " 内存可见性 ".

public class Demo17 {
    private static  int isQuit = 0;

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            while (isQuit == 0) {
                // 循环体里啥都没干.
                // 此时意味着这个循环, 一秒钟就会执行很多很多次.
            }
            System.out.println("t1 退出!");
        });
        t1.start();

        Thread t2 = new Thread(() -> {
            System.out.println("请输入 isQuit: ");
            Scanner scanner = new Scanner(System.in);
            // 一旦用户输入的值, 不为 0, 此时就会使 t1 线程执行结束.
            isQuit = scanner.nextInt();
        });
        t2.start();
    }
}

2) volatile 不保证原子性

volatile 和 synchronized 有着本质的区别. synchronized 能够保证原子性, volatile 保证的是内存可见性. 

3)synchronized 也能保证内存可见性

synchronized 既能保证原子性, 也能保证内存可见性.

5.wait和notify 

多线程中一个比较重要的机制.
协调多个线程的执行顺序的
本身多个线程的执行顺序,是随机的(系统随机调度,抢占式执行的)很多时候,是希望能够通过一定的手段,协调的执行顺序的,join 是影响到线程结束的先后顺序相比之下,此处是希望线程不结束,也能够有先后顺序的控制。

  • wait 等待,让指定线程进入阻塞状态
  • notify 通知,唤醒对应的阻塞状态的线程,
public class Demo18 {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("t1 结束!");
        });

        Thread t2 = new Thread(() -> {
            try {
                t1.join();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("t2 结束!");
        });
        t1.start();
        t2.start();

        System.out.println("主线程结束!");
    }
}
public class Demo19 {
    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();

        synchronized (object) {
            System.out.println("wait 之前");
            // 把 wait 要放到 synchronized 里面来调用. 保证确实是拿到锁了的.
            object.wait();
            System.out.println("wait 之后");
        }
    }
}

 wait和notify

//释放锁的前提,是加锁

//wait 会持续的阻塞等待下去,直到其他线程调用 notify 唤醒,

public class Demo20 {
    public static void main(String[] args) {
        Object object = new Object();

        Thread t1 = new Thread(() -> {
            synchronized (object) {
                System.out.println("wait 之前");
                try {
                    object.wait(3000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("wait 之后");
            }
        });

        Thread t2 = new Thread(() -> {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            synchronized (object) {
                System.out.println("进行通知");
                object.notify();
            }
        });

        t1.start();
        t2.start();
    }
}

5.1 notify和notifyAll

notify->一次唤醒一个线程
notifyAll->一次唤醒全部线程 (唤醒的时候,wait 要涉及到一个重新获取锁的过程也是需要串行执行的)

调用 wait 不一定就只有一个线程调用.

N 个线程都可以调用 wait此时,当有多个线程调用的时候,这些线程都会进入阻塞状态

唤醒的时候,也就有两种方式了

举报

相关推荐

0 条评论