一、线程不安全 与 线程安全
1. 了解线程不安全
线程不安全示例1:模拟抢票
public class Unsafe01 extends Thread{
private int num = 30;
@Override
public void run() {
while(true) {
if(num > 0) {
try {
Thread.sleep(100);
} catch(Exception e) {
}
System.out.println(Thread.currentThread().getName()+"卖出了第"+num--+"张票");
}else {
System.out.println("票卖完了");
return;
}
}
}
public static void main(String[] args) {
Unsafe01 sell = new Unsafe01();
Thread sell1 = new Thread(sell, "窗口1");
Thread sell2 = new Thread(sell, "窗口2");
Thread sell3 = new Thread(sell, "窗口3");
sell1.start();
sell2.start();
sell3.start();
}
}
- (会出现负数,抢到同一张票的情况)
线程不安全示例2:
import java.util.ArrayList;
import java.util.List;
public class Unsafe02 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
for(int i = 0;i < 10000;i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
System.out.println(list.size());
}
}
- 输出的值小于10000
2.使用线程安全
1. 使用线程安全的两种方式
- 同步方法
- 同步块
2.使用方法
对于同步方法:在方法前面加上 synchronized
public synchronized void play(){
}
对于同步块:
synchronized(obj){
}
synchronized 保证在同一个时刻只有一个线程在使用。
3. 修改以上两个程序:
示例1加上同步方法
public class Safe01 extends Thread{
private int num = 30;
boolean flag = true;
@Override
public void run() {
while(flag) {
sale();
try {
Thread.sleep(100);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
//synchronized 锁的是this(也就是对象)的资源,而不是方法。
private synchronized void sale() {
if(num > 0) {
try {
Thread.sleep(100);
} catch(Exception e) {
}
System.out.println(Thread.currentThread().getName()+"卖出了第"+num--+"张票");
}else {
System.out.println("票卖完了");
flag = false;
return;
}
}
public static void main(String[] args) {
Safe01 sell = new Safe01();
Thread sell1 = new Thread(sell, "窗口1");
Thread sell2 = new Thread(sell, "窗口2");
Thread sell3 = new Thread(sell, "窗口3");
sell1.start();
sell2.start();
sell3.start();
}
}
示例2加上同步块
import java.util.ArrayList;
import java.util.List;
public class Safe02 {
public static void main(String[] args) throws InterruptedException {
List<String> list = new ArrayList<String>();
for(int i = 0;i < 10000;i++) {
new Thread(()->{
synchronized(list) {
list.add(Thread.currentThread().getName());
}
}).start();
}
Thread.sleep(1);
System.out.println(list.size());
}
}
3. synchronized限定的范围太大会影响效率,太小有可能发挥不了功能,发生错误。
使用以下代码测试一下(将示例1改为同步块的形式,并添加了计时):
import java.util.Date;
public class Safe03 extends Thread{
private int num = 300;
boolean flag = true;
long start;
long end;
@Override
public void run() {
while(flag) {
if(num <= 0) {
Date date = new Date();
end = date.getTime();
System.out.println((end-start)/1000+"seconds");
return;
}
synchronized((Integer)num) {
if(num > 0) {
System.out.println(Thread.currentThread().getName()+"卖出了第"+num--+"张票");
}else {
System.out.println("票卖完了");
flag = false;
Date date = new Date();
end = date.getTime();
System.out.println((end-start)/1000+"seconds");
return;
}
try {
Thread.sleep(100);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
Safe03 sell = new Safe03();
Date date = new Date();
sell.start = date.getTime();
Thread sell1 = new Thread(sell, "窗口1");
Thread sell2 = new Thread(sell, "窗口2");
Thread sell3 = new Thread(sell, "窗口3");
sell1.start();
sell2.start();
sell3.start();
}
}
第一次同步块锁 num:
第二次锁 this: