线程同步
- 当多个线程操作同一资源,例如上万人同时抢几张火车票,几个人都想上厕所但只有一个坑位。这个时候就会由于资源的抢夺,导致程序输出错误的结果,比如说,一个人还没上完厕所,另一个就进去了。
- 线程同步就是用来解决上述问题,总的来说就是通过队列+锁的方式来控制对公共资源操作时不受其他线程影响
- 通过加锁的方式保证了程序有条不紊的运行,但是一个线程持有锁会导致其他所有线程被挂起,加锁解锁导致了比较多的上下文切换和调度延时,因此牺牲了一定的效率
- 在Java中是通过锁机制synchronized来实现的,主要可以分为两种
-
同步方法 public synchronized void method(int args){}
-
同步块 synchronized(Obj){}
代码演示
例1-同步方法
package MultiProcess;
public class Unsafe {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket, "我").start();
new Thread(buyTicket, "我室友").start();
new Thread(buyTicket, "黄牛").start();
}
}
class BuyTicket implements Runnable{
boolean flag = true;
private int ticketNums = 10;
@Override
public void run() {
while(flag){
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void buy() throws InterruptedException{
if(ticketNums <= 0){
flag = false;
return;
}
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + "买了第" + ticketNums-- + "张票");
}
}
结果
我买了第4张票
我室友买了第3张票
黄牛买了第2张票
我买了第1张票
黄牛买了第-1张票
我室友买了第0张票
由于多线程操作同一资源ticketNum,进行ticketNum--,导致了线程不安全问题。因为ticketNum--操作需要经过三个步骤,把ticketNum的值读到寄存器中,进行-1运算,写回内存。所以A线程刚把ticketNum放到寄存器,B线程就会读到寄存器里的值,没有读-1运算后的值,导致了结果的异常。
添加同步方法
package MultiProcess;
public class Unsafe {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket, "我").start();
new Thread(buyTicket, "我室友").start();
new Thread(buyTicket, "黄牛").start();
}
}
class BuyTicket implements Runnable{
boolean flag = true;
private int ticketNums = 10;
@Override
public void run() {
while(flag){
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private synchronized void buy() throws InterruptedException{
if(ticketNums <= 0){
flag = false;
return;
}
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + "买了第" + ticketNums-- + "张票");
}
}
结果
黄牛买了第7张票
我买了第6张票
黄牛买了第5张票
黄牛买了第4张票
我室友买了第3张票
黄牛买了第2张票
黄牛买了第1张票
例2-同步块
package MultiProcess;
import java.util.ArrayList;
import java.util.List;
public class UnsafeList {
public static void main (String[] args) {
List<String> list = new ArrayList<String>();
for(int i = 0; i < 100000; i++){
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
结果
99983
因为多个线程可能将元素放到了同一位置上
添加同步块
package MultiProcess;
import java.util.ArrayList;
import java.util.List;
public class UnsafeList {
public static void main (String[] args) {
List<String> list = new ArrayList<String>();
for(int i = 0; i < 100000; i++){
new Thread(() -> {
synchronized (list) {
list.add(Thread.currentThread().getName());
}
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
结果
100000