zoukankan      html  css  js  c++  java
  • 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);
        }
  • 相关阅读:
    比特币and区块链
    C#汽车租赁系统 完整版
    C#托盘程序设置
    网络电视精灵项目
    C#文件操作 File(静态类)
    深入解读XML解析
    ListView 控件总结
    DataGridView 的使用总结
    动态添加节点
    IrisSkin2.dll 添加皮肤
  • 原文地址:https://www.cnblogs.com/wwwwyc/p/6375113.html
Copyright © 2011-2022 走看看