返回
基础
分类

哪些义务局营,使用Thread类和利用Runnable接口

日期: 2020-03-23 16:39 浏览次数 : 143

// 当为true的线程,他执行的时间会返回
 public static void main(String[] args) {
  Thread t1 = new MyThead(true);
  Thread t2 = new MyThead(false);
  Thread t3 = new MyThead(true);
  t1.setName("第一个");
  t2.setName("第二个");
  t3.setName("第三个");
  t1.start();
  t2.start();
  t3.start();

1.创建线程

 

 }

  在Java中创建线程有两种方法:使用Thread类和使用Runnable接口。在使用Runnable接口时需要建立一个Thread实例。因此,无论是通过Thread类还是Runnable接口建立线程,都必须建立Thread类或它的子类的实例。Thread构造函数:

时间:2016-4-15 09:56

}

  • public Thread( ); 

  • public Thread(Runnable target); 

  • public Thread(String name); 

  • public Thread(Runnable target, String name); 

  • public Thread(ThreadGroup group, Runnable target); 

  • public Thread(ThreadGroup group, String name); 

  • public Thread(ThreadGroup group, Runnable target, String name); 

  • public Thread(ThreadGroup group, Runnable target, String name, long stackSize);

 

class MyThead extends Thread {
 private boolean flag;

方法一:继承Thread类覆盖run方法

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

 public MyThead(boolean flag) {
  this.flag = flag;
 }

复制代码代码如下:

 

 public void setFlag(boolean flag) {
  this.flag = flag;
 }

public class ThreadDemo1 {
     public static void main(String[] args){
         Demo d = new Demo();
         d.start();
         for(int i=0;i<60;i++){
             System.out.println(Thread.currentThread().getName()+i);
         }

    1、进程

 public void run() {
  for (int i = 0; i < 20; i++) {
   if (flag) {
    Thread.yield();// 当同级线程到达的时候,会让步在这里
   }
   System.out.println(this.getName());

     }
 }
 class Demo extends Thread{
     public void run(){
         for(int i=0;i<60;i++){
             System.out.println(Thread.currentThread().getName()+i);
         }
     }
 }

        进程是一个正在执行中的程序。

  }
 }

方法二:

        每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。
        多任务的理解:
                计算机可以运行多个任务,哪个任务运行,哪个任务就占用CPU资源。 

}

复制代码代码如下:

    2、线程(例:Thunder)

public class ThreadDemo2 {
    public static void main(String[] args){
        Demo2 d =new Demo2();
        Thread t = new Thread(d);
        t.start();
        for(int x=0;x<60;x++){
            System.out.println(Thread.currentThread().getName()+x);
        }
    }
}
class Demo2 implements Runnable{
    public void run(){
        for(int x=0;x<60;x++){
            System.out.println(Thread.currentThread().getName()+x);
        }
    }
}

        线程就是进程中的一个独立的控制单元。
        线程是对进程的进一步分化。 

