码迷,mamicode.com
首页 > 编程语言 > 详细

JavaSE:多线程总结(Thread)

时间:2015-03-11 00:45:28      阅读:230      评论:0      收藏:0      [点我收藏+]

标签:javase   记录   

今天来进行JavaSE多线程的总结:

先从几个概念说起:
程序:是一组指令的集合,一段静态的代码。
进程:每一个运行的程序,都会对应一个进程。
线程:一个进程中包含一个或多个线程,如果包含多个线程,那么这个程 序就是支持多线程的。

多线程的优点:
①提高了计算机对CPU的利用率。
②提高了程序的响应速度

线程的创建,运行
线程的创建:
①继承Thread类:必须重写run()方法
②实现Runnable接口:必须实现run()方法
通常将run()的主体称为线程体
线程的启动:
通过start()方法来启动一个线程,不是通过调用run()方法来启动,同时start()方法也不是运行一个线程。

来看第一种方式的一个经典例子,多窗口售票问题。
这里我们假设有三个窗口,100张票同时出售。
窗口类:

public class Window extends Thread{
    static int tickets = 100;
    public Window(String name){
        super(name);//Thread父类中有这个构造器
    }
    public void run(){
        while(true){
            if(tickets > 0){
                System.out.println(Thread.currentThread().getName() + "售出一张票,剩余" + tickets-- + "张票");
            }else{
                break;
            }   
        }
    }
}

测试:

public static void main(String[] args){
    Window w1 = new Window("窗口1");
    Window w2 = new Window("窗口2");
    Window w3 = new Window("窗口3");

    w1.start();
    w2.start();
    w3.start();
}

我们看到了三个窗口售出了100张票,需要注意的是,tickets为什么要声明成静态的?其余的问题我们暂且不说。

第二种方式:

public class Window implements Runnable{
    int ticket = 100;
    public void run(){
        //具体执行和上面一样
    }
}

来看测试:

public static void main(String[] args){
    Window w = new Window();
    Thread w1 = new Thread(w);
    Thread w2 = new Thread(w);
    Thread w3 = new Thread(w);
    w1.start();
    w2.start();
    w3.start();
}

看到的运行结果和上面没什么两样,但是我这里要说明一点:这里的ticket为什么不是静态的?

我来解释一下,通过继承的方式,创建了三个Window对象,而通过实现的方式,创建了一个Window对象。对于继承方式,三个对象要共享一个数据,只能将他们声明为类变量。而实现的方式,值创建了一个对象,所以这个对象不需要声明成静态的。

通过上面的对比,我们发现通过实现的方式更有优点,具体表现在:
①多个线程可共享同一个接口子类的对象,共享对象里的属性
②避免了单继承的局限性
③不需要将共享数据声明为静态的,就可以实现数据的共享,减少了内存的开销。

接下来来说Thread类中的常用方法:
上面已经说过两个:
run() 方法体为线程体
start():启动一个线程
currentThread():当前线程, static
getName():得到线程名
setName():设置线程名
yield():线程让步,调用此方法的线程放弃当前CPU的执行权,该线程可继续竞争CPU执行权
join():调用join的线程,优先获取CPU资源,直到该线程执行完毕,其他线程才可以竞争到CPU的执行权
sleep():调用该方法的线程放弃当前CPU的执行权,转换成一种睡眠状态,等睡眠时间结束,重新竞争CPU资源
isAlive():判断一个线程是否存活着。

线程通信:
wait(); 使线程进入等待状态
notify(); 唤醒进入等待状态
notifyAll(); 唤醒所有进入等待转态的线程

线程优先级:
getPriority():获取线程的优先级
setPriority():设置线程的优先级

线程的调度:
同优先级的线程组成队列,先来先服务,使用时间片策略
对于高优先级的线程,使用优先调度的抢占式策略

守护线程:通过在start()前调用,thread.setDaemon(true),可以将一个线程设置为守护线程。当JVM中只剩守护线程时,JVM将自动退出。
垃圾回收就是守护线程

线程的生命周期:
技术分享

注意理解这张图,后面遇到什么问题,可以返回来,看看这张图。

线程同步:
来解决上面出现的重票和错票问题。
为什么会出现这样的错误呢?
因为:多个线程在操作同一对象的共享数据时,一个线程只执行了其中的一部分语句,还没有执行完,而另一个线程就加进来了,导致数据共享错误。
解决办法:多个线程在操作同一对象的共享数据时,让一个线程执行完,再执行下一个线程

线程同步:
①同步代码块
将需要同步的代码用synchronized包起来

synchronized(同步监视器/锁){
    //操作共享数据的代码
}

②同步方法
将方法的声明处加上synchronized关键字

public synchronized void show(){
    //需要同步的代码
}

这里涉及到一个锁的同步监视器,即锁,我们来看第一种,这里的这个锁可以是任意的对象来充当,但是必须保证这把锁是唯一的。对于实现Runnable接口的类来说,可以使用this来充当,因为就创建了一个对象;对于继承Thread类来说,不可使用this来充当,因为创建了三个对象,每个对象握各自的锁,前面所说的问题都会出现,但是我们也不需要创建静态的类对象来充当这把锁,我们可以使用 当前类.class 来充当这把锁,不管是实现还是继承都可以使用,可以是说它是一个万能的锁。
再来看第二种:没有发现锁?其实它默认的是当前对象充当的锁

对于静态的同步方法来说,它的锁是当前的类
对于非静态的同步方法来说,它的锁是当前对象

还有一种方式也能实现,Lock接口

Lock lock = new ReentrantLock();
lock.lock();
//需要同步的代码
lock.unlock();//需要手动的释放锁

使用线程同步当然就使效率降低了。

单例设计懒汉模式:
饿汉模式:

public class Singleton{
    private Singleton(){
    }
    private static Singleton instance = new Singleton();
    public static Singleton getInstance(){
        return instance;
    }
}

懒汉模式(使用线程同步):

public class Singleton{
    privata Singleton(){
    }
    private static Singleton instance = null;
    public Singleton getInstance(){
        if(instance == null){
            synchronized(Singleton.class){
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

在线程同步的过程中也会发生死锁的问题:
死锁:不同的线程分别占着对方的资源不放弃,都等待对方放弃自己需要的资源,就形成了死锁。

public class DeadLock{
    static StringBuffer sb1 = new StringBuffer();
    static StringBuffer sb2 = new StringBuffer();
    public static void main(String[] args){
        new Thread(){
            synchronized(sb1){
                Thread.currentThread().sleep(10);
                sb1.append("A");
                synchronized(sb2){
                    sb2.append("B");
                }
            }
        }.start();

        new Thread(){
            synchronized(sb2){
                Thread.currentThread().sleep(10);
                sb1.append("C");
                synchronized(sb1){
                    sb2.append("D");
                }
            }
        }.start();
    }
}

这样就发生了死锁。使用Thread.currentThread().sleep(10);只不过是将问题放大了。

在使用的过程中,应该尽量避免死锁,减少同步资源的定义。

线程通信:
wait()
notify()
notifyAll()

使两个线程交替打印1-100之间的数

public class Print implements Runnable{
    int num = 1;

    public void run() {
        while (true) {
            synchronized (this) {
                notify();
                if (num <= 100) {
                    System.out.println(Thread.currentThread().getName() + ":"
                            + num);
                    num++;
                } else {
                    break;
                }
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
    Print p = new Print();
    new Thead(p).start();
    new Thead(p).start();

明天补充消费者生产者线程。

JavaSE:多线程总结(Thread)

标签:javase   记录   

原文地址:http://blog.csdn.net/sloverpeng/article/details/44186845

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!