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

Java线程同步与死锁、生产者消费者模式以及任务调度等

时间:2015-05-28 21:37:02      阅读:215      评论:0      收藏:0      [点我收藏+]

标签:线程同步   生产者消费者模式   任务调度   死锁   

一、Thread类基本信息方法

技术分享

package Threadinfo;

public class MyThread implements Runnable{
    private boolean flag = true;
    private int num = 0;
    @Override
    public void run() {
        while(flag)
        {
            System.out.println(Thread.currentThread().getName()+"-->"+num++);
        }
    }

    public void stop()
    {
        this.flag = !this.flag;
    }
}
package Threadinfo;

/**
 * Thread.currendThread()  当前线程
 * setName()   设置名称
 * getName()   获取名称
 * isAlive()   判断状态
 */
@SuppressWarnings("all")
public class Demo01 {
    public static void main(String[] args) throws InterruptedException {
        MyThread it = new MyThread();
        //Thread(Runnable target, String name) 如果不取名字,将是一个自动编号的名称。
        Thread proxy = new Thread(it,"挨踢");//(自定义线程,名字)
        proxy.setName("test");

        System.out.println(proxy.getName());//test

        //main线程
        System.out.println(Thread.currentThread().getName());

        proxy.start();
        System.out.println("启动后的状态:"+proxy.isAlive());//true

        proxy.sleep(5);
        it.stop();
        proxy.sleep(5);
        System.out.println("停止后的状态:"+proxy.isAlive());//false
    }
}
package Threadinfo;

/**
 * 优先级:代表概率,不是绝对的优先级,不代表绝对的先后顺序
 * MAX_PRIORITY   10
 * NORM_PRIORITY  5(默认)
 * MIN_PRIORITY  1
 * setPriority()设置优先级
 * getPriority()
 */
public class Demo02 {
    public static void main(String[] args) throws InterruptedException {
        MyThread it1 = new MyThread();
        Thread p1 = new Thread(it1,"挨踢1");//(自定义线程,名字)
        MyThread it2 = new MyThread();
        Thread p2 = new Thread(it2,"挨踢2");//(自定义线程,名字)
        //没有设置优先级就是普通(默认)优先级
        p1.setPriority(Thread.MIN_PRIORITY);//设置优先级
        p2.setPriority(Thread.MAX_PRIORITY);
        p1.start();
        p2.start(); 
        Thread.sleep(100);
        it1.stop();
        it2.stop();  
    }
}

二、线程同步

由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问。由于我们可以通过private关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是synchronized关键字,它包括两种用法:synchronized方法和synchronized块,同步块锁定的是引用类型this类.class

同步:并发 多个线程访问同一份资源确保资源安全。–>线程安全

1、同步方法与同步块

Test1   //线程不安全
Test2   //线程安全 锁定正确 
test3   //线程安全 锁定正确
test4   //线程安全 锁定范围(过小)不正确  可能出现问题
test5   //线程不安全 锁定资源不正确
test6   //线程安全 锁定不正确
package Threadsyn;

public class Demo01 {
    public static void main(String[] args) {
        //真实角色
        Web12306 web = new Web12306();
        //代理
        Thread t1 = new Thread(web,"111");
        Thread t2 = new Thread(web,"222");
        Thread t3 = new Thread(web,"333");
        //启动线程
        t1.start();
        t2.start();
        t3.start();
    }
}

class Web12306 implements Runnable{
    private int num = 20;
    private boolean flag = true;

    @Override
    public void run() {
        while(flag)
        {
            test5();
        }
    }   

    //线程不安全
    public void test1()
    {
        if(num<=0)
        {
            flag = false;//跳出循环
            return;
        }
        try {
            Thread.sleep(500);//模拟延时
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"抢到了"+num--);       
    }

