一、线程共享数据
a)继承Thread,那么我们可以创建很多个这样的类,但是每个这样的类都是相互不关联的,也就是说我们Thread类中的内容每个创建出来的类都有一份,因此它不适合作为数据共享的线程来操作。同时由于Java继承的唯一性,我们只能继承一个对象。
b)使用runnable就可以解决唯一性和不能共享的问题(不是说使用runnable就解决了共享问题,只是相对于创建Thread来说,它可以算的上是共享了,为了获得更精确的共享问题,它必须的使用线程同步操作)。实现了runnable接口的类比较适合用作共享数据。
一个测试例子à证明runnable能实现数据共享,thread不能
Thread_thread一个继承了Thread的线程
Thread_runnable是一个时间了runnable的接口,他们在run里面有共同的方法
for(int i=0;i<20;i++){ if(ticket>0){ System.out.println(ticket); ticket--; } } thread_thread th1=new thread_thread(); thread_thread th2=new thread_thread(); thread_thread th3=new thread_thread(); th1.start(); th2.start(); th3.start(); |
输入了三组321321321
因为创建的是三个对象,每一个对象都拥有自己的一个备份
将一个runnable作为参数,实例化三个thread对象
thread_runnable ru=new thread_runnable(); Thread th1=new Thread(ru); Thread th2=new Thread(ru); Thread th3=new Thread(ru); th1.start(); th2.start(); th3.start(); |
输入了32133
虽然说着不是完整意义上的数据共享,但是相当于上述打印三组完整的数据来说,它已经实现了数据共享,我们从中也可以看到,我们只创建了一个runnable对象(数据只产生了一份),它由三个Thread调用。
新建三个runnable对象,分别给每一个thread传递
Thread th1=new Thread(new thread_runnable()); Thread th2=new Thread(new thread_runnable()); Thread th3=new Thread(new thread_runnable()); th1.start(); th2.start(); th3.start(); |
打印结果是321321321
我们可以看到我们产生了三个runnable对象,每一个都有自己的一份使用
综上所述:只有将一个runnable对象作为参数,传递给thread对象才能实现数据共享。
注意:当我们创建一个Thread对象,并多次调用start方法的时候,系统是不会给你创建多个Thread线程的,它只会运行那个唯一的Thread一次而已,也就是说你运行了一次start方法之后再调用一个它的start方法是没有意义的(那个Thread没有结束的情况下),系统不会给你多次运行的。
二、线程同步
a)线程代码块(在代码中添加Synchronized(对象){})
i.Synchronized(对象),每个对象都有个标志位,当我们进入synchronized代码块中,系统就让这个对象的标志位变为0,就相当于给这个对象添加上了一把锁,当别的代码运行到这个代码块的时候因为加了锁,所以不能进去,当第一个程序它运行出去之后,系统就会让标志位变为1,相当于解锁。这样别的代码又可以访问了。从而实现同步(安全)操作。
ii.当我们将我们的标志位对象放在run方法里面定义的时候,我们是不能实现同步的,因为我们每次运行一个线程,都将调用它的run方法,从而每次都会创建一个新的标志位对象,也就是说我们所有的run方法都含有自己的一个标志位对象,因此不能实现加锁的过程。一般都是放在runnable接口中进行定义的。
b)线程方法(在代码的方法申明中public和void之间添加synchronized)
i.每次只能有一个线程调用这个同步方法,而且每次这个方法都得运行完,这就是同步代码方法。
ii.同步方法默认使用的是this来作为标志对象位的,这个this就是我们的当前类。
c)注意:
i.当一个同步代码块和一个同步代码方法使用的不是同一个对象作为标志位的时候,它们就不会实现同步,这也就是数,当两个同步代码块不使用同一个对象作为标志位,那他们就不能实现同步。
ii.调用线程的Start方法的时候,并没有真正的运行这个代码,而只是说这个代码已经准备就绪,有运行的可能。
三、线程通信
a)当我们的代码中使用了synchronized(对象)同步代码块的时候,如果我们想实现线程通信,也就是如果我们想使用wait、notify或者notifyall时,我们必须在静态代码块中使用对象.wait()、对象.notify()、对象.notifyAll()来通信,不然的话讲会报Illegal的错误。
b)Notify是唤醒同一监视器下(相当于同一个标志位对象)的第一个wait线程,而notifyall是唤醒所有的处于同一监视器下的(同一标志位对象)的线程。