例子:创建三个窗口卖票,总票数为100张。 1、分析问题
- (1).问题:卖票过程中,出现了重票,错票--->线程的安全问题
- (2).问题出现原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来,也可操作车票
- (3).如何解决:当一个线程a在操作ticket的时候,其他线程不能参与进来,直到线程a操作完成ticket时,其他线程才可以开始操作ticket,这种情况即使线程a出现了阻塞问题,也不能改变
- (4).在Java中,我们通过同步机制,来解决线程的安全问题
2、解决方法 1)、方法一:同步代码块;synchronized(同步监视器);需要被同步的代码
说明:1.操作共享数据的代码,即为需要被同步的代码。--->不能包含代码多了,也不能包含代码少了. 2.共享数据:多个线程共同操作的变量,比如:ticket就是共享数据。 3.同步监视器,俗称:锁,任何一个类的对象,都可以充当锁。 要求:多个线程必须要共用同一把锁 补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器
2)、方法二:同步方法
Runnable实现
public class MyRunnable {
public static void main(String[] args) {
Window2 window2 = new Window2();
Thread t1 = new Thread(window2);
Thread t2 = new Thread(window2);
Thread t3 = new Thread(window2);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
class Window2 implements Runnable {
int ticket = 100;
Object object = new Object();
Rose rose = new Rose();
public void run() {
while (true) {
// this
// 任何唯一的类对象
// Window2.class
synchronized (Window2.class) {
try {
// 休眠重票错票
Thread.currentThread().sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + " :卖票,票号为:" + ticket);
ticket--;
} else {
break;
}
}
}
}
}
Thread类
说明:在继承Thread类的创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类充当同上监视器。
public class MyThread {
public static void main(String[] args) {
Window3 t1 = new Window3();
Window3 t2 = new Window3();
Window3 t3 = new Window3();
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
class Window3 extends Thread {
static int ticket = 100;
static Object object = new Object();
public void run() {
while (true) {
// 错误的方式:this代表着t1,t2,t3三个对象
// 任何唯一的类对象
// Class clazz = Window3.class Window3只会加载一次
synchronized (Window3.class) {
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (ticket > 0) {
System.out.println(getName() + ":卖票,票号为:" + ticket);
ticket--;
} else {
break;
}
}
}
}
}
结构:class Rose {
// 1.属性
// 2.方法
// 3.构造方法
// 4.代码块
// 5.内部类
}
结果
两种方法实现结果都一样:
同步的好处和弊端
- 好处:同步解决了多线程的安全问题
- 弊端:多线程都需要判断锁,比较消耗资源
总结
主要是用两种表现形式: (1)同步代码块: 可以指定需要获取哪个对象的同步锁,使用synchronized的代码块同样需要锁,但他的锁可以是任意对象,考虑到安全问题,一般还是使用同一个对象,相对来说效率较高。
注意: 1、虽然同步代码快的锁可以使任何对象,但是在进行多线程通信使用同步代码快时,必须保证同步代码快的锁的对象和,否则会报错。 2、同步函数的锁是this,也要保证同步函数的锁的对象和调用wait、notify和notifyAll的对象是同一个对象,也就是都是this锁代表的对象。 格式: synchronized(对象) { 需同步的代码; }
(2)同步函数 同步方法是指进入该方法时需要获取this对象的同步锁,在方法上使用synchronized关键字,使用this对象作为锁,也就是使用了当前对象,因为锁住了方法,所以相对于代码块来说效率相对较低。
注:静态同步函数的锁是该方法所在的类的字节码文件对象,即类名.class文件 格式: 修饰词 synchronized 返回值类型 函数名(参数列表) { 需同步的代码; }