多线程编程[二]

继 多线程编程[一]:http://www.cnblogs.com/wangfajun/p/6547648.html,我们开始第二篇文章啦。。。

上一篇中结尾,我们了解到,同步函数用的锁是 this ,那么我们接下来,在同步函数上加下个静态标示符static试试:

public class Test {
    public static void main(String[] args) {
        try {
            Ticket one = new Ticket();
            new Thread(one).start();
            Thread.sleep(10);
            one.flag = false;
            new Thread(one).start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
class Ticket implements Runnable{
    private static int ticket = 1000;
    boolean flag = true;
    @Override
    public void run() {
        if(flag){
            synchronized(this){
                while(true){
                    if(ticket>0){
                        System.out.println(Thread.currentThread().getName()+"同步代码块..."+ ticket--);
                    }
                }
            }
        }else{
            while(true)
                sale();
        }
    }
    public static synchronized void sale(){
        if(ticket>0){
            System.out.println(Thread.currentThread().getName()+"同步方法..."+ ticket--);
        }
    }
}
执行结果:
  ....   
  二号窗口卖票...2
  二号窗口卖票...1
  二号窗口卖票...0

好吧,又出现了0号票。线程又不安全了。思考线程安全的连个前提:

  1.必须要有两个或以上的线程

  2.必须是多个线程使用同一个锁

肯定是2没满足,那么,静态同步函数的锁对象不是this,是什么呢?

我们知道静态资源的特点:进内存的时候,内存中没有本类的对象,那么有谁?静态方法是不是由类调用的 ?类在进内存的时候,有对象吗? 有,就是那份字节码文件对象(Ticket.class),Ticket进内存,紧跟着,静态资源进内存,OK,我们来试试。。

将上面同步代码块中的this锁换成如下:

synchronized(Ticket.class){     while(true){          if(ticket>0){                System.out.println(Thread.currentThread().getName()+"同步代码块..."+ ticket--);          }     }}
执行结果:Thread-0同步代码块...5
Thread-0同步代码块...4
Thread-0同步代码块...3
Thread-0同步代码块...2
Thread-0同步代码块...1

最后一张为1号票,线程安全。

结论:静态同步函数使用的锁是该方法所在类的字节码文件对象,也就是 类名.class。

看完[一]再到这里,我们穿插一个小的知识点。单例模式,因为这个模式要学会了线程同步,才比较好讲这个知识点。

(好像刚出来的那会,面试中基本会问这个问题,或者笔试中让你写一个单利模式,有记忆吧。哈哈。。。青春一去不复返啊。。)

1饱汉模式:

class Single{
    public static final Single single = new Single();
    public static Single getInstance(){
        return single;
    }
}

初始化时,就给你创建了一个实例化对象,不会出现线程安全问题,再看看懒汉模式:

2..懒汉模式(延迟加载):

class Single{
    public static Single single = null;
    public static Single getInstance(){
        if(single == null){
            single = new Single();
        }
        return single;
    }
}

假如现在有A、B两个线程,同时访问getInstance方法时,分析下执行过程:

1.A线程进入if条件判断,发现single为null,然后线程A挂在这了,

2.B线程进来了,然后也挂这了,

3.A线程活了,继续往下执行,new了一个Single对象出来了

4.B线程也活了,往下执行又创建了一个Single对象

这样是不是线程又不安全了?没错。。。。听明白了否?

如何解决?修改代码如下:

class Single{
    public static Single single = null;
    public static synchronized Single getInstance(){
        if(single == null){
            single = new Single();
        }
        return single;
    }
}

加了一个 synchronized 来修饰,每个线程想要获得这个Single实例的时候,都要判断锁,我们发现,线程安全了。。。

不过虽然这样解决了线程安全问题,但是getInstance()方法效率比较低,如何提高效率?接着修改代码:

class Single{
    public static Single single = null;
    public static Single getInstance(){
        if(single==null){
            synchronized(Single.class){
                if(single == null){
                    single = new Single();
                }
            }
        }
        return single;
    }
}

将同步函数变成同步代码块,然后再在同步代码块外包了一层if条件判断,为什么这么做就提升了效率?假设现在有A、B.等等多个线程,再来分析下执行过程:

1.A线程进入synchronized方法块中,获得了锁,挂这了

2.B线程进入最外层if条件判断,single为null,满足,继续执行,发现锁被A线程拿着了,进不去,B线程挂这了

3.A线程活了,进入内层if条件判断,single为null,满足,new了一个Single对象出来,释放了锁,此时single已经被实例化了

4.B线程活了,进入了synchronized方法块种,内层if条件判断,不满足,执行return single.

后续的其他线程进来,判断最外层if条件判断,single都不为空了,直接返回,这样效率是不是提升了?

...回家吃饭了。。。明天继续。。

时间: 2025-01-02 04:21:23

多线程编程[二]的相关文章

Linux系统编程@多线程编程(二)

线程的操作 线程标识 线程的ID表示数据类型:pthread_t (内核中的实现是unsigned long/unsigned int/指向pthread结构的指针(不可移植)几种类型) 1.对两个线程ID进行比较 #include <pthread.h> int pthread_equal(pthread_t tid1, pthread tid2); //返回值:若相等则返回非0值,不相等返回0 2.获取自身的线程id #include <pthread.h> pthread_t

JavaSE入门学习51:多线程编程(二)

二Java多线程 (4)线程的状态 线程状态转换示意图: 线程控制的基本方法: 1)sleep()方法 可以调用Thread的静态方法: public static void sleep(long millis) throws interruptedException 使得当前线程休眠(暂时停止执行millis毫秒) 由于是静态方法,sleep()方法可以由类名直接调用 thread.sleep(...) 实例: 打印十次当前日期时间 <span style="font-size:18px

Android中的多线程编程(二)Handler的原理(附源码)

Android中Handler的原理 一.Handler的原理: 1.Handler.Looper.MessageQueue之间的关系. (1).Handler类:向MessageQueue消息队列中发送消息,接收Looper返回来的消息并处理. (2).Looper类: 存储消息队列的容器.负责接收Handler发送的消息,并直接把消息回传给Handler自己. (3).MessageQueue类:存储消息. 2.关系: (1).创建Handler对象的时候,它就会绑定到默认的线程(UI线程)

java多线程编程(二创建线程)

1.概念           因为java是完全面向对象的,所以在java中,我们说的线程,就是Thread类的一个实例对象.所以,一个线程就是一个对象,它有自己字段和方法. 2.创建线程 创建线程有2种方法:1.扩展线程父类Thread类 , 2.实现Runnable接口.2种方法是由区别的. 提示:Thread类已经实现了Runnable接口,Thread类中不但实现了run方法,也定义了更多的,全面的线程对象的操作方法,而Runnable接口中只有run这一个方法. 通过扩展Thread类

java多线程编程(二)

wait 和 sleep 区别? 1.wait可以指定时间也可以不指定,sleep必须指定时间. 2.在同步中时,对cpu的执行权和锁的处理不同.  wait:释放执行权,释放锁.  sleep:释放执行权,不释放锁.

UNIX环境编程学习笔记(27)——多线程编程(二):控制线程属性

lienhua342014-11-09 1 线程属性概括 POSIX 线程的主要属性包括 scope 属性.detach 属性.堆栈地址.堆栈大小.优先级.在头文件 pthread.h 中定义了结构体pthread_attr_t 来记录线程的属性. 在创建线程的函数pthread_create 的第二个参数 attr 就是一个pthread_attr_t结构体的指针,通过该参数,我们可以控制新创建的线程的属性.如果 atrr参数为 NULL,表示创建一个默认属性的新线程. pthread_att

Windows下多线程编程(二)

线程的分类 1.     有消息循环线程 MFC中有用户界面线程,从CWinThread派生出一个新的类作为UI线程类CUIThread,然后调用AfxBeginthread(RUNTIME_CLASS(CUIThread));启动线程.UI线程可以直接创建模态对话框,而不用担心消息循环的问题,因为UI线程默认自带消息循环. MFC非用户界面线程,不能创建模态对话框,但是可以创建非模态对话框或普通窗口,但是必须自己写消息循环. MSG msg; while(GetMessage(&msg, NU

C++多线程编程二

1. 死锁与解锁: #include <iostream> #include <thread> #include <mutex> using namespace std; //thread引用类型函数,模板,避免类型转换,尽量指针,引用 //锁住一个变量之后,尽快操作完解锁,不要再锁,否则互锁 #define COUNT 100000 mutex g_mutex1, g_mutex2;//互斥量 void add1(int *p1, int *p2) { for (in

多线程编程学习笔记——async和await(二)

接上文 多线程编程学习笔记——async和await(一) 三.   对连续的异步任务使用await操作符 本示例学习如何阅读有多个await方法方法时,程序的实际流程是怎么样的,理解await的异步调用 . 1.示例代码如下. using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Thread