Java 中常用的单例,同步,线程安全等问题

积淀点东西,这都是多线程应用比较经典的场景。

1  wait方法:

        该方法属于Object的方法,wait方法的作用是使得当前调用wait方法所在部分(代码块)的线程停止执行,并释放当前获得的调用wait所在的代码块的锁,并在其他线程调用notify或者notifyAll方法时恢复到竞争锁状态(一旦获得锁就恢复执行)。
        调用wait方法需要注意几点:
        第一点:wait被调用的时候必须在拥有锁(即synchronized修饰的)的代码块中。
        第二点:恢复执行后,从wait的下一条语句开始执行,因而wait方法总是应当在while循环中调用,以免出现恢复执行后继续执行的条件不满足却继续执行的情况。
        第三点:若wait方法参数中带时间,则除了notify和notifyAll被调用能激活处于wait状态(等待状态)的线程进入锁竞争外,在其他线程中interrupt它或者参数时间到了之后,该线程也将被激活到竞争状态。
        第四点:wait方法被调用的线程必须获得之前执行到wait时释放掉的锁重新获得才能够恢复执行。


2  notify方法和notifyAll方法:
        notify方法通知调用了wait方法,但是尚未激活的一个线程进入线程调度队列(即进入锁竞争),注意不是立即执行。并且具体是哪一个线程不能保证。另外一点就是被唤醒的这个线程一定是在等待wait所释放的锁。
        notifyAll方法则唤醒所有调用了wait方法,尚未激活的进程进入竞争队列。


