线程的同步
使用多线程模拟火车站售票程序,开启三个窗口售票。
public class Tick implements Runnable{
private static int num = 100;
@Override
public void run() {
while(true) {
if(num > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"销售车票座位号为:" + num--);
}else {
break;
}
}
}
}
//此时创建三个线程 表示三个售票窗口
public class SaleTicket {
public static void main(String[] args) {
Thread t1 = new Thread(new Tick(),"1号窗口");
Thread t2 = new Thread(new Tick(),"2号窗口");
Thread t3 = new Thread(new Tick(),"3号窗口");
Thread t4 = new Thread(new Tick(),"4号窗口");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
问题的原因: 当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有 执行完,另一个线程参与进来执行。导致共享数据的错误。
解决办法: 对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。
如何实现线程同步:
1 同步方法
2 同步代码块
Synchronized java对于多线程的安全问题提供了专业的解决方式:同步机制
使用同步代码块来实现线程同步
public static void main(String[] args) {
Object obj = new Object();
Thread t1 = new Thread() {
public void run() {
while(true) {
synchronized(obj){
System.out.print("中");
System.out.print("北");
System.out.print("大");
System.out.print("学");
System.out.println();
}
}
};
};
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
while(true) {
synchronized (obj) {//synchronized (同步锁)
System.out.print("蓝");
System.out.print("桥");
System.out.print("软");
System.out.print("件");
System.out.print("学");
System.out.print("院");
System.out.println();
}
}
}
});
t1.start();
t2.start();
}
}
在同步代码块中 同步锁/线程锁由谁来担当?
在同步代码块中 锁是一个对象 而且可以是任意对象
在同步代码块中 锁对象必须是同一个对象
也可以使用本类的字节码文件对象
静态方法的同步代码块 的锁对象:
可以是任意的静态对象都可以 必须是同一个对象
可以使用本类的字节码文件对象
什么时候可以使用this来作为同步锁:
public class SynchronizedTest {
//static String s = "aa";
public static void main(String[] args) {
SynchronizedTest st = new SynchronizedTest();
st.show1();
st.show2();
}
public void show1() {//同步方法
new Thread() {
public void run() {
while(true) {
print1();
}
};
}.start();
}
public void show2() {
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
print2();
}
}
}).start();
}
public void print1() {
synchronized (this) {
System.out.print("中");
System.out.print("北");
System.out.print("大");
System.out.print("学");
System.out.println();
}
}
public void print2() {
synchronized (this) {
System.out.print("蓝");
System.out.print("桥");
System.out.print("软");
System.out.print("件");
System.out.print("学");
System.out.print("院");
System.out.println();
}
}
}
同步方法
同步方法中的锁对象是谁?
public synchronized void print1() {
System.out.print("中");
System.out.print("北");
System.out.print("大");
System.out.print("学");
System.out.println();
}
public void print2() {
synchronized (this) {
System.out.print("蓝");
System.out.print("桥");
System.out.print("软");
System.out.print("件");
System.out.print("学");
System.out.print("院");
System.out.println();
}
}
通过上述代码 证明同步方法中的锁对象是this
静态同步方法的锁对象是谁?
public static synchronized void print1() {
System.out.print("中");
System.out.print("北");
System.out.print("大");
System.out.print("学");
System.out.println();
}
public static void print2() {
synchronized (SynchronizedTest.class) {
System.out.print("蓝");
System.out.print("桥");
System.out.print("软");
System.out.print("件");
System.out.print("学");
System.out.print("院");
System.out.println();
}
}
通过上述代码 证明静态同步方法中的锁对象是本类的字节码对象
总结:
1 什么时候使用同步?
当多个线程操作共享数据时 就需要线程同步。
2 线程同步的方式:同步代码块 同步方法 可以时静态也可以时非静态
3 同步代码块的锁对象?
当在非静态方法中 对存在线程安全问题的代码实行同步策略 此时的锁对象可以是任意对象 只需要保证所有的线程使用的是同一个锁对象 this也可以充当锁对象
在静态代码块中 可以使用静态对象来作为锁对象 保证所有的线程使用的是同一个锁对象
4 同步方法的锁对象?
对于非静态方法 锁对象为this
对于静态方法 字节码对象 本类的class对象
线程中需要避免的问题:死锁
public static void main(String[] args) {
String lock1 = "aaa";
String lock2 = "bbb";
new Thread() {
@Override
public void run() {
while(true) {
synchronized (lock1) {
System.out.println("AAAA");
synchronized (lock2) {
System.out.println("BBBB");
}
}
}
}
}.start();
new Thread() {
@Override
public void run() {
while(true) {
synchronized (lock2) {
System.out.println("CCCCCCCCCCCCCCCCCCCCC");
synchronized (lock1) {
System.out.println("DDDDDDDDDDDDDDDDDDDD");
}
}
}
}
}.start();
}
死锁是如何出现的:死锁的出现是因为锁的嵌套
在开发中 应尽量避免死锁的出现 应尽量不要使用锁的嵌套
锁在什么时候会被释放?
1 同步的代码执行结束
2 当前线程在同步代码块、同步方法中遇到break、return终止了该代码块、 该方法的继续执行。
3当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导 致异常结束。
4当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线 程暂停,并释放锁。
不会释放锁的操作
线程执行同步代码块或同步方法时,程序调用Thread.sleep()、 Thread.yield()方法暂停当前线程的执行
线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程 挂起,该线程不会释放锁(同步监视器)。
应尽量避免使用suspend()和resume()来控制线程
单例设计模式:
1 将构造器私有化
2 提供一个该类的静态变量(类型为当前类)
3 提供对外获取该类实例对象的方法
饿汉:
// 饿汉式 是否存在线程安全问题 不存在
public class Singleton1 {
private static Singleton1 instance = new Singleton1();
private Singleton1() {
}
public static Singleton1 getInstance() {
return instance;
}
}
懒汉:
//懒汉式 是否存在线程问题 存在
//如何解决 使用线程同步
public class Singleton2 {
private static Singleton2 instance = null;
private Singleton2() {
}
public static Singleton2 getInstance() {
synchronized (Singleton2.class) {
if(instance == null) {
instance = new Singleton2();
}
}
return instance;
}
}