2.线程的生命周期

        线程控制着进程的执行。

  与人有生老病死一样,线程也同样要经历开始(等待)、运行、挂起和停止四种不同的状态。这四种状态都可以通过Thread类中的方法进行控制。下面给出了Thread类中和这四种状态相关的方法。

        一个进程中至少有一个线程。

  • // 开始线程

  • publicvoid start( ); 

  • publicvoid run( ); 

  • // 挂起和唤醒线程

  • publicvoid resume( );     // 不建议使用

  • publicvoid suspend( );    // 不建议使用

  • publicstaticvoid sleep(long millis); 

  • publicstaticvoid sleep(long millis, int nanos); 

  • // 终止线程

  • publicvoid stop( );       // 不建议使用

  • publicvoid interrupt( ); 

  • // 得到线程状态

  • publicboolean isAlive( ); 

  • publicboolean isInterrupted( ); 

  • publicstaticboolean interrupted( ); 

  • // join方法

  • publicvoid join( ) throws InterruptedException; 

        举例:

 

            JVM启动的时候会有一个java.exe进程,该进程中至少有一个线程负责Java程序的执行,而这个线程运行的代码存在于main方

    线程在建立后并不马上执行run方法中的代码,而是处于等待状态。线程处于等待状态时,可以通过Thread类的方法来设置线程不各种属性,如线程的优先级(setPriority)、线程名(setName)和线程的类型(setDaemon)等。

            法中,该线程称为主线程。其实JVM启动时不止一个线程,还有负责垃圾回收机制的线程。

   当调用start方法后,线程开始执行run方法中的代码。线程进入运行状态。可以通过Thread类的isAlive方法来判断线程是否处于运行状态。当线程处于运行状态时,isAlive返回true,当isAlive返回false时,可能线程处于等待状态,也可能处于停止状态。下面的代码演示了线程的创建、运行和停止三个状态之间的切换,并输出了相应的isAlive返回值。

    3、多线程存在的意义

   一但线程开始执行run方法,就会一直到这个run方法执行完成这个线程才退出。但在线程执行的过程中,可以通过两个方法使线程暂时停止执行。这两个方法是suspend和sleep。在使用suspend挂起线程后,可以通过resume方法唤醒线程。而使用sleep使线程休眠后,只能在设定的时间后使线程处于就绪状态(在线程休眠结束后,线程不一定会马上执行,只是进入了就绪状态,等待着系统进行调度)。

            提高效率

在使用sleep方法时有两点需要注意:

    4、线程的创建方式

1. sleep方法有两个重载形式,其中一个重载形式不仅可以设毫秒,而且还可以设纳秒(1,000,000纳秒等于1毫秒)。但大多数操作系统平台上的Java虚拟机都无法精确到纳秒,因此,如果对sleep设置了纳秒,Java虚拟机将取最接近这个值的毫秒。

        通过对Java API的查找,Java已经提供了对线程这类事物的描述,就是Thread类。

2. 在使用sleep方法时必须使用throws或try{...}catch{...}。因为run方法无法使用throws,所以只能使用try{...}catch{...}。当在线程休眠的过程中,使用interrupt方法中断线程时sleep会抛出一个InterruptedException异常。sleep方法的定义如下:

        创建线程的第一种方式:继承Thread类。

  1. public**static**void sleep(long millis) throws InterruptedException

  2. public**static**void sleep(long millis, int nanos) throws InterruptedException

        步骤:

有三种方法可以使终止线程。

            1、定义类继承Thread。

1.  使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。

            2、复写Thread类中的run方法。

2.  使用stop方法强行终止线程(这个方法不推荐使用,因为stop和suspend、resume一样,也可能发生不可预料的结果)。

                目的是将自定义的代码存储在run()方法中,让线程执行指定的代码。

3.  使用interrupt方法中断线程。

            3、调用线程的start方法。

  1. 使用退出标志终止线程

                该方法有两个作用:启动线程,调用run方法。只有start才能调用线程。

  当run方法执行完后,线程就会退出。但有时run方法是永远不会结束的。如在服务端程序中使用线程进行监听客户端请求,或是其他的需要循环处理的任务。在这种情况下,一般是将这些任务放在一个循环中,如while循环。如果想让循环永远运行下去,可以使用while(true){...}来处理。但要想使while循环在某一特定条件下退出,最直接的方法就是设一个boolean类型的标志,并通过设置这个标志为true或false来控制while循环是否退出。下面给出了一个利用退出标志终止线程的例子。

                如果直接调用run方法,则为主线程调用。线程创建了,并没有执行。

   join方法的功能就是使异步执行的线程变成同步执行。也就是说,当调用线程实例的start方法后,这个方法会立即返回,如果在调用start方法后后需要使用一个由这个线程计算得到的值,就必须使用join方法。如果不使用join方法,就不能保证当执行到start方法后面的某条语句时,这个线程一定会执行完。而使用join方法后,直到这个线程退出,程序才会往下执行。下面的代码演示了join的用法。

                run仅仅是封装线程代码。

 3.多线程安全问题

    为什么要覆盖run方法?

  问题原因:当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没执行完,另一个线程参与进来执行,导致共享数据的错误。

        Thread类用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码,该存储功能就是run方法,
        也就是说Thread类中的run方法,用于存储线程要运行的代码,而主线程运行的代码存放在main方法中。

  解决办法:对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不执行。

 