    //线程安全(效率降低了) 只能被一个线程进行访问
    public synchronized void test2()
    {
        if(num<=0)
        {
            flag = false;//跳出循环
            return;
        }
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"抢到了"+num--);
    }

    //线程安全 锁定正确
    public void test3()
    {
        //a   b   c
        synchronized (this) {
            if(num<=0)
            {
                flag = false;//跳出循环
                return;
            }
            try {
                Thread.sleep(500);//模拟延时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"抢到了"+num--);           
        }
    }

    //线程安全 锁定范围(过小)不正确  可能出现问题
    public void test4()
    {
        //a   b   c
        synchronized (this) {
            if(num<=0)
            {
                flag = false;//跳出循环
                return;
            }
        }
        try {
            Thread.sleep(500);//模拟延时
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"抢到了"+num--);   
    }

    //线程不安全 锁定资源不正确
    public void test5()
    {
        //a   b   c
        synchronized ((Integer)num) {
            if(num<=0)
            {
                flag = false;//跳出循环
                return;
            }
            try {
                Thread.sleep(500);//模拟延时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"抢到了"+num--);           
        }   
    }
    //线程安全 锁定不正确
    public void test6()
    {   
        if(num<=0)
        {
            flag = false;//跳出循环
            return;
        }
        //a   b   c
        synchronized (this) {       
            try {
                Thread.sleep(500);//模拟延时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"抢到了"+num--);           
        }
    }
}

我们常用的单例设计模式懒汉式就在进行多线程时,就使用了线程同步。

package Threadsyn;

/**
 * 单例设计模式:确保一个类只有一个对象
 */
public class Demo02 {
    public static void main(String[] args) {
        Jvm jvm1 = Jvm.getInstance();
        Jvm jvm2 = Jvm.getInstance();   
        //单线程jvm1与jvm2地址一样,如果是多线程可能就不一样了,存在延时。
        System.out.println(jvm1);
        System.out.println(jvm2);
        System.out.println("------------");
        JvmThread thread1 = new JvmThread(2000);
        JvmThread thread2 = new JvmThread(2000);
        thread1.start();
        thread2.start();
    }
}
class JvmThread extends Thread{
    private long time;
    public JvmThread() {
    }   
    public JvmThread(long time) {
        this.time = time;
    }
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()
                +"-->创建:"+Jvm.getInstance1(time));
    }
}

/**
 * 单例设计模式
 * 确保一个类只有一个对象
 * 懒汉式  双重检查
 * 1、构造器私有化,避免外部直接创建对象
 * 2、声明一个私有的静态变量
 * 3、创建一个对外的公共的静态方法访问该变量,如果变量没有对象,创建该对象。
 */
class Jvm
{
    //声明一个私有的静态变量  
    private static Jvm instance = null;

    //构造器私有化,避免外部直接创建对象
    private Jvm(){      
    }

    //创建一个对外的公共的静态方法访问该变量,如果变量没有对象,创建该对象
    public static Jvm getInstance()
    {
        if(null==instance){
            instance = new Jvm();   
        }
        return instance;
    }

