1、线程同步
- 同一进程的多个线程共享一块内存空间,当一个线程在操作这块内存空间时,其它线程必须排队等待
- 只有前一个线程执行结束,释放资源和锁,后面的线程获得锁,才可以操作这个内存空间
- 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题
- 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题
2、线程不安全案例
public class TestThreadSynchronized {
public static void main(String[] args) {
TicketWindow ticketWindow = new TicketWindow();
new Thread(ticketWindow,"张三").start();
new Thread(ticketWindow,"李四").start();
new Thread(ticketWindow,"王五").start();
}
}
class TicketWindow implements Runnable{
private int ticketNum = 10;
private boolean flag = true;
@Override
public void run() {
while (flag){
buy();
}
}
private void buy(){
if (ticketNum<=0){
flag = false;
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "出售了" + ticketNum-- + "张票");
}
}
输出:
张三出售了9张票
王五出售了10张票
李四出售了8张票
王五出售了7张票
李四出售了6张票
张三出售了5张票
王五出售了4张票
张三出售了3张票
李四出售了2张票
王五出售了1张票
张三出售了0张票
李四出售了-1张票
3、证明 ArrayList 是不安全的案例
public class UnSafeList {
/**
* 证明 ArrayList 是不安全的
*/
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
new Thread(() ->
list.add(Thread.currentThread().getName())
).start();
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
// 额外知识
public class TestJUC {
public static void main(String[] args) {
// JUC 包下,线程安全的arrayList
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 1000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
4、线程同步实现方式一:同步方法,默认锁的是this(对象的本身)
// 同步方法,使线程安全
private synchronized void buy(){
if (ticketNum<=0){
flag = false;
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "出售了" + ticketNum-- + "张票");
}
5、线程同步实现方式二:同步代码块
public class UnSafeList {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 1000; 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());
}
}
总结:
- 同步块 synchronized (Obj){},监视的对象是需要操作的资源对象
- Obj 称之为 同步监视器,Obj 可以是任何对象,但是推荐使用共享资源作为同步监视器
- 同步监视器的执行过程:第一个线程访问,锁定同步监视器,第二个要等第一个线程访问完毕,解锁同步监视器才能访问