场景:抢票
抢票的核心问题就是共享数据,多个人可以理解为多个线程,同时抢票;票数是共享数据,所有人都只能从唯一的余票池里抢票
方式一:同步代码块
synchronized(同步监视器){
//需要被同步的代码
}
说明:
1.操作共享数据的代码,即为需要被同步的代码
2.共享数据:多个线程共同操作的变量,比如:ticket就是共享数据
3.同步监视器,俗称锁,任何一个类的对象都可以充当锁,
a) 基于Thread类继承实现的代码块同步
public class ThreadDemo01{
public static void main(String[] args) {
Windows w1 = new Windows();
Windows w2 = new Windows();
Windows w3 = new Windows();
w1.setName("W1");
w2.setName("W2");
w3.setName("W3");
w1.start();
w2.start();
w3.start();
}
}
class Windows extends Thread{
private static int TICKET=100;
private static Object obj = new Object();
@Override
public void run() {
while (true){
synchronized (Window.class) { //这个也一样,Window.class也是代表Window这个类
//synchronized (obj) { --这种是大家最常用的方法,前面new好的对象,这里引用
if (TICKET > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + ":" + TICKET);
TICKET--;
} else {
break;
}
}
}
}
}
b) 基于实现Runnable接口的代码块同步
public class ThreadDemo02{
public static void main(String[] args) {
Windows w = new Windows();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.setName("T1");
t2.setName("T2");
t3.setName("T3");
t2.start();
t1.start();
t3.start();
}
}
class Windows implements Runnable{
private int TICKET=100;
@Override
public void run() {
while (true) {
//this是代表Window1,因为在main里面,Window1只new了一次,所以这个this代表的对象唯一,故也可以作为锁来使用
synchronized(this){
if (TICKET > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + TICKET);
TICKET--;
}else {
break;
}
}
}
}
}
方式二: 同步方法
说明:同步方法实际上就是将需要同步的代码单独写成一个方法,将整个方法进行同步即可,
a) 实现Runnable接口的方法同步
public class ThreadDemo03{
public static void main(String[] args) {
Windows w = new Windows();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.setName("T1");
t2.setName("T2");
t3.setName("T3");
t2.start();
t1.start();
t3.start();
}
}
class Windows implements Runnable{
private int TICKET=100;
@Override
public void run() {
while (true) {
show();
}
}
private synchronized void show(){ //同步监视器默认是this
if (TICKET > 0) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + TICKET);
Thread.yield();
TICKET--;
}
}
}
b) 继承Thread类的子类的方法同步
public class ThreadDemo04{
public static void main(String[] args) {
Windows w1 = new Windows();
Windows w2 = new Windows();
Windows w3 = new Windows();
w1.setName("W1");
w2.setName("W2");
w3.setName("W3");
w1.start();
w2.start();
w3.start();
}
}
class Windows extends Thread{
private static int TICKET=100;
@Override
public void run() {
while (true){
show();
}
}
public static synchronized void show(){
if (TICKET > 0) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + TICKET);
TICKET--;
}
}
}
方式三
Lock锁方式解决线程安全问题
public class ThreadDemo05 {
public static void main(String[] args) {
Windows w = new Windows();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.start();
t2.start();
t3.start();
}
}
class Windows implements Runnable{
private int TICKET=100;
//1.实例化ReentrantLock
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true){
try {
//2.调用lock()
lock.lock();
if (TICKET > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + TICKET);
TICKET--;
}else {
break;
}
} finally {
//3.解锁
lock.unlock();
}
}
}
}
- 线程安全的问题,最核心的问题就是对共享数据的操作,最核心的处理办法就是禁止多个线程同时操作共享数据,类似synchronized等其实也就是将多个线程的操作还是串行着去执行的,所以做了同步机制的代码执行效率就比没有的慢
- 演示代码是比较基础的,很多线程安全问题还是挺隐蔽的,类似死锁也是很隐蔽的,跟Demo级别的代码比起来还是很复杂的,需要不断总结和多练习才能搞定啦
网友评论