继续上篇的总结,这次我们讲线程同步机制
线程同步是为了确保线程安全,所谓线程安全指的是多个线程对同一资源进行访问时,有可能产生数据不一致问题,导致线程访问的资源并不是安全的。如果多线程程序运行结果和单线程运行的结果是一样的,且相关变量的值与预期值一样,则是线程安全的。
举个简单的例子,后边会写到这个例子,铁路售票,分四个窗口卖,一共一百张,需要同步。
线程同步机制包括同步代码块和同步方法两种。
1.同步代码块
package com.tao.syn;
public class Demo1_Synchronized {
public static void main(String[] args) {
final Printer p=new Printer();
new Thread(){
public void run(){
for(int i=1;i<100;i++){
p.print1();
}
}
}.start();
new Thread(){
public void run(){
for(int i=1;i<100;i++){
p.print2();
}
}
}.start();
}
}
class Demo{
}
class Printer {
Demo d=new Demo();
public void print1(){
synchronized (d) { //同步代码块,锁机制,锁对象可以是任意的
System.out.print("清");
System.out.print("华");
System.out.print("大");
System.out.print("学");
System.out.println();
}
}
public void print2(){
synchronized (d) { // synchronized (new Demo())不可行,因为不是同一对象了
System.out.print("我");
System.out.print("爱");
System.out.print("你");
System.out.println();
}
}
}
2.同步方法
package com.tao.syn;
public class Demo2_Synchronized {
/*
非静态的同步方法的锁对象是神马?
答:非静态的同步方法的锁对象是this
静态的同步方法的锁对象是什么?
是该类的字节码对象
*/
public static void main(String[] args) {
final Printer p=new Printer();
new Thread(){
public void run(){
for(int i=1;i<1000;i++){
p.print1();
}
}
}.start();
new Thread(){
public void run(){
for(int i=1;i<1000;i++){
p.print2();
}
}
}.start();
}
}
class Demo2{
}
class Printer2 {
Demo d=new Demo();
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 (Printer2.class) { // synchronized (new Demo())不可行,因为不是同一对象了
System.out.print("我");
System.out.print("爱");
System.out.print("你");
System.out.println();
}
}
}
铁路卖票的例子
package com.tao.syn;
/**
*
* @author 天外飞星
* 铁路卖票,四个窗口,一共100张
*
*/
public class Demo3_Ticket {
public static void main(String[] args) {
new Ticket().start(); //开启四条线程,即四个窗口
new Ticket().start();
new Ticket().start();
new Ticket().start();
}
}
class Ticket extends Thread{
private static int ticket=100; //必须设置为static的,四个线程共享ticket
public void run(){
while(true){
synchronized (Ticket.class) { //只能class,因为使用对象的话,每个Ticket都会创建一个
if(ticket==0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
}
}
System.out.println("卖第"+ticket--+"张票");
}
}
}
单例设计模式
单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例。
具体实现
需要:
(1)将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
(2)在该类内部产生一个唯一的实例化对象,并且将其封装为private static类型。
(3)定义一个静态方法返回这个唯一对象。
实现一:立即加载 / “饿汉模式”
package com.tao.thread;
public class Demo2_Singleton1 {
public static void main(String[] args) {
Singleton1 s2 = Singleton1.getInstance();
// Singleton.s1=null;
Singleton1 s3 = Singleton1.getInstance();
System.out.println(s2 == s3);
}
}
class Singleton1 {
// 1.私有构造方法,其他类不能访问该类构造方法
private Singleton1() {
};
/*
* 一、饿汉式(常用)
*/
// 2.创建本类对象
private static Singleton1 s1 = new Singleton1();
// 3.对外提供公共的访问方法
public static Singleton1 getInstance() { // 获取实例
return s1;
}
}
实现二:延迟加载 / “懒汉模式”
package com.tao.thread;
public class Demo3_Singleton2 {
public static void main(String[] args) {
Singleton2 s2 = Singleton2.getInstance();
// Singleton.s1=null;
Singleton2 s3 = Singleton2.getInstance();
System.out.println(s2 == s3);
}
}
class Singleton2 {
// 1.私有构造方法,其他类不能访问该类构造方法
private Singleton2() {
};
/*
* 二、懒汉式
*/
// 2.创建本类对象
private static Singleton2 s1;
public static Singleton2 getInstance(){
if(s1==null){
//线程1等待,线程2等待可能会导致创建两个对象 ,平时不用,面试用
s1=new Singleton2();
}
return s1;
}
}
实现三、final修饰方法
package com.tao.thread;
/**
* 三、final修饰方法
* @author 天外飞星
*
*/
public class Demo4_Singleton3 {
public static void main(String[] args) {
Singleton4 s2=Singleton4.s1; // 成员变量被私有,不能通过类名.调用
// Singleton4 s1=null;
Singleton4 s3=Singleton4.s1;
System.out.println(s2==s3);
}
}
class Singleton4{
//1.私有构造方法,其他类不能访问该类构造方法
private Singleton4(){};
static final Singleton4 s1=new Singleton4();
}