线程数据共享与协作
线程数据共享
- 数据(即任意的对象)在多个线程之间的共享
方式一:构造方法传入共享数据(对象)
测试类
public class ThreadShareDataTest {
public static void main(String[] args) {
Object object = new Object();
NewThread newThread1 = new NewThread(object);
NewThread newThread2 = new NewThread(object);
newThread1.start();
newThread2.start();
}
}
线程类
class NewThread extends Thread{
Object object;
public NewThread(Object object) {
this.object = object;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(getName() + object + "--->" + i);
}
}
}
结果:在两个线程之间是同一个对象,这样就实现了数据的共享
Thread-0java.lang.Object@2b8a94a4—>0
Thread-1java.lang.Object@2b8a94a4—>0
Thread-0java.lang.Object@2b8a94a4—>1
Thread-1java.lang.Object@2b8a94a4—>1
Thread-0java.lang.Object@2b8a94a4—>2
Thread-1java.lang.Object@2b8a94a4—>2
Thread-1java.lang.Object@2b8a94a4—>3
Thread-1java.lang.Object@2b8a94a4—>4
Thread-0java.lang.Object@2b8a94a4—>3
Thread-1java.lang.Object@2b8a94a4—>5
Thread-0java.lang.Object@2b8a94a4—>4
Thread-1java.lang.Object@2b8a94a4—>6
Thread-0java.lang.Object@2b8a94a4—>5
Thread-0java.lang.Object@2b8a94a4—>6
Thread-0java.lang.Object@2b8a94a4—>7
Thread-1java.lang.Object@2b8a94a4—>7
Thread-0java.lang.Object@2b8a94a4—>8
Thread-1java.lang.Object@2b8a94a4—>8
Thread-0java.lang.Object@2b8a94a4—>9
Thread-1java.lang.Object@2b8a94a4—>9
方式二:在线程类中定义静态成员变量
- 在线程类中定义一个静态的成员变量,可以通过类名给变量赋值
测试类
public class ThreadShareDataTest {
public static void main(String[] args) {
NewThread newThread1 = new NewThread();
NewThread newThread2 = new NewThread();
NewThread newThread3 = new NewThread();
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.submit(newThread1);
executorService.submit(newThread2);
executorService.submit(newThread3);
executorService.shutdown();
}
}
线程类
class NewThread extends Thread{
static Object object = new Object();
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(currentThread().getName()+ "--->" + getName() + "--->" + object + "--->" + i);
}
}
}
结果
pool-1-thread-1—>Thread-0—>java.lang.Object@309d7a5a—>0
pool-1-thread-2—>Thread-1—>java.lang.Object@309d7a5a—>0
pool-1-thread-2—>Thread-1—>java.lang.Object@309d7a5a—>1
pool-1-thread-2—>Thread-1—>java.lang.Object@309d7a5a—>2
pool-1-thread-1—>Thread-0—>java.lang.Object@309d7a5a—>1
pool-1-thread-2—>Thread-1—>java.lang.Object@309d7a5a—>3
pool-1-thread-1—>Thread-0—>java.lang.Object@309d7a5a—>2
pool-1-thread-2—>Thread-1—>java.lang.Object@309d7a5a—>4
pool-1-thread-1—>Thread-0—>java.lang.Object@309d7a5a—>3
pool-1-thread-1—>Thread-0—>java.lang.Object@309d7a5a—>4
pool-1-thread-2—>Thread-1—>java.lang.Object@309d7a5a—>5
pool-1-thread-1—>Thread-0—>java.lang.Object@309d7a5a—>5
pool-1-thread-2—>Thread-1—>java.lang.Object@309d7a5a—>6
pool-1-thread-2—>Thread-1—>java.lang.Object@309d7a5a—>7
pool-1-thread-2—>Thread-1—>java.lang.Object@309d7a5a—>8
pool-1-thread-2—>Thread-1—>java.lang.Object@309d7a5a—>9
pool-1-thread-1—>Thread-0—>java.lang.Object@309d7a5a—>6
pool-1-thread-2—>Thread-2—>java.lang.Object@309d7a5a—>0
pool-1-thread-1—>Thread-0—>java.lang.Object@309d7a5a—>7
pool-1-thread-1—>Thread-0—>java.lang.Object@309d7a5a—>8
pool-1-thread-1—>Thread-0—>java.lang.Object@309d7a5a—>9
pool-1-thread-2—>Thread-2—>java.lang.Object@309d7a5a—>1
pool-1-thread-2—>Thread-2—>java.lang.Object@309d7a5a—>2
pool-1-thread-2—>Thread-2—>java.lang.Object@309d7a5a—>3
pool-1-thread-2—>Thread-2—>java.lang.Object@309d7a5a—>4
pool-1-thread-2—>Thread-2—>java.lang.Object@309d7a5a—>5
pool-1-thread-2—>Thread-2—>java.lang.Object@309d7a5a—>6
pool-1-thread-2—>Thread-2—>java.lang.Object@309d7a5a—>7
pool-1-thread-2—>Thread-2—>java.lang.Object@309d7a5a—>8
pool-1-thread-2—>Thread-2—>java.lang.Object@309d7a5a—>9
synchronized
- 上面打印出的结果有交叉执行的现象,为了使原本并发运行的多个线程实现串行运行,即多线程间同步执行,需要通过对象锁机制来实现,synchronized就是一个利用锁实现线程同步的关键字。
对需要同步的代码,使用synchronized环绕
class NewThread extends Thread{
Object lock;
public NewThread(Object lock) {
this.lock = lock;
}
@Override
public void run() {
//lock变量就是共享的数据
synchronized (lock) {
for (int i = 0; i < 10; i++) {
System.out.println(getName() + "--->" + lock + "--->" + i);
}
}
}
测试类
public class ThreadShareDataTest {
public static void main(String[] args) {
Object lock = new Object();
NewThread newThread1 = new NewThread(lock);
NewThread newThread2 = new NewThread(lock);
NewThread newThread3 = new NewThread(lock);
newThread1.start();
newThread2.start();
newThread3.start();
}
}
结果
Thread-0—>java.lang.Object@434e2b1d—>0
Thread-0—>java.lang.Object@434e2b1d—>1
Thread-0—>java.lang.Object@434e2b1d—>2
Thread-0—>java.lang.Object@434e2b1d—>3
Thread-0—>java.lang.Object@434e2b1d—>4
Thread-0—>java.lang.Object@434e2b1d—>5
Thread-0—>java.lang.Object@434e2b1d—>6
Thread-0—>java.lang.Object@434e2b1d—>7
Thread-0—>java.lang.Object@434e2b1d—>8
Thread-0—>java.lang.Object@434e2b1d—>9
Thread-1—>java.lang.Object@434e2b1d—>0
Thread-1—>java.lang.Object@434e2b1d—>1
Thread-1—>java.lang.Object@434e2b1d—>2
Thread-1—>java.lang.Object@434e2b1d—>3
Thread-1—>java.lang.Object@434e2b1d—>4
Thread-1—>java.lang.Object@434e2b1d—>5
Thread-1—>java.lang.Object@434e2b1d—>6
Thread-1—>java.lang.Object@434e2b1d—>7
Thread-1—>java.lang.Object@434e2b1d—>8
Thread-1—>java.lang.Object@434e2b1d—>9
Thread-2—>java.lang.Object@434e2b1d—>0
Thread-2—>java.lang.Object@434e2b1d—>1
Thread-2—>java.lang.Object@434e2b1d—>2
Thread-2—>java.lang.Object@434e2b1d—>3
Thread-2—>java.lang.Object@434e2b1d—>4
Thread-2—>java.lang.Object@434e2b1d—>5
Thread-2—>java.lang.Object@434e2b1d—>6
Thread-2—>java.lang.Object@434e2b1d—>7
Thread-2—>java.lang.Object@434e2b1d—>8
Thread-2—>java.lang.Object@434e2b1d—>9
虽然可能多次运行的线程的运行顺序可能不同,但是只要在一个线程拿到对象锁之后,其他线程就需要等待,在一个线程执行完之后释放锁之后,再开始另外一个线程。
-
线程之间数据共享的意义也在于多个线程之间持有的锁对象是同一个对象,这样多个线程就实现了串行执行,不会出现交叉执行的现象,否则多个线程之间持有的锁对象不是同一个对象,将无法串行化。
-
锁对象其实也可以是Class类对象,使用 类名.class或Class.forName(“全类名”)或者 对象.getClass() 由于Class类的构造方法是私有的,且为单例模式,所以使用类名.class 作为锁对象也是可以的。
线程协作
wait()方法
- 对象锁调用了wait()方法会使当前持有该对象锁的线程处于线程等待状态同时该线程释放对对象锁的控制权,直到在其他线程中该对象锁调用notify()方法或notifyAll()方法时等待此对象锁的线程才会被唤醒
notify()方法
- 对象锁调用notify()方法就会唤醒在此对象锁上等待的单个线程
notifyAll()方法
- 对象锁调用notifyAll()方法就会唤醒在此对象锁上等待的所有线程;调用notifyAll()方法并不会立即激活某个等待线程,它只能撤销等待线程的中断状态,这样它们就能够在当前线程退出同步方法或同步代码块法后与其它线程展开竞争,以争取获得资源对象来执行
线程之间的协作
public class ThreadTogetherWorkTest {
Date time;
public static void main(String[] args) {
ThreadTogetherWorkTest threadTogetherWorkTest = new ThreadTogetherWorkTest();
TimeThread timeThread =threadTogetherWorkTest.new TimeThread();
DisplayTimeThread displayTimeThread = threadTogetherWorkTest.new DisplayTimeThread();
timeThread.start();
displayTimeThread.start();
}
class TimeThread extends Thread{
@Override
public void run() {
time = new Date();
}
}
class DisplayTimeThread extends Thread{
@Override
public void run() {
System.out.println(time);
}
}
}
执行结果
有可能为 (displayTimeThread线程先夺取到了CPU的执行权)
null
或者为(timeThread线程先夺取到了CPU的执行权)
Mon Jul 01 17:26:45 CST 2019
- 为了一定可以打印出时间信息需要线程之间的协作
方式一
- 使用join
public class ThreadTogetherWorkTest {
Date time;
public static void main(String[] args) {
ThreadTogetherWorkTest threadTogetherWorkTest = new ThreadTogetherWorkTest();
TimeThread timeThread =threadTogetherWorkTest.new TimeThread();
DisplayTimeThread displayTimeThread = threadTogetherWorkTest.new DisplayTimeThread(timeThread);
timeThread.start();
displayTimeThread.start();
}
class TimeThread extends Thread{
@Override
public void run() {
time = new Date();
}
}
class DisplayTimeThread extends Thread{
TimeThread timeThread;
public DisplayTimeThread(TimeThread timeThread) {
this.timeThread = timeThread;
}
@Override
public void run() {
if(time==null) {
try {
timeThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(time);
}
}
}
方式二
- 使用synchronized执行同步代码块,配合wait和notify方法
public class ThreadTogetherWorkTest {
Date time;
public static void main(String[] args) {
ThreadTogetherWorkTest threadTogetherWorkTest = new ThreadTogetherWorkTest();
TimeThread timeThread =threadTogetherWorkTest.new TimeThread();
DisplayTimeThread displayTimeThread = threadTogetherWorkTest.new DisplayTimeThread();
timeThread.start();
displayTimeThread.start();
}
class TimeThread extends Thread{
@Override
public void run() {
time = new Date();
synchronized (ThreadTogetherWorkTest.class) {
ThreadTogetherWorkTest.class.notify();
}
}
}
class DisplayTimeThread extends Thread{
@Override
public void run() {
if(time==null) {
synchronized (ThreadTogetherWorkTest.class) {
try {
ThreadTogetherWorkTest.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println(time);
}
}
}
结果
Mon Jul 01 17:34:44 CST 2019
注意:wait方法和sleep方法的区别
- wait方法非静态但是是Object的方法;sleep是Thread的静态方法
- wait方法要配合synchronized以及notify使用;sleep不需要
- 执行wait方法时当前线程进入阻塞状态,并且释放对象锁;执行sleep方法当前线程虽然也会进入阻塞状态,但是并不释放对象锁