问题:以售票为例,卖票过程中出现重票、错票,就是出现了线程的安全问题
原因:某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票。
synchronized(同步监视器){
//需要被同步的代码
}
说明:1.操作共享数据的代码,即为需要被同步的代码
2.共享数据:多个线程共同操作的变量。比如票的数量
3.同步监视器。俗称:锁。任何一个类的对象,都可以充当锁
要求:多个线程必须要共用同一把锁
补充:
1.在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器(不用重新定义一个对象,比较方便)
2.在继承Thread类创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类充当同步监视器
同步代码块
处理Runnable接口的线程安全问题
package Main;
public class Window implements Runnable { //实现Runnable接口
private int ticket=100;
@Override
public void run() {
while (true) {
synchronized (this) { //覆盖共享数据ticket,需要被同步的代码在括号里
if (ticket > 0) {
try {
Thread.sleep(100); //增加重票和错票的概率,可以明显对比加了锁之后的安全性
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖票,票号:" + ticket);
ticket--;
} else {
break;
}
}
}
}
}
package Main;
public class Main {
public static void main(String[] args) {
Window w=new Window(); //应该只创建一个对象
Thread t1=new Thread(w);
Thread t2=new Thread(w);
Thread t3=new Thread(w);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
/*三个窗口共同销售100张票*/
t1.start();
t2.start();
t3.start();
}
}
处理继承Thread类的线程安全问题
package Main;
public class Window1 extends Thread {
private static int ticket=100; //因为创建了3个Window1的对象,所以用静态变量
@Override
public void run() {
while(true)
{
synchronized (Window1.class) { //当前类充当同步监视器
if (ticket > 0) {
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(currentThread().getName() + "卖票,票号:" + ticket);
ticket--;
} else {
break;
}
}
}
}
}
package Main;
public class Main {
public static void main(String[] args) {
Window1 w1=new Window1();
Window1 w2=new Window1();
Window1 w3=new Window1();
w1.setName("窗口1");
w2.setName("窗口2");
w3.setName("窗口3");
w1.start();
w2.start();
w3.start();
}
}
同步方法
处理Runnable接口的线程安全问题
package Main;
public class Window implements Runnable {
private int ticket=100;
@Override
public void run() {
while (true) {
show();
}
}
public synchronized void show() { //将需要被同步的代码封装成一个方法,在方法的定义前面加上synchronized.同步监视器:this
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖票,票号:" + ticket);
ticket--;
}
}
}
package Main;
public class Main {
public static void main(String[] args) {
Window w=new Window();
Thread t1=new Thread(w);
Thread t2=new Thread(w);
Thread t3=new Thread(w);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
处理继承Thread类的线程安全问题
package Main;
public class Window1 extends Thread {
private static int ticket=100;
@Override
public void run() {
while(true)
{
show();
}
}
public static synchronized void show() //方法的声明再加上static ,同步监视器:Window1.class
{
if (ticket > 0) {
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(currentThread().getName() + "卖票,票号:" + ticket);
ticket--;
}
}
}
package Main;
public class Main {
public static void main(String[] args) {
Window1 w1=new Window1();
Window1 w2=new Window1();
Window1 w3=new Window1();
w1.setName("窗口1");
w2.setName("窗口2");
w3.setName("窗口3");
w1.start();
w2.start();
w3.start();
}
}