线程安全问题出现的原因和解决方法
线程安全问题出现的原因和解决方法
线程安全问题出现的根本原因:
1. 必须要存在两个或者两个以上的线程共享着一个资源。
2. 操作共享资源的代码必须有两句或者两句以上。
线程安全问题的解决方案(2个):
解决思路:
就是将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程是不可以参与运算的。
必需要当前线程把这些带那都执行完毕后,其他线程才能参与运算。
同步的好处:解决了线程的安全问题。
同步的弊端:相对降低了效率,因为同步外的线程都会判断同步锁。
同步的前提:同步中必须有多个线程并使用同一个锁。
1. 同步代码块
synchronized(锁){
需要被同步的代码
}
class SaleTicket extends Thread{
static int num = 50;//票数 非静态的成员变量,非静态的成员变量数据是在每个对象中都会维护一份数据的。
public SaleTicket(String name) {
super(name);
}
@Override
public void run() {
while(true){
//同步代码块
synchronized ("锁") {
if(num>0){
System.out.println(Thread.currentThread().getName()+"售出了第"+num+"号票");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
num--;
}else{
System.out.println("售罄了..");
break;
}
}
}
}
}
public class Demo4 {
public static void main(String[] args) {
//创建三个线程对象,模拟三个窗口
SaleTicket thread1 = new SaleTicket("窗口1");
SaleTicket thread2 = new SaleTicket("窗口2");
SaleTicket thread3 = new SaleTicket("窗口3");
//开启线程售票
thread1.start();
thread2.start();
thread3.start();
}
}
同步代码块需要注意的事项注意:
1. 同步代码块的锁可以是任意的对象。 同步函数的锁是固定的,非静态函数的锁对象是this对象。 静态函数的锁对象是class对象。
2. 锁对象必须是多线程共享的对象,否则锁不住。
3. 在同步代码块或者是同步函数中调用sleep方法是不会释放锁对象的,如果是调用了wait方法是会释放锁对象的。
2. 同步函数。
修饰符 synchronized 返回值类型 函数名(形参列表..){
}
class BankThread extends Thread{
static int count = 5000;
public BankThread(String name){
super(name);
}
@Override //
public synchronized void run() {
while(true){
synchronized ("锁") {
if(count>0){
System.out.println(Thread.currentThread().getName()+"取走了1000块,还剩余"+(count-1000)+"元");
count= count - 1000;
}else{
System.out.println("取光了...");
break;
}
}
}
}
public class Demo1 {
public static void main(String[] args) {
//创建两个线程对象
BankThread thread1 = new BankThread("老公");
BankThread thread2 = new BankThread("老婆");
//调用start方法开启线程取钱
thread1.start();
thread2.start();
}
}
同步函数注意事项:
1.如果函数是一个非静态的同步函数,那么锁对象是this对象;
2.如果函数是静态的同步函数,那么锁对象是当前函数所属的类的字节码文件(class对象);
3.同步函数的锁对象是固定的,不能由自己指定。
同步函数和同步代码块的区别:
同步函数的锁是固定的this;
同步代码块的锁是任意的对象。
建议使用同步代码块。
静态的同步函数使用的锁是该函数所属字节码文件对象
可以用getClass()方法来获取,也可以用当前 类名.class表示