1.1 线程安全与同步实例
1.1.1 购票同步对象锁
【
/*
* 用程序模拟铁路售票系统:实现通过两个售票点发售某日某次列车的50张车票,
* 一个售票点用一个线程表示
*/
publicclass SyncDemo {
public static void main(String[] args) {
/*
* new SaleTicketThread("窗口1").start(); new
* SaleTicketThread("窗口2").start();
*/
// 创建票对象(内部封装了卖票的方法,并且具备运行在线程中的能力)
Ticket tickt = new Ticket();
// 让多个窗口同时卖票(让线程执行指定的任务对象)
new Thread(tickt, "窗口1").start();
new Thread(tickt, "窗口2").start();
}
}
classTicket implements Runnable {
int num = 50;// 票数
Object obj = new Object();
@Override
public void run() {
// 不停地卖票
while (true) {
sale();
}
}
/**
* 卖票,每调用一次,卖一张票
*/
//同步方法,其实就是同步代码块的简写方式
public synchronized void sale() {
// 同步代码块,同一时间只能有一个线程进行执行里面的代码
// synchronized (this) {// 同步锁,每个对象都可以作为同步锁进行使用(可用任何对象)
if (num > 0) {
try {
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()
+"...sale..." + num--);
} catch(InterruptedException e) {
//TODO Auto-generated catch block
e.printStackTrace();
}
}
// }
}
}
classSaleTicketThread extends Thread {
private int num = 50;
public SaleTicketThread(String name) {
super(name);
}
@Override
public void run() {
while (num > 0) {
System.out.println(getName()+ "......sale....." + num);
num--;
}
}
}
】
1.1.2 同账户同步存钱
【
publicclass Ex1 {
public static void main(String[] args){
Account account = newAccount();
new Thread(account, "小强").start();
new Thread(account, "小花").start();
}
}
classAccount implements Runnable{
private int money = 0; //存款
@Override
public void run() {
//存钱三次,每次存100元,并且在读完之后显示账户的余额
for(int i=0;i<3;i++){
saveMoney(100);
}
}
/**
* 存钱
*/
public synchronized void saveMoney(intmoney){
this.money +=money;
System.out.println(Thread.currentThread().getName()+"存入100元,账户余额为"+this.money);
try {
Thread.sleep(500);
} catch (InterruptedExceptione) {
// TODOAuto-generated catch block
e.printStackTrace();
}
}
}
】
1.1.3 火车过山洞
【
/*
* 有5辆火车要过山洞,但确保山洞同时只能有一辆火车通过(过山洞需要1秒),打印输出火车通过的顺序。
* (过山洞的顺序是不可控的,只要保证同一时间只有一辆火车能通过山洞即可)
* 提示:使用线程同步,一辆火车就是一个线程
*/
publicclass Ex2 {
public static void main(String[] args){
new Train("火车1").start();
new Train("火车2").start();
new Train("火车3").start();
new Train("火车4").start();
new Train("火车5").start();
}
}
classTrain extends Thread{
public Train(String name){
super(name);
}
@Override
public void run() {
synchronized (Train.class) {
System.out.println(getName()+"过山洞.....");
try {
Thread.sleep(1000);
} catch(InterruptedException e) {
// TODOAuto-generated catch block
e.printStackTrace();
}
}
}
}
】
1.1.4 处理单例模式中的线程同步问题
【
在懒汉式单例模式中存在线程同步问题
处理方案:采用双重判断的形式同时解决效率和线程安全的问题
】
【
publicclass Demo {
public static void main(String[] args){
new Thread() {
@Override
public void run() {
System.out.println(Singleton3.getInstance());
}
}.start();
new Thread() {
@Override
public void run() {
System.out.println(Singleton3.getInstance());
}
}.start();
}
}
// 单例模式(饿汉式)
classSingleton {
//静态字段是在类加载的时候就初始化了
private static Singleton instance = newSingleton();
// 私有化构造方法,防止外界创建对象
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
// 单例模式(懒汉式)
// 存在线程安全问题,可以通过双重判断加同步处理解决这里的线程安全问题
classSingleton2 {
private static Singleton2 instance;
private Singleton2() {
// emty
}
// 在这里,线程安全问题只会出现在第一次创建对象的时候,只要对象已经被创建完,
// 那么就不需要再对代码进行同步处理
// public static synchronizedSingleton2 getInstance(){//存在线程安全问题,可以通过双重判断加同步处理解决这里的线程安全问题
public static Singleton2 getInstance(){
if (instance == null) { //通过双重判断对象是否存在
synchronized(Singleton2.class) {//同步
if(instance == null) {
try{
Thread.sleep(100);
}catch (InterruptedException e) {
//TODO Auto-generated catch block
e.printStackTrace();
}
instance= new Singleton2();
}
}
}
return instance;
}
}
/*使用静态内部类实现单例模式
* 在类加载的时候会加载类中的成员,会初始化静态字段,将类中的成员加载到内存中(方法区)
*
*/
classSingleton3{
private Singleton3(){
}
//静态代码块只会在类加载的时候被调用一次
static{
System.out.println("外部类中的静态代码块......");
}
//静态内部类
private static class Inner{
static Singleton3 instance =new Singleton3();
static{
System.out.println("内部类中的静态代码块.....");
}
}
public static Singleton3 getInstance(){
return Inner.instance;
}
}
】
1.1.5 数字和字母的间隔输出
【
/**
* 1. 写两个线程,一个线程打印 1~52,另一个线程打印字母A-Z打印顺序为12A34B56C……5152Z(2个数字1个字母)。
提示:使用线程间的通信。
*/
publicclass Ex1 {
public static void main(String[] args){
// TODO Auto-generated methodstub
Printer printer = newPrinter();
newNumberThread(printer).start();
newLetterThread(printer).start();
}
}
//数字输出线程
classNumberThread extends Thread{
private Printer printer;
public NumberThread(Printer printer){
this.printer = printer;
}
@Override
public void run() {
for(int i=1;i<=52;i++){
printer.printNum(i);
}
}
}
//字母输出线程
classLetterThread extends Thread{
private Printer printer;
public LetterThread(Printer printer){
this.printer = printer;
}
@Override
public void run() {
for(charc='A';c<='Z';c++){
printer.printLetter(c);
}
}
}
/**
* 打印输出类
*/
classPrinter{
private boolean numOut = false; //信号量,记录数字是否已经输出
//输出数字
public synchronized void printNum(intnum){
try {
if(numOut){//如果数字已经输出,就等待输出字母
wait();//注意:wait、notify的调用者必须是当前同步代码块对应的同步锁
}
System.out.println(num); //输出数字
//如果刚刚输出的数字是偶数的话,就唤醒字母输出线程
if(num % 2 == 0){
numOut =true; //标记已经输出数字
notify(); //唤醒字母输出线程去输出字母
}
Thread.sleep(200);
} catch (InterruptedExceptione) {
// TODOAuto-generated catch block
e.printStackTrace();
}
}
//输出字母
public synchronized voidprintLetter(char c){
try {
if(!numOut){ //如果还没有输出数字,就等待数字输出
wait();
}
System.out.println(c); //输出字母
numOut = false; //标记已经输出国字母,等待输出数字
notify(); //唤醒数字输出线程去输出数字
Thread.sleep(200);
} catch (InterruptedExceptione) {
// TODOAuto-generated catch block
e.printStackTrace();
}
}
}
】
1.1.6 主子线程的循环输出
【
/**
* 子线程循环3次,接着主线程循环6次,接着又回到子线程循环3次,接着再回到主线程又循环6次,如此循环10次,请写出程序
*/
publicclass Ex2 {
static boolean flag = false; // 记录子线程是否已经输出
static Object lock = new Object();
public static void main(String[] args){
// TODO Auto-generated methodstub
// 子线程
new Thread() {
@Override
public void run() {
try {
for(int i = 1; i <= 10; i++) {
synchronized(lock) {
if(flag) {
lock.wait();
}
System.out.println("~~~~~~~~~~~~~~第" + i
+"回合~~~~~~~~");
for(int j = 1; j <= 3; j++) {
System.out.println("子线程" + j);
Thread.sleep(200);
}
flag= true;
lock.notify();
}
}
} catch(InterruptedException e) {
//TODO Auto-generated catch block
e.printStackTrace();
}
};
}.start();
// 主线程
for (int i = 1; i <= 10;i++) {
try {
synchronized(lock) {
if(!flag){
lock.wait();
}
for(int j = 1; j <= 6; j++) {
System.out.println("主线程....." + j);
Thread.sleep(200);
}
flag= false;
lock.notify();
}
} catch(InterruptedException e) {
// TODOAuto-generated catch block
e.printStackTrace();
}
}
}
}
】