Java多线程案例
前言:我们上篇博客中介绍了Java多线程的状态以及创建等基础知识,接下来我们将通过代码来进行实际操作
卖票机制
卖票机制第一次(失败):
Tickets
public class Tickets extends Thread{
public Tickets(String name){
super();
this.setName(name);
}
private int ticket = 1;
@Override
public void run() {
while (true){
if(ticket<=10){
System.out.println(Thread.currentThread().getName()+"正在售第"+(ticket++)+"张票");
}
}
}
}
public class TestThread{
public static void main(String[] args) {
Tickets tickets1 = new Tickets("窗口一:");
Tickets tickets2 = new Tickets("窗口二:");
Tickets tickets3 = new Tickets("窗口三:");
tickets1.start();
tickets2.start();
tickets3.start();
}
}
运行结果如下:
窗口一:正在售第1张票
窗口一:正在售第2张票
窗口二:正在售第1张票
窗口二:正在售第2张票
窗口二:正在售第3张票
窗口二:正在售第4张票
窗口一:正在售第3张票
窗口一:正在售第4张票
窗口一:正在售第5张票
窗口一:正在售第6张票
窗口三:正在售第1张票
窗口一:正在售第7张票
窗口二:正在售第5张票
窗口一:正在售第8张票
窗口三:正在售第2张票
窗口一:正在售第9张票
窗口二:正在售第6张票
窗口一:正在售第10张票
窗口三:正在售第3张票
窗口三:正在售第4张票
窗口三:正在售第5张票
窗口三:正在售第6张票
窗口三:正在售第7张票
窗口三:正在售第8张票
窗口三:正在售第9张票
窗口三:正在售第10张票
窗口二:正在售第7张票
窗口二:正在售第8张票
窗口二:正在售第9张票
窗口二:正在售第10张票
如上一共创建了三个线程,由于变量不共享所以每个线程都有自己的变量,所以不会存在多个线程共同访问一个变量的情况,这也就是说在JVM分配给线程独立的内存中运行
卖票机制第二次(失败)
Tickets
public class Tickets extends Thread{
private int ticket = 1;
@Override
public void run() {
while (true){
if(ticket<=10){
System.out.println(Thread.currentThread().getName()+"正在售第"+(ticket++)+"张票");
}
}
}
}
TestThread
public class TestThread{
public static void main(String[] args) {
Tickets tickets = new Tickets();
Thread thread1 = new Thread(tickets,"窗口一");
Thread thread2 = new Thread(tickets,"窗口二");
Thread thread3 = new Thread(tickets,"窗口三");
thread1.start();
thread2.start();
thread3.start();
}
}
运行结果如下:
窗口二正在售第1张票
窗口三正在售第3张票
窗口一正在售第2张票
窗口三正在售第5张票
窗口二正在售第4张票
窗口三正在售第7张票
窗口一正在售第6张票
窗口三正在售第9张票
窗口二正在售第8张票
窗口一正在售第10张票
从这次的运行结果来看,我们解决了对于上述变量不共享的问题,虽然此次结果比之前的要好一些,但是效果却不是我们想要的,这个原因在上一篇博客Java多线程基础提到过,在并发情况下还是存在不安全因素的
卖票机制第三次(成功)
Tickets
public class Tickets extends Thread{
private int ticket = 1;
@Override
public void run() {
while (true){
synchronized (Tickets.class){
try {
if(ticket<=10){
System.out.println(Thread.currentThread().getName()+"正在售第"+(ticket++)+"张票");
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
}
TestThread
public class TestThread{
public static void main(String[] args) {
Tickets tickets = new Tickets();
Thread thread1 = new Thread(tickets,"窗口一");
Thread thread2 = new Thread(tickets,"窗口二");
Thread thread3 = new Thread(tickets,"窗口三");
thread1.start();
thread2.start();
thread3.start();
}
}
运行结果如下:
窗口一正在售第1张票
窗口一正在售第2张票
窗口一正在售第3张票
窗口一正在售第4张票
窗口一正在售第5张票
窗口一正在售第6张票
窗口一正在售第7张票
窗口一正在售第8张票
窗口一正在售第9张票
窗口一正在售第10张票
这次终于成功了,经过代码对比可以看出,最后我们通过对synchronized 的使用,所有线程在售卖票之前都会进行排序操作,如果一个线程进入,发现此时处于被锁状态则说明有其他线程正在处理这个数据,那就要等到这个线程处理完成之后才能访问处理,这段被synchronized关键字标注的代码块被称为是互斥区或者是临界区
当然其实对于synchronized 所放的位置也是不确定我们也可以锁到run()方法上:
总体来说CPU可能随机的在多个处于就绪状态中的线程中进行切换,这时就可能出现线程的安全问题
线程安全问题,其实是指多线程环境下对共享资源的访问可能会引起此共享资源的不一致性,解决该问题则需要同步锁的加入,执行synchronized部分代码的时候必须需要对象锁,而一个对象只有一个锁,只有执行完synchronized里面的代码后释放锁,其他线程才可以获得锁,那么就保证了同一时刻只有一个线程访问synchronized里面的代码