Java多线程之线程安全与同步实例

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();

}

}

}

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,602评论 18 399
  • 本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。 首先讲...
    李欣阳阅读 2,444评论 1 15
  • Java多线程学习 [-] 一扩展javalangThread类 二实现javalangRunnable接口 三T...
    影驰阅读 2,952评论 1 18
  • 我发现,看完的书,读过的文章过两天就忘的一干二净了,那么读书的意义在哪里
    南冠客阅读 202评论 0 0
  • 大家好,我是大因,我又要开始胡说八道了. 最近几天满世界都是某对明星夫妻出轨的新闻,作为一个两耳不闻窗外事,一心只...
    因爱4inlove阅读 222评论 0 0