同步代码块:

    当主线程结束后,只要还有线程在运行,那么进程就不停止。

复制代码代码如下:

 

public class ThreadDemo3 {
    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();
    }
}
class Ticket implements Runnable{
    private int ticket =400;
    public void run(){
        while(true){
            synchronized (new Object()) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                if(ticket<=0)
                    break;
                System.out.println(Thread.currentThread().getName()+"---卖出"+ticket--);
            }

        在下面的例子中main为主线程。

        }
    }
}

 

同步函数

    class thread extends Thread

复制代码代码如下:

    {

public class ThreadDemo3 {
    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();
    }
}
class Ticket implements Runnable{
    private int ticket = 4000;
必赢手机登录网址 ,    public synchronized void  saleTicket(){
        if(ticket>0)
            System.out.println(Thread.currentThread().getName()+"卖出了"+ticket--);

        public void run()

    }
    public void run(){
        while(true){
            saleTicket();
        }
    }
}

        {

同步函数锁是this 静态同步函数锁是class

            for(int i = 0; i<20; i++)

线程间的通信

                System.out.println("thread--"+i);

复制代码代码如下:

        }

public class ThreadDemo3 {
    public static void main(String[] args){
        class Person{
            public String name;
            private String gender;
            public void set(String name,String gender){
                this.name =name;
                this.gender =gender;
            }
            public void get(){
                System.out.println(this.name+"...."+this.gender);
            }
        }
        final Person p =new Person();
        new Thread(new Runnable(){
            public void run(){
                int x=0;
                while(true){
                    if(x==0){
                        p.set("张三", "男");
                    }else{
                        p.set("lili", "nv");
                    }

    }

                    x=(x+1)%2;
                }
            }
        }).start();
        new Thread(new Runnable(){
            public void run(){
                while(true){
                    p.get();
                }
            }
        }).start();
    }

 

}
/*
张三....男
张三....男
lili....nv
lili....男
张三....nv
lili....男
*/

    public class ThreadDemo1 {

修改上面代码

        public static void main(String[] args)

复制代码代码如下:

        {

public class ThreadDemo3 {
     public static void main(String[] args){
         class Person{
             public String name;
             private String gender;
             public void set(String name,String gender){
                 this.name =name;
                 this.gender =gender;
             }
             public void get(){
                 System.out.println(this.name+"...."+this.gender);
             }
         }
         final Person p =new Person();
         new Thread(new Runnable(){
             public void run(){
                 int x=0;
                 while(true){
                     synchronized (p) {
                         if(x==0){
                             p.set("张三", "男");
                         }else{
                             p.set("lili", "nv");
                         }
                         x=(x+1)%2;    
                     }

            thread t = new thread();

                 }
             }
         }).start();
         new Thread(new Runnable(){
             public void run(){
                 while(true){
                     synchronized (p) {
                         p.get();
                     }
                 }
             }
         }).start();
     }

            t.start();

 }
 /*
 lili....nv
 lili....nv
 lili....nv
 lili....nv
 lili....nv
 lili....nv
 张三....男
 张三....男
 张三....男
 张三....男
 */

            //t.run(); 

等待唤醒机制

            for(int i = 0; i<20; i++)

复制代码代码如下:

            {

/*
 *线程等待唤醒机制
 *等待和唤醒必须是同一把锁 
 */
public class ThreadDemo3 {
    private static boolean flags =false;
    public static void main(String[] args){
        class Person{
            public String name;
            private String gender;
            public void set(String name,String gender){
                this.name =name;
                this.gender =gender;
            }
            public void get(){
                System.out.println(this.name+"...."+this.gender);
            }
        }
        final Person p =new Person();
        new Thread(new Runnable(){
            public void run(){
                int x=0;
                while(true){
                    synchronized (p) {
                        if(flags)
                            try {
                                p.wait();
                            } catch (InterruptedException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            };
                        if(x==0){
                            p.set("张三", "男");
                        }else{
                            p.set("lili", "nv");
                        }
                        x=(x+1)%2;
                        flags =true;
                        p.notifyAll();
                    }

                System.out.println("main--"+i);

                }
            }
        }).start();
        new Thread(new Runnable(){
            public void run(){
                while(true){
                    synchronized (p) {
                        if(!flags)
                            try {
                                p.wait();
                            } catch (InterruptedException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            };
                        p.get();
                        flags =false;
                        p.notifyAll();
                        }
                }
            }
        }).start();
    }

            }

}

        }

生产消费机制一

    }

