同步代码块解决线程安全
使用同步代码块解决线程安全,使用静态变量作为全局共享变量
//设场景,3个人在分发100包标有号码的小礼品,分完停止 代码如下:
package com.ruoyi.web.controller.thread;
import org.apache.poi.ss.formula.functions.T;
/**
* @author zhuang.bq
* @create 2024/5/16 14:53
* @desc
**/
public class MyThread6 extends Thread {
//标识这个类所有的对象 都共享该参数
static int num = 0;// 0-99
public MyThread6() {
}
public MyThread6(String name) {
super(name);
}
@Override
public void run() {
while (true) {
if (num < 100) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
num++;
System.out.println(getName() + "正在分发" + num + "包");
} else {
break;
}
}
}
/**
* 设场景,3个人在分发标有号码的100包小礼品,分完停止
* @param args
*/
public static void main(String[] args){
MyThread6 t1 = new MyThread6("员工1-");
MyThread6 t2 = new MyThread6("员工2-");
MyThread6 t3 = new MyThread6("员工3-");
t1.start();
t2.start();
t3.start();
}
}
执行结果如下:会发现线程不安全导致不同员工(线程)分发了同一个号码的礼品,即相同的号码数出现了多次,且超出了范围号码
使用同步代码块 可以解决多线程抢占同一个资源的问题 即优化后的代码 但是同步代码块只能锁住共享对象,但是不能控制线程的执行顺序
package com.ruoyi.web.controller.thread;
/**
* @author zhuang.bq
* @create 2024/5/16 14:53
* @desc
**/
public class MyThread6 extends Thread {
//标识这个类所有的对象 都共享该参数 之所以加上static 是因为我们会多次创建MyThread类 所以需要加上static标识,往下 我们用Runable就可以不用了。
static int num = 0;// 0-99
static Object object = new Object();//锁对象一定要是唯一的 锁不是唯一 会一个线程一个锁 就没有意义了
public MyThread6() {
}
public MyThread6(String name) {
super(name);
}
@Override
public void run() {
while (true) {
//同步代码块
synchronized (object) { //不能让synchronized同步代码块锁在while外面,否则会变成只能让一个线程来分发
// synchronized (MyThread6.class) { //也可以用当前类的字节码锁住
//特点1:锁默认打开,有一个线程进去了,锁自动关闭
//特点2:里面的代码全部执行完毕,线程出来,锁自动打开
if (num < 100) {
try {
Thread.sleep(10); //线程执行有随机性 cpu被抢走 导致数据被抢占
} catch (InterruptedException e) {
e.printStackTrace();
}
num++;
System.out.println(getName() + "正在分发" + num + "张");
} else {
break;
}
}
}
}
/**
* 设场景,3个人分发100张粮票,分完停止
*
* @param args
*/
public static void main(String[] args) {
MyThread6 t1 = new MyThread6("员工1-");
MyThread6 t2 = new MyThread6("员工2-");
MyThread6 t3 = new MyThread6("员工3-");
t1.start();
t2.start();
t3.start();
}
}
--使用lock锁 手动处理优化上面问题
同步方法解决线程安全
特点1:同步方法是锁住方法里面所有的代码
特点2:所对象不能自己指定
非静态:this 静态:当前类的字节码文件对象
package com.ruoyi.web.controller.thread;
/**
* 实现runnable接口方式实现
*
* @author zhuang.bq
* @create 2024/5/13 15:57
* @desc
**/
public class MyRun3 implements Runnable {
int num = 0;//与继承Thread不同的是,实现Runnable无需多次创建MyRun类 创建一个对象之后 我们实例化的时候 num值即为全局变量 无需加上static
@Override
public void run() {
//1.循环
//2.同步代码块 然后放入同步方法中
//3.判断共享数据未到末尾
//4.判断共享数据到末尾了
while (true) {
if (deal()) {
break;
}
;
}
}
//this
private synchronized boolean deal() {
if (num == 100) {
return true;
} else {
try {
Thread.sleep(10); //防止一个线程全部执行完
} catch (InterruptedException e) {
e.printStackTrace();
}
num++;
System.out.println(Thread.currentThread().getName() + "_在分发第" + num + "张粮票!");
}
return false;
}
/**
* 设场景,3个人分发100张粮票,分完停止
*
* @param args
*/
public static void main(String[] args) {
MyRun3 myRun3 = new MyRun3();//创建run对象 表示需要执行的任务 myRun3 == this
Thread t1 = new Thread(myRun3);//创建线程对象,并将自定义线程丢进去
Thread t2 = new Thread(myRun3);
Thread t3 = new Thread(myRun3);
t1.setName("员工1:");
t2.setName("员工2");
t3.setName("员工3");
t1.start();//开启线程
t2.start();
t3.start();
}
}
ps:以上代码是通synchronized 自动锁,在进入synchronized之后 自动加锁,处理完之后 自动解锁
使用lock锁 手动锁定 解决Thread继承超额分配同时分配问题
package com.ruoyi.web.controller.thread;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author zhuang.bq
* @create 2024/5/16 14:53
* @desc
**/
public class MyThread7 extends Thread {
//标识这个类所有的对象 都共享该参数
static int num = 0;// 0-99
static Object object = new Object();//锁对象一定要是唯一的 锁不是唯一 会一个线程一个锁 就没有意义了
static Lock lock = new ReentrantLock(); //防止多次创建 生成多把锁
public MyThread7() {
}
public MyThread7(String name) {
super(name);
}
@Override
public void run() {
try {
while (true) {
lock.lock();
// synchronized (object) {
if (num < 100) {
Thread.sleep(10); //线程执行有随机性 cpu被抢走 导致数据被抢占
num++;
System.out.println(getName() + "正在分发" + num + "张");
} else {
break;
}
// }
// lock.unlock();//如果放在while里面 会使得其他线程无法获得锁
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
/**
* 设场景,3个人在分发100张粮票,分完停止
*
* @param args
*/
public static void main(String[] args) {
MyThread7 t1 = new MyThread7("员工1-");
MyThread7 t2 = new MyThread7("员工2-");
MyThread7 t3 = new MyThread7("员工3-");
t1.start();
t2.start();
t3.start();
}
}
自己使用锁 可能会发生死锁
·多个线程同时抢到同一把锁 不释放资源的情况下 会出现死锁
下面就是错误示范 尽量别让两个锁嵌套即可
package com.ruoyi.web.controller.thread;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author zhuang.bq
* @create 2024/5/16 14:53
* @desc
**/
public class MyThread8 extends Thread {
static Object objA = new Object();
static Object objB = new Object();
public MyThread8() {
}
public MyThread8(String name) {
super(name);
}
@Override
public void run() {
while (true){
if("线程A".equals(getName())){
synchronized (objA){
System.out.println("线程A拿到A锁,准备拿B锁!");
synchronized (objB){
System.out.println("线程A拿到B锁,顺利执行完成一轮!");
}
}
} else if("线程B".equals(getName())){
synchronized (objB){
System.out.println("线程B拿到B锁,准备拿A锁!");
synchronized (objA){
System.out.println("线程B拿到A锁,顺利执行完成一轮!");
}
}
}
}
}
/**
* 设场景,3个人在分发100张粮票,分完停止
*
* @param args
*/
public static void main(String[] args) {
MyThread8 t1 = new MyThread8("线程A");
MyThread8 t2 = new MyThread8("线程B");
t1.start();
t2.start();
}
}