Java程序设计学习笔记(五) — 多线程

 

时间:2016-4-15 09:56

 

——多线程(还有多核编程)

 

    1、进程
        进程是一个正在执行中的程序。
        每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。
        多任务的理解:
                计算机可以运行多个任务,哪个任务运行,哪个任务就占用CPU资源。 
    2、线程(例:Thunder)
        线程就是进程中的一个独立的控制单元。
        线程是对进程的进一步分化。 
        线程控制着进程的执行。
        一个进程中至少有一个线程。
        举例:
            JVM启动的时候会有一个java.exe进程,该进程中至少有一个线程负责Java程序的执行,而这个线程运行的代码存在于main方
            法中,该线程称为主线程。其实JVM启动时不止一个线程,还有负责垃圾回收机制的线程。
    3、多线程存在的意义
            提高效率
    4、线程的创建方式
        通过对Java API的查找,Java已经提供了对线程这类事物的描述,就是Thread类。
        创建线程的第一种方式:继承Thread类。
        步骤:
            1、定义类继承Thread。
            2、复写Thread类中的run方法。
                目的是将自定义的代码存储在run()方法中,让线程执行指定的代码。
            3、调用线程的start方法。
                该方法有两个作用:启动线程,调用run方法。只有start才能调用线程。
                如果直接调用run方法,则为主线程调用。线程创建了,并没有执行。
                run仅仅是封装线程代码。
    为什么要覆盖run方法?
        Thread类用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码,该存储功能就是run方法,
        也就是说Thread类中的run方法,用于存储线程要运行的代码,而主线程运行的代码存放在main方法中。
 
    当主线程结束后,只要还有线程在运行,那么进程就不停止。
 
        在下面的例子中main为主线程。
 
    class thread extends Thread
    {
        public void run()
        {
            for(int i = 0; i<20; i++)
                System.out.println("thread--"+i);
        }
    }
 
    public class ThreadDemo1 {
        public static void main(String[] args)
        {
            thread t = new thread();
            t.start();
            //t.run(); 
            for(int i = 0; i<20; i++)
            {
                System.out.println("main--"+i);
            }
        }
    }
