官渡网站设计制作,软件外包公司绩效考核内容,做海报哪个网站的素材多,网站怎么上传源码目录
前言
一.线程控制方法
1.1启动线程--start()
1.2线程睡眠---sleep()方法
1.3中断线程--interrupt() 方法
1.4等待线程---join()
二.线程安全
2.1数据不安全---数据共享
⭐不安全的演示和原因 ⭐不安全的处理方法
⭐synchronized的使用
2.2数据不安全---内存可…目录
前言
一.线程控制方法
1.1启动线程--start()
1.2线程睡眠---sleep()方法
1.3中断线程--interrupt() 方法
1.4等待线程---join()
二.线程安全
2.1数据不安全---数据共享
⭐不安全的演示和原因 ⭐不安全的处理方法
⭐synchronized的使用
2.2数据不安全---内存可见性
⭐不安全的演示和原因
⭐不安全的处理方法
2.3 synchronized和volatile的区别
三.认识wait()、notify()
3.1wait()方法
3.2notify()方法
3.3wait()方法和sleep()方法的对比
四.总结 个人主页tq02的博客_CSDN博客-C语言,Java,Java数据结构领域博主 本文由 tq02 原创首发于 CSDN 本章讲解内容多线程的控制、安全讲解 学习专栏 C语言 JavaSE MySQL基础 前言 JavaEE的多线程知识点我们在 多线程(1) 中学习到了如何创建多线程、多线程的运行状态等而本章主要讲解多线程的线程控制手段以及线程安全。 一.线程控制方法 线程控制就是控制线程的执行速率让某某线程先执行等。 根据生命周期我们可以知道线程控制方法大概有start()、sleep()、interrupt()、join()、wait()、yield()、notify()、
1.1启动线程--start()
class MyThread extends Thread {Overridepublic void run() {System.out.println(这里是线程运行的代码);}
}public class Text{public static void main(String[] args) {//创建MyThread实例MyThread t1new MyThread();//调用start方法启动线程t1.start();//t1.run(); 虽然也是执行run函数当中的操作但是并不是启动线程而是调用方法}
} 通过覆写 run 方法创建一个线程对象但线程对象被创建出来并不意味着线程就开始运行了。并且不是指调用run方法就可以使用线程运行而是单纯的执行方法run并不能达到并行的机制只有使用start()方法才可以使线程运行
1.2线程睡眠---sleep()方法 线程会暂停运行并且可以设置暂停运行的时间例如sleep(1000),是休眠了1秒。
注是暂停运行而不是停止运行而时间的设置长度更像是闹钟时间一到就开始运行。 1.3中断线程--interrupt() 方法 线程一旦开始运行想让线程强制停止目前有2种方法。 通过共享的标记来进行沟通调用 interrupt() 方法来通知 1.通过共享的标记来进行沟通 使用自定义的变量来作为标志位.并且给标志位上并且final修饰后期会讲解volatile 关键字使用它也可以执行操作。
public class Text{// 写作成员变量就不是触发变量捕获的逻辑了. 而是 内部类访问外部类的成员 , //本身就是 ok 的~~public static boolean isQuit false;public static void main(String[] args) throws InterruptedException {//或者 final boolean isQuit falseThread t new Thread(() - {while (!isQuit) {System.out.println(hello thread);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});t.start();// 主线程这里执行一些其他操作之后, 要让 t 线程结束.Thread.sleep(3000);// 这个代码就是在修改前面设定的标志位.isQuit true;System.out.println(把 t 线程终止);}
} 从上面的代码当中我们可以发现isQuit变量用于lambda表达式因此会有一个变量捕获的环节而在变量捕获当中捕获到的外部变量isquit不可以修改因此需要使用final修饰主线程执行完毕时将isQuit修改为true;导致子线程强行结束。 2.调用 interrupt() 方法来通知 Thread.interrupted() 或者 Thread.currentThread().isInterrupted() 代替自定义标志位. Thread.currentThread():获取当前对象的引用isInterrupted()方法是指提供一个标志位。 Thread.interrupted():将标志位改为true public static void main(String[] args) throws InterruptedException {Thread t new Thread(() - {// Thread.currentThread 就是 t .// 但是 lambda 表达式是在构造 t 之前就定义好的. 编译器看到的 lambda 里的 t 就会认为这是一个还没初始化的对象.因此wile的括号里不可以使用t.while (!Thread.currentThread().isInterrupted()) {System.out.println(hello thread);try {Thread.sleep(1000);} catch (InterruptedException e) {// e.printStackTrace();break;}}});t.start();Thread.sleep(3000);// 把上述的标志位给设置成 truet.interrupt();}
当线程正在sleep()休眠当中如果使用interrupt()唤醒则会唤醒继续运行
1.4等待线程---join() 等待线程是指两个线程进行时一个线程等待另一个线程执行结束才可以执行结束。 public static void main(String[] args) {Thread b new Thread(() - {for (int i 0; i 5; i) {System.out.println(张三工作);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(张三工作结束了);});Thread a new Thread(() - {for (int i 0; i 3; i) {System.out.println(李四工作);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}try {// 如果 b 此时还没执行完毕, b.join 就会产生阻塞的情况b.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(李四工作结束了);});b.start();a.start();} 例如原本李四工作比张三工作要快可是我使用了join()方法然后让李四等待张三结束才能结束。方法还可以使用其他参数用于不同意思。
方法作用join()等待线程结束jion(long millis)等待线程结束或者等待一定时间自动结束 而除了这些操作方法之外我们还有wait()、notify()方法但是需要先学习线程安全才能更好的学习。
二.线程安全 线程安全指多线程环境下代码运行的结果是符合我们预期的即在单线程环境应该的结果则说这个程序是线程安全的。
线程不安全出现的根本原因
线程是抢占式执行多个线程同时对共享数据进行操作产生数据覆盖内存可见性指令重排序 2.1数据不安全---数据共享
⭐不安全的演示和原因
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个线程都执行50000次的count按理而言count最后的结果是100000次可是执行之后却不足100000次。数据会存放在堆区多个线程都可以访问。
出现的原因
count实际上操作
内存的数据加载到CPU寄存器中寄存器中的数据执行返回给内存。 由于count在堆上会被多个线程共享访问 t1和t2线程同时获取count元素进行自增然后将结果返回内存器当中t1返回2t2页返回2将t1返回的值覆盖因此少了一次自增 ⭐不安全的处理方法 线程不安全的原因主要是多个线程会共享访问数据导致数据的原子性被破坏因此我们可以使用一种方法让其他线程知晓该数据正在被其他线程使用(可见性)。
方法加锁
当一个线程在执行操作时对需要操作的数据或者方法进行加锁这样其他线程使用不到了。 例如需要操作的方法或者数据是一个房间线程一进去时将门锁了只有线程一操作结束出来时线程二才能进去
使用关键字synchronized加锁操作
class Counter {public int count 0;synchronize 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);
}
将increase()方法进行加锁当一个线程进入时就会进行加锁其他线程只能等待该线程释放。 ⭐synchronized的使用 synchronized关键字进行加锁时是需要对相同的对象展开。
加锁的目的为了互斥使用资源而加锁的对象也是需要相同的两个线程对同一个对象进行加锁时会产生锁竞争/锁冲突。
class Counter {public int count 0;void increase() {synchronized(this){count;}}
}
this是目前对象而我们也可以使用其他对象。 2.2数据不安全---内存可见性
⭐不安全的演示和原因
static class Counter {public int flag 0;
}public static void main(String[] args) {Counter counter new Counter();Thread t1 new Thread(() - {while (counter.flag 0) {//代码操作}System.out.println(循环结束!);});Thread t2 new Thread(() - {Scanner scanner new Scanner(System.in);System.out.println(输入一个整数:);counter.flag scanner.nextInt();});t1.start();t2.start();
} 在这个代码中我们只需要输入非零数按逻辑而言线程t1也应该会结束可事实上t1不会结束原因t1读取自己工作内存中的内容而t2对flag进行修改t1却无法得知他的修改。
问题产生的原因
counter.flag0实际上的指令load(读指令jcmp(比较指令)编译器发现这个这个逻辑是一样的每一次都是需要读取指令结果却是一样的因此编译器会将逻辑优化掉直接进行jcmp指令。因此即使flag发生了改变t1也无法得知。 ⭐不安全的处理方法 使用volatile关键字使用volatile关键字修饰变量之后编译器就会禁止优化。
static class Counter {public volatile int flag 0;
}public static void main(String[] args) {Counter counter new Counter();Thread t1 new Thread(() - {while (counter.flag 0) {//代码操作}System.out.println(循环结束!);});Thread t2 new Thread(() - {Scanner scanner new Scanner(System.in);System.out.println(输入一个整数:);counter.flag scanner.nextInt();});t1.start();t2.start();
}
本质上保证修饰的变量的内存可见性禁止编译器的优化 2.3 synchronized和volatile的区别
volatile不保证原子性只保证了内存可见性synchronized保证了原子性、也保证了内存可见性 三.认识wait()、notify() 由于线程之间是抢占式执行的因此线程之间执行的先后顺序难以预知。但是实际开发中我们希望可以协调控制多个线程的执行先后顺序。
而涉及到协调控制方法
wait() / wait(long timeout)让线程进入等待状态notify() / notifyAll(): 唤醒在当前对象上等待的线程
3.1wait()方法 线程进入等待状态如果没有唤醒则会一直一直等待并且需要搭配synchronized 来使用. 脱离 synchronized 使用 wait 会直接抛出异常. wait()结束等待的条件 其他线程调用该对象的 notify 方法.wait 等待时间超时 (wait 方法提供一个带有 timeout 参数的版本, 来指定等待时间).其他线程调用该等待线程的 interrupted 方法, 导致 wait 抛出 InterruptedException 异常. 代码示例
public static void main(String[] args) throws InterruptedException {Object object new Object();synchronized (object) {System.out.println(等待中);object.wait();System.out.println(等待结束);}
}
3.2notify()方法 用于唤醒wait()进入等待的线程。在notify()方法后当前线程不会马上释放该对象锁要等到执行notify()方法的线程将程序执行完也就是退出同步代码块之后才会释放对象锁。notifyAll()方法:则是唤醒所有等待的线程。注虽然是同时唤醒但是需要竞争锁因此并不是同时执行依然有先来后到的执行 3.3wait()方法和sleep()方法的对比 理论上 wait 和 sleep 完全是没有可比性的因为一个是用于线程之间的通信的一个是让线程阻塞一段时间唯一的相同点就是都可以让线程放弃执行一段时间 wait 需要搭配 synchronized 使用. sleep 不需要. wait 是 Object 的方法 sleep 是 Thread 的静态方法. 四.总结 volatile 能够保证内存可见性. 强制从主内存中读取数据synchronize既可以保证原子性也可以保证内存可见性。
Java多线程实现数据共享的原理 JVM 把内存分成了这几个区域: 方法区, 堆区, 栈区, 程序计数器. 其中堆区这个内存区域是多个线程之间共享的. 只要把某个数据放到堆内存中, 就可以让多个线程都能访问到