复制代码代码如下:

——————————输出结果为

public class ThreadDemo4 {
    private static boolean flags =false;
    public static void main(String[] args){
        class Goods{
            private String name;
            private int num;
            public synchronized void produce(String name){
                if(flags)
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                this.name =name+"编号:"+num++;
                System.out.println("生产了...."+this.name);
                flags =true;
                notifyAll();
            }
            public synchronized void consume(){
                if(!flags)
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                System.out.println("消费了******"+name);
                flags =false;
                notifyAll();
            }

    main--0

        }
        final Goods g =new Goods();
        new Thread(new Runnable(){
            public void run(){
                while(true){

    thread--0

                    g.produce("商品");

    main--1

                }
            }
        }).start();
        new Thread(new Runnable(){
            public void run(){
                while(true){

    thread--1

                    g.consume();

    main--2

                }
            }
        }).start();
    }

    thread--2

}

    main--3

生产消费机制2

    thread--3

复制代码代码如下:

    main--4

public class ThreadDemo4 {
    private static boolean flags =false;
    public static void main(String[] args){
        class Goods{
            private String name;
            private int num;
            public synchronized void produce(String name){
                while(flags)
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                this.name =name+"编号:"+num++;
                System.out.println(Thread.currentThread().getName()+"生产了...."+this.name);
                flags =true;
                notifyAll();
            }
            public synchronized void consume(){
                while(!flags)
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                System.out.println(Thread.currentThread().getName()+"消费了******"+name);
                flags =false;
                notifyAll();
            }

    thread--4

        }
        final Goods g =new Goods();
        new Thread(new Runnable(){
            public void run(){
                while(true){

    main--5

                    g.produce("商品");

    main--6

                }
            }
        },"生产者一号").start();
        new Thread(new Runnable(){
            public void run(){
                while(true){

    thread--5

                    g.produce("商品");

    main--7

                }
            }
        },"生产者二号").start();
        new Thread(new Runnable(){
            public void run(){
                while(true){

    thread--6

                    g.consume();

    main--8

                }
            }
        },"消费者一号").start();
        new Thread(new Runnable(){
            public void run(){
                while(true){

    thread--7

                    g.consume();

    main--9

                }
            }
        },"消费者二号").start();
    }

    thread--8

}
/*
消费者二号消费了******商品编号:48049
生产者一号生产了....商品编号:48050
消费者一号消费了******商品编号:48050
生产者一号生产了....商品编号:48051
消费者二号消费了******商品编号:48051
生产者二号生产了....商品编号:48052
消费者二号消费了******商品编号:48052
生产者一号生产了....商品编号:48053
消费者一号消费了******商品编号:48053
生产者一号生产了....商品编号:48054
消费者二号消费了******商品编号:48054
生产者二号生产了....商品编号:48055
消费者二号消费了******商品编号:48055
*/

    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);

    }