——————————输出结果为
    main--0
    thread--0
    main--1
    thread--1
    main--2
    thread--2
    main--3
    thread--3
    main--4
    thread--4
    main--5
    main--6
    thread--5
    main--7
    thread--6
    main--8
    thread--7
    main--9
    thread--8
    thread--9
    main--10
    main--11
    main--12
    main--13
    thread--10
    thread--11
    thread--12
    thread--13
    main--14
    thread--14
    main--15
    thread--15
    main--16
    thread--16
    main--17
    thread--17
    main--18
    thread--18
    main--19
    thread--19
    程序运行完发现每次运行结果都不同,因为多个线程都在获取CPU的执行权,CPU执行到谁,谁就运行。
    明确一点,在某一个时刻,只能有一个程序在运行(多核CPU除外)。
    CPU在做着快速的切换,以达到看上去是同时运行的效果,我们可以形象把多线程的运行形容为在互相抢夺CPU的执行权。
    这就是多线程的一个特性:随机性。谁抢到谁执行,至于执行多长时间,CPU说了算。 
 
 
    5、多线程的特性
            随机性。
    6、多线程的运行状态
            被创建:被创建start()后进入运行状态。
            当同一个线程被start()两次之后,第二次会出现java.lang.IllegalThreadStateException线程状态异常,已经开启的线程
            是不允许再次开启的。
            运行:运行过程中通过sleep(time)和wait()进入冻结状态,sleep(time)时间到之后自动唤醒,而wait()需要notify()来手动唤醒。
            冻结:放弃了执行资格。
            阻塞(临时状态):具备运行资格,但没有执行权,CPU未切换到该线程。
            消亡:stop()和run()方法结束。
                sleep(time)
                wait()  ---  需要notify()唤醒。
                stop()
    7、获取线程对象以及名称
        可以通过set和get方法设置和获取名称。
        Thread(String name)
        Thread初始化的时候就可以设置名称。
        线程都有自己默认的名称:thread-编号,编号从0开始。
        static Thread currentThread()
            返回当前正在执行的线程对象的引用(线程对象)。
            因为currentThread是静态的,所以没有访问当前线程对象的特有数据。
        获取线程名称:
            getName();
        设置线程名称
            setName()或者构造函数
            System.out.println(Thread.currentThread().getName());
        Thread类中有getName()和setName()方法,可以直接使用super(name)传参设置线程名称,
        因为父类Thread中有Thread(String name)。
     
    8、创建线程的第二种方式:实现Runnable接口
 
        class Run{}
        new Thread(new Run()).start();
 
        继承Runnable接口的子类并不是线程,因为该子类和Thread类无关。

        Thread类有一个构造方法,可以接收一个Runnable接口类型的对象。
        如果有一部分代码需要多个线程去执行,就需要这个代码所在的类继承Thread,但是如果该类已经继承了其他类,就不能继
        承Thread类,只能使用Runnable接口来实现。
        总结:
            可以在继承一个类的同时实现Runnable接口。
        步骤:
        (1)定义类实现Runnable接口。
        (2)覆盖Runnable接口中的run方法。
            将线程要运行的代码存放在该run方法中。
        (3)通过Thread类建立线程对象。(只有Thread能创建线程对象)
        (4)将Runnable接口的子类对象作为实际参数传递给Thread类中的构造函数。
            通过多个线程操作一个对象来保证数据的完整性和唯一性。
            Thread(Runnable target)
            为什么要将Runnable接口的子类对象传递给Thread的构造函数?
            因为:
                自定义的run方法所属的对象是Runnable接口的子类对象,所以要让线程去执行指定对象的run方法,就必须明确该run方法所
                属对象。
        (5)调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
        实现方式和继承方式有什么区别?
            实现方式的好处:避免了单继承的局限性。
            在定义线程时,建议使用实现方式。
            
        如果只有继承,那么Student类中有一部分代码需要多线程去执行时,继承Thread类之后无法再继承Person类,为了避免此情况的
        发生,就出现了接口。
 
        因为Java工程师并不确定将来会出现的对象类型,则Thread定义构造方法的接收的是Runnable类型的引用,这样不论是什么类型
        的对象,都能接收。
 
        两种方式的区别:
            继承Thread类:线程代码 存放在Thread子类的run方法中。a
            实现Runnable接口:线程代码存放在Runnable接口的子类对象的run方法中。
 
——通过多线程实现多窗口售票例子:
 
    class Ticket implements Runnable
    {
        //当将i定义为静态时,所有线程共享一个数据,保证数据唯一性。
        private int tick = 50;
        public void run()
        {
            while (tick > 0)
            {
                System.out.println(Thread.currentThread().getName()+"----"+tick--);
            }
        }
    }
 
    public class TicketDemo {
        public static void main(String[] args)
        {
            Ticket t = new Ticket();
            Thread t1 = new Thread(t);
            Thread t2 = new Thread(t);
            Thread t3 = new Thread(t);
            Thread t4 = new Thread(t);
        //此时通过Thread类创建四个对象共同开启线程
            t1.start();
            t2.start();
            t3.start();
            t4.start();
        }
    }
------------------------------------------------------------------------
 
输出结果:
 
    Thread-0----50
    Thread-2----48
    Thread-1----49
    Thread-2----46
    Thread-0----47
    Thread-2----44
    Thread-2----42
    Thread-1----45
    Thread-2----41
    Thread-2----39
    Thread-2----38
    Thread-2----37
    Thread-2----36
    Thread-2----35
    Thread-2----34
    Thread-2----33
    Thread-0----43
    Thread-2----32
    Thread-1----40
    Thread-2----30
    Thread-2----28
    Thread-0----31
    Thread-2----27
    Thread-2----25
    Thread-2----24
    Thread-2----23
    Thread-1----29
    Thread-2----22
    Thread-2----20
    Thread-0----26
    Thread-2----19
    Thread-2----17
    Thread-2----16
    Thread-2----15
    Thread-2----14
    Thread-1----21
    Thread-2----13
    Thread-0----18
    Thread-2----11
    Thread-1----12
    Thread-2----9
    Thread-0----10
    Thread-2----7
    Thread-2----5
    Thread-2----4
    Thread-2----3
    Thread-2----2
    Thread-2----1
    Thread-1----8
    Thread-0----6