    //用多线程是访问的地址可能不一样  没有同步不需要等待,可能发生错误。
    public static Jvm getInstance1(long time)
    {
        if(null==instance){
            try {
                Thread.sleep(time);//延时,放大错误发生的概率
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            instance = new Jvm();   
        }
        return instance;
    }

    //创建一个对外的公共的静态方法访问该变量,如果变量没有对象,创建该对象
    //同步方法,这样就可以避免错误。
    public static synchronized Jvm getInstance2(long time)
    {
        if(null==instance)
        {
            try {
                Thread.sleep(time);//延时,放大错误发生的概率
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            instance = new Jvm();   
        }
        return instance;
    }

    //创建一个对外的公共的静态方法访问该变量,如果变量没有对象,创建该对象
    public static Jvm getInstance3(long time)
    {
        //a b -->效率不高  c 存在对象也需要等待(同getInstance2方法效率差不多)
        synchronized(Jvm.class)
        {
            if(null==instance)
            {
                try {
                    Thread.sleep(time);//延时,放大错误发生的概率
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                instance = new Jvm();           
            }
        }       
        return instance;
    }

    //创建一个对外的公共的静态方法访问该变量,如果变量没有对象,创建该对象
    public static Jvm getInstance4(long time)
    {
        //c d e -->提高效率  提供已经存在对象的访问效率
        if(null==instance)
        {
            //a b
            synchronized (Jvm.class) { //第一个创建了,之后以后就可以直接使用。
                if(null==instance){
                    try {
                        Thread.sleep(time);//延时,放大错误发生的概率
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    instance = new Jvm();
                }
            }           
        }   
        return instance;
    }
}

2、单例设计模式

package Threadsyn;

/**
 * 单例设计模式
 * 确保一个类只有一个对象  //外部只能使用对象,不能创建对象。
 * 懒汉式
 * 1、构造器私有化
 * 2、声明私有的静态属性
 * 3、对外提供访问属性的静态方法,确保该对象存在
 */
public class MyJvm {
    private static MyJvm instance = null;
    private MyJvm(){
    }   

    public static MyJvm getInstance()
    {
        if(null==instance)//提高效率
        {
            synchronized (MyJvm.class) {
                if(null==instance)//安全
                {
                    instance = new MyJvm();
                }
            }
        }
        return instance;
    }
}

/**
 * 饿汉式
 * 1、构造私有化
 * 2、声明私有的静态属性,同时创建对象
 * 3、对外提供访问属性的静态方法,确保该对象存在
 */
class MyJvm2 {
    private static MyJvm2 instance = new MyJvm2();//类加载时创建
    private MyJvm2(){       
    }

    public static MyJvm2 getInstance(){ 
        return instance;
    }
}

/**
 * 使用了内部类,类在使用的时候才加载   延缓了加载时间  提高效率
 */
class MyJvm3 {

    private MyJvm3(){       
    }

    public static MyJvm3 getInstance(){ 
        return JVMholder.instance;
    }

    private static class JVMholder{
        private static MyJvm3 instance = new MyJvm3();
    }
}

本博主更加详细的单例设计模式
http://blog.csdn.net/scgaliguodong123_/article/details/42550599

三、线程死锁

package Threadsyn;

/**
 * 模拟死锁
 * 过多的同步方法可能造成死锁 
 * 一个把商品的资源获取了,一个把钱的资源获取了
 */

public class Demo03 {
    public static void main(String[] args)
    {
        //同一份资源,不同的线程
        Object g = new Object();
        Object m = new Object();

        //一手给钱
        Test1 t1 = new Test1(g,m);
        //多态不能使用新增方法
        //Runnable proxy = new Thread(t1);
        Thread proxy = new Thread(t1);
        proxy.start();

        //一手给货
        Test2  t2 = new Test2(g,m);
        Thread proxy2 = new Thread(t2);
        proxy2.start();
    }   
}

class Test1 implements Runnable
{
    Object goods ;
    Object money ;

    public Test1(Object goods, Object money) {
        super();
        this.goods = goods;
        this.money = money;
    }

    @Override
    public void run() {
        while(true)
        {
            test();
        }
    }

    public void test()
    {
        synchronized (goods) {
            try {
                Thread.sleep(500);//延时,加大发生错误的可能性。
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized(money){    
            }   
        }
        System.out.println("一手给货");
    }
}


class Test2 implements Runnable
{
    Object goods ;
    Object money ;

    public Test2(Object goods, Object money) {
        super();
        this.goods = goods;
        this.money = money;
    }
    @Override
    public void run() {
        while(true)
        {
            test();
        }
    }

    public void test()
    {
        synchronized (money) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized(goods)
            {       
            }       
        }
        System.out.println("一手给钱");
    }
}

四、生产者消费者模式

package Threadprocon;

/**
 * 一个场景,共同的资源 
 * 生产者消费者模式 信号灯法
 * wait(): 等待     释放锁  
 * sleep:休眠   不释放锁
 * notify()/notifyAll():唤醒
 *  notify、wait要与synchronized一起使用才起作用
 */

public class Movie {
    private String pic;
    //信号灯
    //flag-->T 生产者生产,消费等待,生产完成后,通知消费
    //flag-->F 消费者消费,生产者等待,消费完后,通知生产
    private boolean flag = true;

    /**
     * 播放
     * @param pic
     */
    public synchronized void play(String pic)
    {
        if(!flag)//生产者等待
        {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }       
        //开始生产
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //生产完毕
        this.pic = pic;
        System.out.println("生产电影:"+pic);
        //通知消费
        this.notify();
        //生产者停下
        this.flag = false;
    }

    public synchronized void watch()
    {
        if(flag)//消费者等待
        {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //开始消费
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("看电影:"+pic);
        //消费完毕
        //通知生产
        this.notify();
        //消费停止
        this.flag = true;
    }
}
package Threadprocon;

/**
 * 生产者
 * @author liguodong
 */
public class Player implements Runnable{

    private Movie m;    

    public Player(Movie m) {
        super();
        this.m = m;
    }

    @Override
    public void run() {
        for(int i=0;i<20;i++){
            if(0==i%2){
                m.play("何以笙箫默");
            }else{
                m.play("功夫之王");
            }
        }
    }
}
package Threadprocon;

/**
 * 消费者
 * @author liguodong
 */
public class Watcher implements Runnable{

    private Movie m;

    public Watcher(Movie m) {
        super();
        this.m = m;
    }

    @Override
    public void run() {
        for(int i=0;i<20;i++){
            m.watch();
        }
    }
}
package Threadprocon;

public class Demo01 {
    public static void main(String[] args) {
        //共同的资源
        Movie m = new Movie();

        //多线程
        Player p= new Player(m);
        Watcher w = new Watcher(m);

        new Thread(p).start();
        new Thread(w).start();

    }
}

运行结果:

生产电影:何以笙箫默
看电影:何以笙箫默
生产电影:功夫之王
看电影:功夫之王

五、任务调度

1、Timer
使用 Timer 实现任务调度的核心类是 Timer 和 TimerTask。其中 Timer 负责设定 TimerTask 的起始与间隔执行时间。
java.util.Timer定时器类—- Timer本身就是一个线程,只是这个线程是用来调用其他线程的
java.util.TimerTask任务类 —-而TimerTask类是一个抽象类,该类实现了Runnable接口,该类具备多线程的能力。
在这种实中现方式中,Timer类实现的是类似闹钟的功能,也就是定时或者每隔一定时间触发一次线程。然后通过继承TimerTask使该类获得多线程的能力,将需要多线程执行的代码书写在run方法内部,然后通过Timer类启动线程的执行。
在实际使用时,一个Timer可以启动任意多个TimerTask实现的线程,但是多个线程之间会存在阻塞。如果多个线程之间需要完全独立运行的话,最好还是一个Timer启动一个TimerTask实现。

Timer 的设计核心是一个 TaskQueue 和一个 TaskThread。Timer 将接收到的任务丢到自己的 TaskQueue 中,用于定时器线程共享,定时生产任务,通过各种调度,定时执行任务,当他们过时的时候将他们从队列中删除。TimerThread 在创建 Timer 时会启动成为一个守护线程。这个线程会轮询所有任务,找到一个最近要执行的任务,然后休眠,当到达最近要执行任务的开始时间点,TimerThread 被唤醒并执行该任务。之后 TimerThread 更新最近一个要执行的任务,继续休眠。
Timer的优点在于简单易用,但由于所有任务都是由同一个线程来调度,因此所有任务都是串行执行的,同一时间只能有一个任务在执行,前一个任务的延迟或异常都将会影响到之后的任务。

package Threadschedule;

/**
 * 了解任务调度
 * Timer()
 * 
 *  运行一次
 *  schedule(TimerTask task, Date time)   
 * 
 *  起始时间开始执行,以后每个多少秒执行一次。   
 *  schedule(TimerTask task, Date firstTime, long period)
 */
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class Demo01 {
    public static void main(String[] args) {
        Timer timer = new Timer();//计数器
        //schedule(TimerTask task, Date firstTime, long period) 
        timer.schedule(new TimerTask(){ 
            int iNum=1;
            @Override
            public void run() {

                System.out.println("女神来啦,屌丝"+(iNum++)+"号就位。");
            }
        },
        new Date(System.currentTimeMillis()+1000),
        1000);
    }
}

运行结果:

女神来啦,屌丝1号就位。
女神来啦,屌丝2号就位。
女神来啦,屌丝3号就位。
女神来啦,屌丝4号就位。

2、ScheduledExecutor
由于 Timer存在的缺陷,Java 5 推出了基于线程池设计的 ScheduledExecutor
设计思想是,每一个被调度的任务都会由线程池中一个线程去执行,因此任务是并发执行的,相互之间不会受到干扰。需要注意的是,只有当任务的执行时间到来时,ScheduedExecutor 才会真正启动一个线程,其余时ScheduledExecutor 都是在轮询任务的状态。

package Threadschedule;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Demo02 implements Runnable {
    private String jobName = "";

    public Demo02(String jobName) {
        super();
        this.jobName = jobName;
    }

    @Override
    public void run() {
        System.out.println("execute " + jobName);
    }

    public static void main(String[] args) {
        //创建一个线程池
        ScheduledExecutorService service = Executors.newScheduledThreadPool(10);

        long initialDelay1 = 1;
        long period1 = 1;
        // 从现在开始1秒钟之后,每隔1秒钟执行一次job1   时间固定
        service.scheduleAtFixedRate(
                new Demo02("job1"), initialDelay1,
                period1, TimeUnit.SECONDS);//TimeUnit.SECONDS表示以秒为单位

        long initialDelay2 = 2;
        long delay2 = 2;
        // 从现在开始2秒钟之后,每隔2秒钟执行一次job2   时间不固定
        service.scheduleWithFixedDelay(
                new Demo02("job2"), initialDelay2,
                delay2, TimeUnit.SECONDS);
    }
}

运行结果:

execute job1
execute job1
execute job2
execute job1
execute job1
execute job2

以上用到了ScheduledExecutorService 中两种最常用的调度方法ScheduleAtFixedRateScheduleWithFixedDelay
ScheduleAtFixedRate 每次执行时间为上一次任务开始起向后推一个时间间隔,即每次执行时间为 :initialDelay, initialDelay+period, initialDelay+2*period, …
ScheduleWithFixedDelay 每次执行时间为上一次任务结束起向后推一个时间间隔,即每次执行时间为:initialDelay, initialDelay+executeTime+delay, initialDelay+2*executeTime+2*delay
由此可见,ScheduleAtFixedRate 是基于固定时间间隔进行任务调度,ScheduleWithFixedDelay 取决于每次任务执行的时间长短,是基于不固定时间间隔进行任务调度。

当然除了以上两种任务调度之外,还有其他的任务调度方式,这里并没有阐述,如开源工具包 Quartz、开源工具包 JCronTab。
对于简单的基于起始时间点与时间间隔的任务调度,使用 Timer 就足够了;
如果需要同时调度多个任务,基于线程池的 ScheduledTimer 是更为合适的选择;
当任务调度的策略复杂到难以凭借起始时间点与时间间隔来描述时,Quartz 与 JCronTab 则体现出它们的优势。
熟悉 Unix/Linux 的开发人员更倾向于 JCronTab,且 JCronTab 更适合与 Web 应用服务器相结合。
Quartz 的 Trigger 与 Job 松耦合设计使其更适用于 Job 与 Trigger 的多对多应用场景。

Java线程同步与死锁、生产者消费者模式以及任务调度等

标签:线程同步   生产者消费者模式   任务调度   死锁   

原文地址:http://blog.csdn.net/scgaliguodong123_/article/details/46117279

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