3 synchronized关键字:
        第一点:synchronized用来标识一个普通方法时,表示一个线程要执行该方法,必须取得该方法所在的对象的锁。
        第二点:synchronized用来标识一个静态方法时,表示一个线程要执行该方法,必须获得该方法所在的类的类锁。
        第三点:synchronized修饰一个代码块。类似这样:synchronized(obj) { //code.... }。表示一个线程要执行该代码块,必须获得obj的锁。这样做的目的是减小锁的粒度,保证当不同块所需的锁不冲突时不用对整个对象加锁。利用零长度的byte数组对象做obj非常经济。


4 atomic action(原子操作):
        在JAVA中,以下两点操作是原子操作。但是c和c++中并不如此。
        第一点:对引用变量和除了long和double之外的原始数据类型变量进行读写。
        第二点:对所有声明为volatile的变量(包括long和double)的读写。
        另外:在java.util.concurrent和java.util.concurrent.atomic包中提供了一些不依赖于同步机制的线程安全的类和方法。


5 单态的编写:

       这玩意有很多种写法,这个是最靠谱的,能充分体现某时某刻某一个对象的实例。

public class Singleton1 {

private static Singleton1 instance; // 声明静态的单例对象的变量

private Singleton1() {

} // 私有构造方法

public static Singleton1 getSingle() { // 外部通过此方法可以获取对象
if (instance == null) {
synchronized (Singleton1.class) { // 保证了同一时间只能只能有一个对象访问此同步块
if (instance == null) {
instance = new Singleton1();
}
}
}
return instance; // 返回创建好的对象
}
}


6. 生产者和消费者

     这个玩意是经常出现在多线程问题时候考虑的问题,还是比较烦人的,贴点代码记录一下。

/**
 * 
 * 生产者和消费者模式实现
 *
 */
public class ProducerConsumer {


    public static void main(String []args){
        Container con = new Container();
        Producer p = new Producer(con);
        Consumer c = new Consumer(con);        
        new Thread(p).start();
        new Thread(c).start();
    }
}


/**
 * 
 * 产品类
 *
 */
class Goods{

    int id;
    
    public Goods(int id){
        this.id=id;
    }


    public String toString(){
        return "商品"+this.id;
    }
}




/**
 * 
 *定义一个可以装下1000个产品的容器
 *
 */
class Container{

//容器采用栈,先进后出
    private int index = 0;
    
    Goods[] goods = new Goods[1000];


    /**
     * 往容器里放入产品
     * @param good
     */
    public synchronized void put(Goods good){
        while(index==goods.length){//当容器满了,生产者等待
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        goods[index]=good;
        index++;
        notifyAll();//当生产者放入商品后通知消费者
    }


    /**
     * 从容器里取出产品(可指定取出什么产品,即传入一个goods对象,也可以简要的单纯让容器的产品件数减少就是)
     * @return
     */
    public synchronized Goods get(){
        while(index==0){//当容器内没有商品是等待
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        index--;
        notifyAll();//当消费者消费了商品后通知生产者
        return goods[index];
    }
}




/**
 * 
 *生产者实现,里面有一个容器,是公共的存储产品的容器.
 *
 */
class Producer implements Runnable{


    Container con = new Container();
    
    public Producer(Container con){
        this.con=con;
    }
    
    public void run(){
        for(int i=0; i<20; i++){
            Goods good = new Goods(i);
            con.put(good);
            System.out.println("生产者产出了产品:"+good.toString());
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}




/**
 * 
 * 消费者实现,里面有一个容器,是公共的存储产品的容器.
 *
 */
class Consumer implements Runnable{


    Container con = new Container();


public Consumer(Container con){
        this.con=con;
    }


    public void run(){
        for(int i=0; i<20; i++){
            Goods good=con.get();
            System.out.println("消费者消费了产品:"+good.toString());
            try {
                Thread.sleep(1000); //休息 1 秒钟.
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


7 售票系统

   这个是典型的多线程问题,对于固定的票数,在不同的售票口,都要对这个总票数进行操作。

public class TicketOffice_Thread {

int ticket_count = 0;

public TicketOffice_Thread() {

}

public TicketOffice_Thread(int ticket_count) {
super();
this.ticket_count = ticket_count;
}

public int getTicket_count(){
return ticket_count;
}

public void setTicket_count(int ticket_count){
this.ticket_count = ticket_count; 
}

/**
* sale tickets.
*/
public void sale_tickets0(){
if(ticket_count-- > 0){  
            System.out.println("sale succeed ,there are : " + ticket_count +" tickets on sale .");  
        }else{  
            System.out.println("there are no tickets !");  
        }
}

/**
* sale tickets.
*/
Object obj = new Object();
public void sale_tickets1(){
synchronized (obj) {
if(ticket_count-- > 0){  
System.out.println("sale succeed ,there are : " + ticket_count +" tickets on sale .");  
}else{  
System.out.println("there are no tickets !");  
}

}

/**
* sale 
* @param ticket_count
*/
public void sale0(int ticket_count){
final TicketOffice_Thread office = new TicketOffice_Thread(ticket_count);
for (int i = 0; i <5 ; i++) {
new Thread(new Runnable() {
@Override
public void run() {
while (office.getTicket_count()>0) {
office.sale_tickets0();
}
}
}).start();
}
}


/**
* sale 
* @param ticket_count
*/

public void sale1(int ticket_count){
final TicketOffice_Thread office = new TicketOffice_Thread(ticket_count);
for (int i = 0; i <5 ; i++) {
new Thread(new Runnable() {
@Override
public void run() {
while (office.getTicket_count()>0) {
office.sale_tickets1();
}
}
}).start();
}
}

public static void main(String[] args) {
TicketOffice_Thread office = new TicketOffice_Thread();
int count = 100;
office.sale0(count);  //无序的
System.out.println("-------------------------------------------------------------------");
office.sale1(count);  //排序的
}
}


8 模拟银行取钱

     这个应用场景还不像是ATM,只要早上工作人员放入一定的money之后,取钱的人就可以一个接一个的排队去取;但是在银行中,某一天银行的钱是一定的,但是在各个不同的窗口,取钱的人数是不同的,同一时间,可能有不同的人都在对某个银行账面上的钱进行存储或者取出,看程序:

public class User implements Runnable {

private static Account account = new Account();

private final int id;

User(int i) {
id = i;
}

public void run() {
int tempMoney = 1;
account.cost("ren", tempMoney*4);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.save("ren", tempMoney*2);
System.out.println("--------线程" + id + "完毕--------剩余总钱数:"+Account.get("ren")+"元");

/*
//一次提取,一次存储金钱.
synchronized (Account.class) {
int tempMoney = 1;
account.load("ren", tempMoney);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.save("ren", tempMoney*2);
System.out.println("--------线程" + id + "完毕--------剩余总钱数:"+Account.get("ren")+"元");
}
*/

}

/**
* test it .
* @param args
*/
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(new User(i)).start();
}
}
}


/**
 * 在存钱和取钱的方法实现中,
 * 使用了 wait 和notify 来实现同步,确保多账户中money的使用特性
 *  
 * @author bruce.
 *
 */
public class Account {

private static HashMap<String, Integer> totalMoney = new HashMap<String, Integer>();

private static long times = 0;

static {
totalMoney.put("ren", 10);
}


/**
* 往账户里面存钱 .
* @param name
* @param num
*/
public synchronized void save(String name, int num) {
long tempTime = times++;
System.out.println("第 " + tempTime + " 次存储" + num + "元之前" + name+ "的余额为:" + totalMoney.get(name));
totalMoney.put(name, totalMoney.get(name) + num);
this.notify();
System.out.println("第 " + tempTime + " 次存储" + num + "元之后" + name+ "的余额为:" + totalMoney.get(name));
}

public static int get(String name) {
return totalMoney.get(name);
}


/**
* 注意wait的用法,必须在loop中,必须在拥有锁的代码块中。
*  前者是当被notify的时候要重新进行条件判断,后者是为了释放锁。
* @param name
* @param num
*/
public synchronized void cost(String name, int num) {
long tempTime = times++;
System.out.println("第 " + tempTime + " 次提取" + num + "元之前" + name + "的余额为:" + totalMoney.get(name));
try {
while (totalMoney.get(name) < num) {
System.out.println("第 " + tempTime + " 次提取" + "余额"+ totalMoney.get(name) + "不足,开始等待wait。");
this.wait();
System.out.println("第 " + tempTime + " 次提取操作被唤醒");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
totalMoney.put(name, totalMoney.get(name) - num);
System.out.println("第 " + tempTime + " 次提取" + num + "元之后" + name + "的余额为:" + totalMoney.get(name));
}
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值