Java基础之线程安全问题
线程安全为标题
在java程序中,如果有多个线程同时运行,并且同时执行同一段代码,如果程序每次运行的结果和单线程运行的结果是一样的,而且其他变量的值也和预期是一样的,那么这段代码就是线程安全的。
但是在多线程操作中也可能会有一些线程安全的问题发生。
以卖票事例作为说明,有100张票往外售卖,售完结束。
public class Tickets implements Runnable{
//初始化票数
private int ticket=100;
public void run() {
//卖票
while (true){
if(ticket>0){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在卖:"+ticket--);
}
}
}
}
public class TicketsTest {
public static void main(String[] args) {
//声明变量
Tickets tickets = new Tickets();
//创建三个线程(窗口)
Thread t1 = new Thread(tickets,"窗口1");
Thread t2 = new Thread(tickets,"窗口2");
Thread t3 = new Thread(tickets,"窗口3");
//三个窗口同时买票
t1.start();
t2.start();
t3.start();
}
}
但是在运行结果中会发生以下这种情况:票已经卖出,但是总数没有减少;或者票已经售空,仍然在减票的情况
窗口3正在卖:100
窗口1正在卖:99
窗口2正在卖:100
窗口1正在卖:98
.......
窗口2正在卖:4
窗口1正在卖:2
窗口3正在卖:1
窗口2正在卖:1
窗口1正在卖:1
这类问题就称作线程安全问题,为了解决这个问题,可以采取一些线程同步的措施
线程同步
当我们使用多个线程访问同一资源的时候,且多个线程中对资源有写的操作,就容易出现线程安全问题。
为了解决并发问题(线程同步问题),也就是多线程并发访问同一个资源,对这个资源进行写操作的问题,java中提供了线程同步(synchronized)来解决这个问题。
解决线程同步的方法:
-
方法1:同步代码块
-
同步代码块:synchronized加在代码块上,格式如下
synchronized (lock){
//需要同步的代码块
....
} -
-
方法2:同步锁
-
对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁.
-
锁对象 可以是任意类型。
-
多个线程对象 要使用同一把锁。
-
-
-
方法3:同步方法
-
在方法进行同步,格式如下:
public synchronized void method(){
//需要同步的代码块
......
} -
Lock锁
java.util.concurrent.locks.Lock
机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作,同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。Lock锁也称同步锁,加锁与释放锁方法化了,如下:public void lock() :加同步锁。public void unlock() :释放同步锁。使用如下:
public class Tickets implements Runnable{
//初始化票数
private int ticket=100;
Lock lock = new ReentrantLock();
public void run() {
//卖票
while (true){
lock.lock();
if(ticket>0){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在卖:"+ticket--);
}
lock.unlock();
}
}
}
线程安全为标题
在java程序中,如果有多个线程同时运行,并且同时执行同一段代码,如果程序每次运行的结果和单线程运行的结果是一样的,而且其他变量的值也和预期是一样的,那么这段代码就是线程安全的。
但是在多线程操作中也可能会有一些线程安全的问题发生。
以卖票事例作为说明,有100张票往外售卖,售完结束。
public class Tickets implements Runnable{
//初始化票数
private int ticket=100;
public void run() {
//卖票
while (true){
if(ticket>0){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在卖:"+ticket--);
}
}
}
}
public class TicketsTest {
public static void main(String[] args) {
//声明变量
Tickets tickets = new Tickets();
//创建三个线程(窗口)
Thread t1 = new Thread(tickets,"窗口1");
Thread t2 = new Thread(tickets,"窗口2");
Thread t3 = new Thread(tickets,"窗口3");
//三个窗口同时买票
t1.start();
t2.start();
t3.start();
}
}
但是在运行结果中会发生以下这种情况:票已经卖出,但是总数没有减少;或者票已经售空,仍然在减票的情况
窗口3正在卖:100
窗口1正在卖:99
窗口2正在卖:100
窗口1正在卖:98
.......
窗口2正在卖:4
窗口1正在卖:2
窗口3正在卖:1
窗口2正在卖:1
窗口1正在卖:1
这类问题就称作线程安全问题,为了解决这个问题,可以采取一些线程同步的措施
线程同步
当我们使用多个线程访问同一资源的时候,且多个线程中对资源有写的操作,就容易出现线程安全问题。
为了解决并发问题(线程同步问题),也就是多线程并发访问同一个资源,对这个资源进行写操作的问题,java中提供了线程同步(synchronized)来解决这个问题。
解决线程同步的方法:
-
方法1:同步代码块
-
同步代码块:synchronized加在代码块上,格式如下
synchronized (lock){
//需要同步的代码块
....
} -
-
方法2:同步锁
-
对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁.
-
锁对象 可以是任意类型。
-
多个线程对象 要使用同一把锁。
-
-
-
方法3:同步方法
-
在方法进行同步,格式如下:
public synchronized void method(){
//需要同步的代码块
......
} -
Lock锁
java.util.concurrent.locks.Lock
机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作,同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。Lock锁也称同步锁,加锁与释放锁方法化了,如下:public void lock() :加同步锁。public void unlock() :释放同步锁。使用如下:
public class Tickets implements Runnable{
//初始化票数
private int ticket=100;
Lock lock = new ReentrantLock();
public void run() {
//卖票
while (true){
lock.lock();
if(ticket>0){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在卖:"+ticket--);
}
lock.unlock();
}
}
}