------------------------------------------------------------------------
 
    通过上面售票的例子发现,虽然数据保证完整性了但是输出顺序与理想不一致,是因为线程间的切换导致的。
 
    9、多线程的安全问题——同步代码块 synchronized
 
    问题出现的原因:
        当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,此时另一个线程参与执行,导致
        共享数据错误。
    解决办法:
        对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。
 
    class Ticket implements Runnable
    {
        private int tick = 100;
        Object obj = new Object();
        public void run()
        {
            while (true)
            {
                synchronized(obj)
                {
                    if(tick > 0)
                    {
                        try
                        {
                            Thread.sleep(10);    // sleep()方法需要抛出InterruptedException异常,但是如果复写了Runnable的run方法,则只
                                                                能try,不能抛出。
                        }
                        catch(Exception ex)
                        {
                        }
                        System.out.println(Thread.currentThread().getName()+"----"+tick--);
                    }
                }
            }
        }
    }
 
    public class TicketDemo {
        public static void main(String[] args)
        {
            Ticket t = new Ticket();
            Thread t1 = new Thread(t);
            Thread t2 = new Thread(t);
            Thread t3 = new Thread(t);
            Thread t4 = new Thread(t);
            t1.start();
            t2.start();
            t3.start();
            t4.start();
        }
    } 
    锁旗标:
        对象如同锁,持有锁的线程可以在程序中同步执行,未持有锁的线程即使获取CPU的执行权也无法执行,因为没有锁。
 
    同步的前提:
        1、必须要有两个或者两个以上的线程才需要同步。
        2、必须是多个线程使用同一个锁。 
        必须保证同步中只能有一个线程运行。 
 
    synchronized的好处:
        解决了多线程的安全问题。
 
    弊端:
        多个线程都需要判断锁,较为消耗资源。
 
    当多线程出现安全隐患,如何查找问题所在(明确哪些代码需要被同步):
        1、明确哪些代码是多线程运行代码。
        2、明确共享数据。
        3、明确多线程运行代码中哪些语句是操作共享数据的。
 
    同步函数:
        run方法不能加synchronized锁,如果加锁就会变成单线程程序,因为第一个线程开启之后run方法就会上锁,则其余线程就无法再
        次进入,直到第一个线程全部运行结束。
            public synchronized void show(){ }
        同步函数使用的锁是this锁。
            因为函数需要被对象调用,那么函数都有一个所属对象的引用,就是this。 
        使用同步锁封装代码时可以将代码提取使用函数封装并同步,就有了同步函数。
        当同步代码块和同步函数分离时,如果当前对象是自定义对象锁而不是this锁,则会容易出现数据混乱的情况,因为同步的前提是
        多个线程使用同一个锁。
 
      /**
 
     * 
 
     * 验证同步函数使用的锁是this锁
 
     * @author WYC
     *
     */
    public class ThreadDemo04 {
        public static void main(String[] args) throws InterruptedException
        {
            Ticket2 t = new Ticket2();
            Thread t1 = new Thread(t);
            Thread t2 = new Thread(t);
            t1.start();
            Thread.sleep(1);
            t.flag = false;
            t2.start();
        }
    }
    class Ticket2 implements Runnable
    {
        private static int tick = 1000;
        Object o = new Object();
        boolean flag = true;
        public void run()
        {
            if(flag)
            {
                while(true)
                {
                    synchronized(Ticket2.class)
                    {
                        if(tick > 0)
                        {
                            try{Thread.sleep(1);}catch(Exception ex){}
                            System.out.println(Thread.currentThread().getName() + "--" + tick--);
                        }
                    }
                }
            }
            else
            {
                while(true)
                {
                    show();
                }
            }
        }
        public static synchronized void show()
        {
            if(tick > 0)
            {
                try{Thread.sleep(10);}catch(Exception ex){}
                System.out.println(Thread.currentThread().getName() + "----" + tick--);
            }
        }
    }
 
    11、静态方法的同步
        如果同步函数被静态修饰后,使用的锁是什么呢?
            通过验证发现不再是this,因为静态方法中不可以定义this。静态进内存时内存中没有本类对象,但是一定有该类对应的字节码
            文件,类名.class,该对象的类型是Class。
        静态方法的同步,使用的锁是该方法所在类的字节码文件对象。
            类名.class
        同步静态方法的锁是加在类上,同步非静态方法的锁是加在那个对象上。
        例:
            synchronized(类.class){ }
            public static synchronized void show(){ }
            以上两个代码会保证同步,因为使用的锁是同一个锁,如果synchronized使用的锁是对象锁,则会出现数据混乱的情况。
        静态方法属于类,普通方法属于对象,再一起加上同步,即同步静态方法就是给类加锁,同步普通方法就是给对象加锁。
 
    12、多线程中的单例设计模式
 
        懒汉式
    class Single
    {
        private Single(){};
        private Single s = null;
        public static Single getInstance()
        {
            if(null == s)    //在锁之外再加一个if判断,可以提高效率,避免每次都判断锁。
            {
                synchronized(Single.clsss)
                        //为了保证对象唯一性,防止多线程并发访问该方法导致出现多个对象,所以使用synchronized同步该方法
                {
                    if(null == s)
                    s = new Single();
                }
            }
            return s;
        }
    }
 
    13、死锁 DeadLock
    
    package duoxiancheng;
    class Lock implements Runnable
    {
        private boolean flag;
        Lock(boolean flag)
        {
            this.flag = flag;
        }
        public void run()
        {
            if(flag)
            {
                synchronized(LockInstance.locka)
                {
                    System.out.println("if locka");
                    synchronized(LockInstance.lockb)
                    {
                        System.out.println("if lockb");
                    }
                }
            }
            else
            {
                synchronized(LockInstance.lockb)
                {
                    System.out.println("else locka");
                    synchronized(LockInstance.locka)
                    {
                        System.out.println("else lockb");
                    }
                }
            }
        }
    }
 
    class LockInstance 
    {
        //LockInstance lock1 = new LockInstance();
        //LockInstacne lock2 = new LockInstance();
        static Object locka = new Object();
        static Object lockb = new Object();
        //Object lock1 = new Object();
    }
 
    public class DeadLock {
        public static void main(String[] args)
        {
            Lock l1 = new Lock(true);
            Lock l2 = new Lock(false);
            Thread t1 = new Thread(l1);
            Thread t2 = new Thread(l2);
            t1.start();
            t2.start();
        }
    }
 
    14、线程间的通讯
        其实就是多个线程在操作同一个资源,但是操作的动作不同(比如存取)。
    class Student
    {
        String name;
        String sex;
    }
 
    class Input implements Runnable
    {
        private Student st;
        Input(Student st)
        {
            this.st = st;
        }
        public void run()
        {
            int x = 30;
            boolean bool = true;
            while(true)
            {
                synchronized(st) //为了避免出现同步问题,在输入和输出语句外加上synchronized代码块,对象选择内存中唯一的st对象
                {
                    if(bool)
                    {
                        st.name = "张三";
                        st.sex = "男";
                        bool = false;
                    }
                    else
                    {
                        st.name = "zhangsan";
                        st.sex = "nan";
                        bool = true;
                    }
                }
            }
        }
    }
 
    class Output implements Runnable
    {
        private Student st;
        Output(Student st)
        {
            this.st = st;
        }
        public void run()
        {
            int x = 30;
            while(true)
            {
                synchronized(st)
                {
                    System.out.println(st.name+"-------"+st.sex);
                    System.out.println(Thread.currentThread().getName());
                }
            }
        }
    }
    
    public class Threadtongxun {
        public static void main(String[] args)
        {
            Student st = new Student();
            Input in = new Input(st);
            Output out = new Output(st);
            Thread t1 = new Thread(in);
            Thread t2 = new Thread(out);
            t1.start();
            t2.start();
        }
    }
 
    15、等待——唤醒机制
        线程运行的时候内存中会建立一个线程池,等待的线程都存在于线程池中。
        notify();唤醒的通常是第一个进入线程池的线程。notifyAll();唤醒所有线程池中的线程。
        wait(); notify(); notifyAll();必须用于同步中,因为只有同步才具有锁。
        
        wait()用于操作线程,但是却定义在了Object类里面,这是为什么?
            因为wait() notify方法在操作同步中的线程时,都必须要标识它们所操作线程持有的锁,只有同一个锁上的等待线程可以被同一
            个锁上的notify唤醒,不可以对不同锁中的线程进行唤醒。
            使用wait()时必须标识出wait()操作的线程所属的锁。
            例如:
               obj.wait()  obj.notify()
            也就是说,等待和唤醒必须是同一个锁。
            而锁可以是任意对象,所以,可以被任意对象调用的方法就定义在Object类中。
 
        class Student
        {
            String name;
            String sex;
            boolean flag = false;
        }
 
        class Input implements Runnable
        {
            private Student st;
            Input(Student st)
            {
                this.st = st;
            }
            public void run()
            {
                int x = 30;
                boolean bool = true;
                while(true)
                {
                    synchronized(st)
                    {
                        if(st.flag)
                        {try{st.wait();}catch(Exception ex){}}
                        if(bool)
                        {
                            st.name = "张三";
                            st.sex = "男";
                            bool = false;
                        }
                        else
                        {
                            st.name = "zhangsan";
                            st.sex = "nan";
                            bool = true;
                        }
                        st.flag = true;
                        st.notify();
                    }
                }
            }
        }
    
        class Output implements Runnable
        {
            private Student st;
            Output(Student st)
            {
                this.st = st;
            }
            public void run()
            {
                int x = 30;
                while(true)
                {
                    synchronized(st)
                    {
                        if(!st.flag)
                        {try{st.wait();}catch(Exception ex){}}
                        System.out.println(st.name+"-------"+st.sex);
                        System.out.println(Thread.currentThread().getName());
                        st.flag = false;
                        st.notify();
                    }
                }
            }
        }
    
        public class Threadtongxun {
            public static void main(String[] args)
            {
                Student st = new Student();
                Input in = new Input(st);
                Output out = new Output(st);
                Thread t1 = new Thread(in);
                Thread t2 = new Thread(out);
                t1.start();
                t2.start();
            }
        }
 
    16、Consumer和Producer
 
        class Person
        {
            private String name;
            private int count = 100;
            private boolean flag = false;
            public synchronized void set(String name)
            {
                while(flag)        //此处如果使用if,则会导致其他线程不会判断flag(因为有两个以上线程),所以使用while多次判断
                try
                {
                    this.wait();
                }
                catch(Exception ex)
                {
                }
                this.name = name+"--"+count--;
                System.out.println(Thread.currentThread().getName()+"Producer--"+this.name);
                flag = true;
                this.notifyAll();    //如果使用notify,则只会唤醒本方线程,会导致全部线程都进入等待状态,需要使用notify唤醒全部线程,
                                           //然后再次进行判断。
            }
            public synchronized void out()
            {
                while(!flag)
                { 
                    try
                    {
                        this.wait();
                    }
                    catch(Exception ex)
                    {
                    }
                    System.out.println(Thread.currentThread().getName()+"Consumer-----"+this.name);
                    flag = false;
                    this.notifyAll();
                }
            }
        class Producer implements Runnable
        {
            private Person p;
            Producer(Person p)
            {
                this.p = p;
            }
            public void run()
            {
                while(true)
                {
                    p.set("++Goods++");
                }
            }
        }
 
        class Consumer implements Runnable
        {
            private Person p;
            Consumer(Person p)
            {
                this.p = p;
            }
            public void run()
            {
                while(true)
                {
                    p.out();
                }
            }
        }
 
        public class ProducerConsumerDemo {
            public static void main(String[] args)
            {
                Person p = new Person();
                Producer pro = new Producer(p);
                Consumer con = new Consumer(p);
                Thread t1 = new Thread(pro);
                Thread t2 = new Thread(pro);
                Thread t3 = new Thread(con);
                Thread t4 = new Thread(con);
                t1.start();
                t2.start();
                t3.start();
                t4.start();
            }
        }
 
    17、JDK5.0升级版
        JDK1.5中提供了多线程升级解决方案,将synchronized替换成显式的Lock操作,将Object中的wait、notify、notifyAll,替换成
        了Condition对象,该对象可以通过Lock锁进行获取。
        该示例中,实现了本方只唤醒对方的操作。
 
        import java.util.concurrent.locks.*;
 
        class Producer implements Runnable // throws InterruptedException
        {
            private Resource r;
            Producer(Resource r)
            {
                this.r = r;
            }
            public void run()
            {
                while(true)
                {
                    r.set("++商品++");
                }
            }
        }
    
        class Consumer implements Runnable //throws InterruptedException
        {
            private Resource r;
            Consumer(Resource r)
            {
                this.r = r;
            }
            public void run()
            {
                while(true)
                {
                    r.out();
                }
            }
        }
 
        class Resource
        {
            private String name;
            private int count = 1;
            private boolean flag = false;
            private Lock lock = new ReentrantLock();
            private Condition condition_pro = lock.newCondition();
            private Condition condition_con = lock.newCondition();
            public void set(String name) //throws InterruptedException
            {
                lock.lock();
                try
                {
                    while(flag)
                        condition_pro.await();
                    this.name = name+"..."+count--;
                    System.out.println(Thread.currentThread().getName()+"--生产者--"+this.name);
                    flag = true;
                    condition_con.signal();
                }
                catch(Exception ex)
                {
                }
                finally
                {
                    lock.unlock();
                }
            }
            public void out() //throws InterruptedException
            {
                lock.lock();
                try
                {
                    while(!flag)
                        condition_con.await();
                    System.out.println(Thread.currentThread().getName()+"消费者"+this.name);
                    flag = false;
                    condition_pro.signal();
                }
                catch (Exception ex)
                {
                }
                finally
                {
                    lock.unlock();
                }
            }
        } 
 
 
        public class ProducerConsumerDemo1_5 {
            public static void main(String[] args)
            {
                Resource r = new Resource();
                Producer pro = new Producer(r);
                Consumer con = new Consumer(r);
                Thread t1 = new Thread(pro);
                Thread t2 = new Thread(pro);
                Thread t3 = new Thread(con);
                Thread t4 = new Thread(con);
                t1.start();
                t2.start();
                t3.start();
                t4.start();
            }
        }
 
    18、如何停止线程?
        只有一种方法,就是run方法结束,开启多线程运行,运行代码通常是循环结构,只要控制住循环,就可以让run方法结束,也就是
        线程结束。
        特殊情况:
            当线程处于冻结状态,就不会读取到标记,那么线程就不会结束,当没有指定的方式让冻结的线程恢复到运行状态时,这时需要
            对冻结状态进行清除,强制让线程恢复到运行状态中来,这样就可以操作标记(循环条件)让线程结束。
            Thread类提供该方法:interrupt();
            使用方法:对象.interrupt();
 
        class StopThread implements Runnable
        {
            private boolean flag = true;
            public synchronized void run()
            {
                while(flag)
                {
                    try
                    {
                        wait();
                        //System.out.println(Thread.currentThread().getName()+"----running");
                    }
                    catch(InterruptedException ex)
                    {
                        flag = false;
                        System.out.println(Thread.currentThread().getName()+"  InterrputException");
                    }
                    System.out.println(Thread.currentThread().getName()+"  over");
                }
            }
        }
 
        public class StopThreadDemo {
            public static void main(String[] args)
            {
                StopThread s = new StopThread();
                Thread t1 = new Thread(s);
                Thread t2 = new Thread(s);
                t1.start();
                t2.start();
                int x = 0;
                while(true)
                {
                    if(x++ == 60)
                    {
                        t1.interrupt();
                        t2.interrupt();
                        break;
                    }
                    System.out.println(Thread.currentThread()+"--"+x);
                }
            }
        }
 
    19、守护线程
        public final void setDaemon(boolean on)  //如果参数为true,则该线程为守护线程
        将该线程标记为守护线程或用户线程,当正在运行的线程都是守护线程时,Java虚拟机退出,该方法必须在启动线程前调用。
        后台线程:当所有的前台线程都结束后,后台线程会自动结束。
 
        class StopThread implements Runnable
        {
            private boolean flag = true;
            public void run()
            {
                while(true)
                {
                    if(flag)
                    try
                    {
                        wait();
                    }
                    catch(Exception ex)
                    {
                    }
                    System.out.println(Thread.currentThread().getName()+"  run");
                }
            }
        }

        public class SetDaemonDemo {
            public static void main(String[] args)
            {
                StopThread s = new StopThread();
                Thread t1 = new Thread(s);
                Thread t2 = new Thread(s);
                t1.setDaemon(true);
                t2.setDaemon(true);
                t1.start();
                t2.start();
                int x = 0;
                w:while(true)
                {
                    if(x++ == 60)
                    {
                        //t1.interrupt();
                        //t2.interrupt();
                        break w;
                    }
                    System.out.println(Thread.currentThread()+"--"+x);
                }
                System.out.println("main over");
            }
        }
 
    20、join方法
        需要抛出throws InterruptedException
        join方法的功能是等待该线程结束主线程再启动,例如t1.join();
            如果调用顺序为:
                t1.start();
                t2.start();
                t1.join();
            则运行顺序为:
                t1   t2交替运行,主线程等到t1结束再运行。
 
        class Join implements Runnable
        {
            public void run()
            {
                for(int i = 60;i>0;i--)
                {
                        System.out.println(Thread.currentThread().getName() + "---"+i);
                }
            }
        }
 
        public class JoinDemo {
            public static void main(String[] args) throws InterruptedException
            {
                Join j = new Join();
                Thread t1 = new Thread(j);
                Thread t2 = new Thread(j);
                t1.start();
                t2.start();
                t1.join();
                int x = 50;
                while(x>0)
                {
                    System.out.println(Thread.currentThread().getName()+"--"+x--);
                }
                System.out.println("main over");
            }
        }
 
    21、优先级与yield方法
        线程类中有一个方法,toString(),覆盖了Object类中的toString,返回该线程的字符串表现形式,包括线程名称、优先级和线程组。
        线程组:谁开启的线程,线程就属于哪个组。
        优先级:所有的线程默认优先级是5,可以通过setPriority()来修改线程的优先级。setPriority方法属于ThreadGroup类。
        Java中优先级1、5、10默认定义为MIN_PRIORITY、NORM_PRIORITY、MAX_PRIORITY:t1.setPriority(MAX_PRIORITY),将t1
            的优先级设置为10级。
        数据是固定的定义成常量,字母大写,数据是共享的定义成静态。
 
        yield()方法:暂停当前执行的线程,执行其他线程。
            可以暂时释放当前线程的执行权,防止连续执行,导致其他线程无执行权。
 
    22、内部类线程
        public class NeibuleiThreadDemo {
            public static void main(String[] args) 
            {
                new Thread()
                {
                    public void run()
                    {
                        int x = 30;
                        while(x-- > 0)
                        {
                            System.out.println(Thread.currentThread().toString()+"--"+x);
                        }
                    }
                }.start();
            }
        }


——ThreadLocal

1、ThreadLocal类只有三个方法:
    *   void set(T value)
        保存值。
    *   T get()
        获取值。
    *   void remove()
        移除值。

2、ThreadLocal的内部是一个Map
    ThreadLocal内部其实是使用一个Map来保存数据的,虽然在使用ThreadLocal时只给出了值,并没有给出键,但是在其内部使用了当前线程作为键。

class TL<T> {
    private Map<Thread, T> map = new HashMap<Thread, T>();
 
    public void set(T value) {
        // 使用当前线程做key
        map.put(Thread.currentThread(), value);
    }
 
    public T get() {
        return map.get(Thread.currentThread());
    }
 
    public void remove() {
        map.remove(Thread.currentThread());
    }
}

3、具体操作

public class Demo012 {
    @Test
    public void fun1() {
        ThreadLocal<String> tl = new ThreadLocal<String>();
        tl.set("Hello");// 存
        tl.set("World");// 取
 
        String s = tl.get();
        tl.remove();// 删
        System.out.println(s);
    }
posted @ 2017-02-07 17:25  WWWYC  阅读(167)  评论(0编辑  收藏  举报