1: 线程概述
在了解线程之前我们先要知道什么是进程。
进程:执行中的程序。
线程:进程的执行单元,线程依靠进程运行,并且只能使用分配给线程的资源
由于JAVA虚拟机采用抢占式调度模式,指根据线程优先级选择线程占用cpu,如优先级相同,则随机选择一个线程,使其占用cpu。如果是单线程,我们自然不用考虑这个问题。但是如果是多线程时,就会出现不同的线程之间抢夺cpu的情况。为了应付这种情况,我们引入线程同步。
线程同步:线程同步,通俗来说就是线程同步就是一种等待机制,多个需要同时访问此对象的[线程池]进入这个对象的等待池形成队列,等待前面的线程使用完成,下一个线程再使用。
2:线程同步的具体使用
public class selltricket implements Runnable {
private int tricket = 100;
@Override
public void run() {
// 相同的票出现多次
// 休眠时其他线程抢夺cpu的执行权
while (true) {
// 同步代码块
synchronized (this){
if (tricket > 0) {
// 模拟出票时间
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + tricket + "票");
tricket--;
}
}
}
}
}
public class 卖票 {
public static void main(String[] args) {
selltricket s1=new selltricket();
Thread t1=new Thread(s1,"窗口一");
Thread t2=new Thread(s1,"窗口二");
Thread t3=new Thread(s1,"窗口三");
t1.start();
t2.start();
t3.start();
}
}
我们先创建sellstricket类,然后在卖票类中创建sellstricket对象,执行线程。
3: 具体分析
为了达到线程同步的目的,我们在sellstricket类中使用sleep()方法模拟出票时间,这样每个线程执行后都会出现一定时间的停滞,这时下一线程就会抢夺cpu,完成规律的线程执行。
private int tricket = 100;
// 模拟出票时间
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
4: 同步代码块解决线程安全问题
事实上,如果按照上面的代码运行,结果会出现一些不和逻辑的错误。 到的结果会是,三条语句的ticket都相同,然后ticket突然减三,接着又输出三条ticket相同的输出语句。
那么,该如何解决这种情况呢? 这种延迟卖票的问题被称为线程安全问题,要发生线程安全问题需要满足三个条件(任何一共条件不满足都不会造成线程安全问题):
是否存在多线程环境
是否存在共享数据/共享变量
是否有多条语句操作着共享数据/共享变量
由于前两个条件是多线程的必备条件,所以为了解决问题,就必须破坏条件三。
解决办法:
- synchronized —— 自动锁
- lock —— 手动锁
5: 自动锁
public class selltricket implements Runnable {
private int tricket = 100;
private Object OBJ=new Object();
@Override
public void run() {
// 相同的票出现多次
// 休眠时其他线程抢夺cpu的执行权
while (true) {
// 同步代码块
;synchronized (this){
if (tricket > 0) {
// 模拟出票时间
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + tricket + "票");
tricket--;
}
}
}
}
}
synchronized(任意对象键):相当于给代码加锁了,任意对象就可以看成是一把锁 这样就能把多条操作共享数据的代码锁起来,让任意时刻只能有一个线程执行。
6:手动锁
public class selltricket implements Runnable {
private int tricket = 100;
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
// 相同的票出现多次
// 休眠时其他线程抢夺cpu的执行权
while (true) {
try {
lock.lock();
if (tricket > 0) {
// 模拟出票时间
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + tricket + "票");
tricket--;
}
}finally {
lock.unlock();
}
//
}
}
}
Lock锁可以更加清晰的表达如何加锁和释放锁,当Lock是接口不能直接实例化,所以我们要采用它 实现类Reentrantlock来实例化
7:总结
以上就是线程同步方法与解决同步代码块解决线程安全问题的方法。