线程安全问题:
线程安全出现的根本原因:
1.存在两个或者两个以上的线程对象共享同一个资源;
2.多线程操作共享资源代码有多个语句。
线程安全问题的解决方案(2个):
方式一:同步代码块
格式:synchronize(锁对象){
需要被同步的代码
}
同步代码块需要注意的事项:
1.锁对象可以是任意的一个对象;
2.一个线程在同步代码块中sleep了,并不会释放锁对象;
3.如果不存在线程安全问题,千万不要使用同步代码块;
4.锁对象必须是多线程共享的一个资源,否则锁不住。
例子:三个窗口售票
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();
}
}
方式二:同步函数(同步函数就是使用synchronized修饰一个函数)
同步函数注意事项:
1.如果函数是一个非静态的同步函数,那么锁对象是this对象;
2.如果函数是静态的同步函数,那么锁对象是当前函数所属的类的字节码文件(class对象);
3.同步函数的锁对象是固定的,不能由自己指定。
例子:两夫妻取钱
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.同步代码块的锁对象可以由我们自由指定,方便控制;
2.同步代码块可以方便的控制需要被同步代码的范围,同步函数必须同步函数的所有代码。