逐渐成为一个情绪稳定且安静成长的人
------24.5.24
线程安全
什么时候发生?
当多个线程访问同一个资源时,导致了数据有问题,出现并发 问题,数据不能及时更新,导致数据发生错误,出现线程安全问题
多线程安全问题示例
javapackage S71ThreadSafe; public class MyTicket implements Runnable{ // 定义100章票 int ticket = 100; @Override public void run(){ while(true){ if(ticket>0){ System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"张票"); ticket--; } } } }
javapackage S71ThreadSafe; public class Demo210Test { public static void main(String[] args) { // new一次,分别传递到三个线程对象中去 MyTicket myTicket = new MyTicket(); Thread th1= new Thread(myTicket, "赵四"); Thread th2= new Thread(myTicket, "刘能"); Thread th3= new Thread(myTicket, "广坤"); th1.start(); th2.start(); th3.start(); } }
解决线程安全问题方式一:同步代码块
synchronized代码块 == 上锁 可以解决线程安全问题,不保证线程顺序
1.格式:
synchronized(任意接收对象){
线程要执行的语句;
}
2.任意对象:
就是我们的锁对象
3.执行:
一个线程拿到锁之后,会进入到同步代码块中执行,在此期间,其他线程拿不到锁,就进不去同步代码块。需要在同步代码块外面等待排队,需要等待执行的线程执行完毕,出了同步代码块,相当于释放锁了,等待的线程才能抢到锁,才能进入到同步代码块中执行,上的锁必须是同一个锁对象,同一把锁
javapackage S71ThreadSafe; import java.util.Scanner; public class MyTicket implements Runnable{ // 定义100章票 int ticket = 100; // 任意new一个对象 Object obj = new Object(); Scanner sc = new Scanner(System.in); @Override public void run(){ while(true){ synchronized (sc) { if (ticket > 0) { System.out.println(Thread.currentThread().getName() + "买了第" + ticket + "张票"); ticket--; } } } } }
javapackage S71ThreadSafe; public class Demo210Test { public static void main(String[] args) { // new一次,分别传递到三个线程对象中去 MyTicket myTicket = new MyTicket(); Thread th1= new Thread(myTicket, "赵四"); Thread th2= new Thread(myTicket, "刘能"); Thread th3= new Thread(myTicket, "广坤"); th1.start(); th2.start(); th3.start(); } }
解决线程安全问题方式二:同步方法
1.普通同步方法_非静态
在普通方法内用一个同步代码块
① 格式:
修饰符 synchronized 返回值类型 方法名(参数){
方法体
return 结果
}
② 默认锁:
this
javapackage S73ThreadSafeSolve2; public class Demo212Test { public static void main(String[] args) { // new一次,分别传递到三个线程对象中去 MyTicket myTicket = new MyTicket(); System.out.println(myTicket); Thread th1= new Thread(myTicket, "赵四"); Thread th2= new Thread(myTicket, "刘能"); Thread th3= new Thread(myTicket, "广坤"); th1.start(); th2.start(); th3.start(); } }
javapackage S73ThreadSafeSolve2; import java.util.Scanner; public class MyTicket implements Runnable{ // 定义100章票 int ticket = 100; Scanner sc = new Scanner(System.in); @Override public void run(){ while(true){ try{ Thread.sleep(100L); } catch (InterruptedException e) { throw new RuntimeException(e); } // 调用同步方法 // method01(); method02(); } } // 定义一个同步方法 public synchronized void method01(){ if (ticket > 0) { System.out.println(Thread.currentThread().getName() + "买了第" + ticket + "张票"); ticket--; } } // 在普通方法内用一个同步代码块 public void method02(){ synchronized (this){ System.out.println(this+"------------------------------------------------------------"); if (ticket>0){ System.out.println(Thread.currentThread().getName() + "买了第" + ticket + "张票"); ticket--; } } } }
只new了一次对象
2.静态同步方法
① 格式
修饰符 static synchronized 返回值类型 方法名(参数){
方法体
return 结果
}
② 默认锁
class对象
javapackage S74ThreadSafeSolve3; import java.util.Scanner; public class MyTicket implements Runnable{ // 定义100章票 static int ticket = 100; Scanner sc = new Scanner(System.in); @Override public void run(){ while(true){ try{ Thread.sleep(100L); } catch (InterruptedException e) { throw new RuntimeException(e); } // 调用同步方法 method02(); } } // 在普通方法内用一个同步代码块 public static void method02(){ synchronized (MyTicket.class){ if (ticket>0){ System.out.println(Thread.currentThread().getName() + "买了第" + ticket + "张票"); ticket--; } } } }
javapackage S74ThreadSafeSolve3; public class Demo213Test { public static void main(String[] args) { // new一次,分别传递到三个线程对象中去 MyTicket myTicket = new MyTicket(); System.out.println(myTicket); Thread th1= new Thread(myTicket, "赵四"); Thread th2= new Thread(myTicket, "刘能"); Thread th3= new Thread(myTicket, "广坤"); th1.start(); th2.start(); th3.start(); } }
StringBuider是线程不安全的,StringBuffer加了死锁是线程安全,线程同步的