当我们使用多个线程访问同一资源的时候,且多个线程中对资源有写的操作,就容易出现线程安全问题。
要解决上述多线程并发访问一个资源的安全性问题:也就是解决重复票与不存在票问题,Java中提供了同步机制
(synchronized)来解决。
1:Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。:
2:当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
3:然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
4:当一个线程访问object的同步代码块时,别的线程对该object的其他同步代码块的访问都被阻塞。
多线程共享数据不安全的演示案例
卖票案例,三个线程代替三个卖票窗口。
package synchronizedDemo;
public class Ticket implements Runnable{
private int ticketNum = 100;
//卖票案例测试用类
@Override
public void run() {
while (true) {
//判断,如果票数大于0,卖票,票数-1,打印输出
if (ticketNum > 0) {
try {
//睡眠0.1秒
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticketNum--;
String name = Thread.currentThread().getName();
System.out.println(name+"正在卖"+ticketNum+"号票");
}
}
}
}
package synchronizedDemo;
public class SynchronizedTest {
public static void main(String[] args) {
//Runnable子类实例
Ticket ticket = new Ticket();
//创建了三个线程
Thread ticketWindow1 = new Thread(ticket, "窗口1");
Thread ticketWindow2 = new Thread(ticket, "窗口2");
Thread ticketWindow3 = new Thread(ticket, "窗口3");
//开启三个线程
ticketWindow1.start();
ticketWindow2.start();
ticketWindow3.start();
}
}
输出的错误结果:
由于多个线程同时访问变量,导致变量出现错误。
synchronized用于同步代码块的演示
synchronized (一个任意对象用来当同步锁) {
需要同步的代码
}
新ticket类:
package synchronizedDemo;
public class NewTicket implements Runnable{
private int ticketNum = 100;
//卖票案例测试用类
Object obj = new Object();
@Override
public void run() {
while (true) {
//仅添加synchoized关键字
synchronized (obj){
//判断,如果票数大于0,卖票,票数-1,打印输出
if (ticketNum > 0) {
try {
//睡眠0.1秒
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticketNum--;
String name = Thread.currentThread().getName();
System.out.println(name+"正在卖"+ticketNum+"号票");
}
}
}
}
}
package synchronizedDemo;
public class NewSynchronizedDemo {
public static void main(String[] args) {
NewTicket newTicket = new NewTicket();
Thread tic1 = new Thread(newTicket, "窗口1");
Thread tic2 = new Thread(newTicket, "窗口2");
Thread tic3 = new Thread(newTicket, "窗口3");
tic1.start();
tic2.start();
tic3.start();
}
}
仅添加了synchronized关键字,错误结果消失,不存在的票不再出现
synchronized用于同步方法
Demo:
package synchronizedDemo;
public class TicketSynMen implements Runnable{
private int ticketNum = 100;
@Override
public void run() {
while (true) {
sellTicket();
}
}
//使用synchronized关键字修饰方法
private synchronized void sellTicket() {
if (ticketNum > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
String name = Thread.currentThread().getName();
System.out.println(name+"正在卖第:"+ticketNum+"张票");
ticketNum--;
}
}
}
package synchronizedDemo;
public class SynchronizedTest {
public static void main(String[] args) {
//Runnable子类实例
TicketSynMen ticketSynMen = new TicketSynMen();
//创建了三个线程
Thread ticketWindow1 = new Thread(ticketSynMen, "窗口1");
Thread ticketWindow2 = new Thread(ticketSynMen, "窗口2");
Thread ticketWindow3 = new Thread(ticketSynMen, "窗口3");
//开启三个线程
ticketWindow1.start();
ticketWindow2.start();
ticketWindow3.start();
}
}
输出正常结果:
注:该段代码不会结束,需要手动结束,可以判断结束。