关于java多线程的概念以及基本用法:java多线程基础
3, 线程间通信
线程在操作系统中是独立的个体,经过特殊的处理,线程间可以实现通信,进而成为一个整体,提高CPU利用率
3.1,等待/通知机制
等待:wait()方法作用是使当前执行代码的线程阻塞,在调用wait()之前,线程必须获得该对象的对象级别锁,即只能在同步方法或同步块中调用wait()方法。执行wait()方法后,当前线程会释放锁。如果调用wait()方法时没有锁,那么会抛出IllegalMonitorStateException
通知:notify()也要在同步方法或者同步代码块中调用,调用之前,线程也必须获得该对象的对象级别的锁,若没有,则会抛出IllegalMonitorStateException。该方法用于通知那些可能等待该对象的对象锁的其他线程,如果有多个线程等待,则由线程规划器随机挑选其中一个呈wait状态的线程,对其发出通知,并使它等待获取该对象的对象锁。执行notify()方法后,当前线程不会马上释放该对象锁,呈wait状态的线程也不能马上获取该对象锁,要等执行notify()方法的线程执行完,当前线程才会释放锁。
方法notifyAll()可以唤醒全部线程
新建MyThread1类:
public class MyThread1 extends Thread{
private Object lock;
public MyThread1(Object lock) {
super();
this.lock = lock;
}
@Override
public void run() {
try {
synchronized(lock) {
System.out.println("开始 wait time"+System.currentTimeMillis());
lock.wait();
System.out.println("结束 wait time"+System.currentTimeMillis());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
新建MyThread2类:
public class MyThread2 extends Thread{
private Object lock;
public MyThread2(Object lock) {
super();
this.lock = lock;
}
@Override
public void run() {
synchronized(lock) {
System.out.println("开始 notify time"+System.currentTimeMillis());
lock.notify();
System.out.println("结束 notify time"+System.currentTimeMillis());
}
}
}
测试类:
public class Test {
public static void main(String[] args) {
try {
Object lock = new Object();
MyThread1 t1 = new MyThread1(lock);
t1.start();
Thread.sleep(3000);
MyThread2 t2 = new MyThread2(lock);
t2.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
结果:
开始 wait time1534469153084
开始 notify time1534469156084
结束 notify time1534469156084
结束 wait time1534469156084
线程t1在执行wait()方法后进入阻塞状态,3秒后线程t2执行,执行了notify()方法后线程t2在执行完代码块后唤醒线程t1
方法wait(time)的作用是等待一段时间是否有线程对锁进行唤醒,如果超过这个时间就自动唤醒
public class MyRunnable {
static private Object lock = new Object();
static private Runnable runnable1 = new Runnable() {
@Override
public void run() {
try {
synchronized(lock) {
System.out.println("wait begin timer="+System.currentTimeMillis());
lock.wait(5000);
System.out.println("wait end timer="+System.currentTimeMillis());
}
}catch (Exception e) {
e.printStackTrace();
}
}
};
public static void main(String[] args) {
Thread t= new Thread(runnable1);
t.start();
}
}
结果:
wait begin timer=1534473901885
wait end timer=1534473906886
3.2,通过管道进行线程间通信:字节流
Java语言里提供了很多的输入/输出流Stream,便于数据读写,pipeStream(管道流)是一种特殊的流,用于在不同线程间直接传送数据,一个线程发送数据到输出管道,另一个线程从输入管道中读取数据,可以实现线程间的通信
Java的JDK提供了4个类
先看看字节流:
新建WriteData类:
public class WriteData {
public void writeMethod(PipedOutputStream out) {
try {
System.out.println("write: ");
for(int i=0;i<20;i++) {
String outData = ""+(i+1);
out.write(outData.getBytes());
System.out.print(outData);
}
System.out.println();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
新建ReadData类:
public class ReadData {
public void readMethod(PipedInputStream input) {
try {
System.out.println("read: ");
byte[] byteArray = new byte[20];
int readLength = input.read(byteArray);
while (readLength!=-1) {
String newData = new String(byteArray,0,readLength);
System.out.print(newData);
readLength = input.read(byteArray);
}
System.out.println();
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
新建两个线程:
public class ThreadWrite extends Thread{
private WriteData write;
private PipedOutputStream out;
public ThreadWrite(WriteData write, PipedOutputStream out) {
super();
this.write = write;
this.out = out;
}
@Override
public void run() {
write.writeMethod(out);
}
}
public class ThreadRead extends Thread{
private ReadData read;
private PipedInputStream input;
public ThreadRead(ReadData read, PipedInputStream input) {
super();
this.read = read;
this.input = input;
}
@Override
public void run() {
read.readMethod(input);
}
}
测试类:
public class Run {
public static void main(String[] args) {
try {
WriteData writeData = new WriteData();
ReadData readData = new ReadData();
PipedInputStream inputStream =new PipedInputStream();
PipedOutputStream outputStream = new PipedOutputStream();
//inputStream.connect(outputStream);
outputStream.connect(inputStream);
ThreadRead threadRead = new ThreadRead(readData, inputStream);
threadRead.start();
Thread.sleep(2000);
ThreadWrite threadWrite = new ThreadWrite(writeData, outputStream);
threadWrite.start();
} catch (IOException e) {
e.printStackTrace();
}catch(InterruptedException e1) {
e1.printStackTrace();
}
}
}
结果:
read:
write:
1234567891011121314151617181920
1234567891011121314151617181920
再看看字符流:
WriteData类:
public class WriteData {
public void writeMethod(PipedWriter out) {
try {
System.out.println("write: ");
for(int i=0;i<20;i++) {
String outData = ""+(i+1);
out.write(outData);
System.out.print(outData);
}
System.out.println();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
ReadData类:
public class ReadData {
public void readMethod(PipedReader input) {
try {
System.out.println("read: ");
char[] byteArray = new char[20];
int readLength = input.read(byteArray);
while (readLength!=-1) {
String newData = new String(byteArray,0,readLength);
System.out.print(newData);
readLength = input.read(byteArray);
}
System.out.println();
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
两个线程类:
public class ThreadRead extends Thread{
private ReadData read;
private PipedReader input;
public ThreadRead(ReadData read, PipedReader input) {
super();
this.read = read;
this.input = input;
}
@Override
public void run() {
read.readMethod(input);
}
}
public class ThreadWrite extends Thread{
private WriteData write;
private PipedWriter out;
public ThreadWrite(WriteData write, PipedWriter out) {
super();
this.write = write;
this.out = out;
}
@Override
public void run() {
write.writeMethod(out);
}
}
测试类:
public class Run {
public static void main(String[] args) {
try {
WriteData writeData = new WriteData();
ReadData readData = new ReadData();
PipedReader inputStream =new PipedReader();
PipedWriter outputStream = new PipedWriter();
//inputStream.connect(outputStream);
outputStream.connect(inputStream);
ThreadRead threadRead = new ThreadRead(readData, inputStream);
threadRead.start();
Thread.sleep(2000);
ThreadWrite threadWrite = new ThreadWrite(writeData, outputStream);
threadWrite.start();
} catch (IOException e) {
e.printStackTrace();
}catch(InterruptedException e1) {
e1.printStackTrace();
}
}
}
结果:
read:
write:
1234567891011121314151617181920
1234567891011121314151617181920
3.2,方法join的使用
很多时候,主线程创建并启动子线程,子线程要进行一些耗时的计算,主线程往往早于子线程结束,如果主线程想等待子线程结束,则需要join()方法
新建MyThread类:
public class MyThread extends Thread{
@Override
public void run() {
try {
int secondValue = (int)(Math.random()*1000);
System.out.println(secondValue);
Thread.sleep(secondValue);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
测试类:
public class Test {
public static void main(String[] args) {
try {
MyThread threadTest = new MyThread();
threadTest.start();
threadTest.join();
System.out.println("我要等子线程结束再开始");
} catch (Exception e) {
e.printStackTrace();
}
}
}
结果:
231
我要等子线程结束再开始
join()方法的作用是使所属线程对象正常执行run()方法,使当前主线程处于阻塞状态,等子线程结束后再执行主线程,join在内部使用wait()方法进行等待
方法join(time)和sleep(time)的区别?
这两个方法都会等待time时间再运行,但是在同步的处理上是不一样的
join(time)的功能是在内部使用wait(time)方法,所以join方法其实会释放锁,
join源码:
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
在执行join(time)方法后,当前线程的锁被释放,此时其他线程可以调用此线程的同步方法,而sleep(time)方法则不会释放锁
3.3,类ThreadLocal的使用
ThreadLocal为变量在每个线程中都创建了一个副本,所以每个线程都可以访问自己内部的副本变量
新建Tools类:
public class Tools {
public static ThreadLocal t1 = new ThreadLocal();
}
两个线程类:
public class ThreadA extends Thread{
@Override
public void run() {
try {
for(int i=0;i<10;i++) {
Tools.t1.set("ThreadA"+(i+1));
System.out.println("ThreadA get Value="+Tools.t1.get());
Thread.sleep(200);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ThreadB extends Thread{
@Override
public void run() {
try {
for(int i=0;i<10;i++) {
Tools.t1.set("ThreadB"+(i+1));
System.out.println("ThreadB get Value="+Tools.t1.get());
Thread.sleep(200);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
测试类:
public class Run {
public static void main(String[] args) {
try {
ThreadA a = new ThreadA();
ThreadB b = new ThreadB();
a.start();
b.start();
for(int i=0;i<10;i++) {
Tools.t1.set("Main"+(i+1));
System.out.println("Main get Value="+Tools.t1.get());
Thread.sleep(200);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果:
Main get Value=Main1
ThreadA get Value=ThreadA1
ThreadB get Value=ThreadB1
ThreadB get Value=ThreadB2
ThreadA get Value=ThreadA2
Main get Value=Main2
Main get Value=Main3
ThreadB get Value=ThreadB3
ThreadA get Value=ThreadA3
Main get Value=Main4
ThreadB get Value=ThreadB4
ThreadA get Value=ThreadA4
ThreadB get Value=ThreadB5
ThreadA get Value=ThreadA5
Main get Value=Main5
Main get Value=Main6
ThreadA get Value=ThreadA6
ThreadB get Value=ThreadB6
ThreadB get Value=ThreadB7
ThreadA get Value=ThreadA7
Main get Value=Main7
ThreadB get Value=ThreadB8
Main get Value=Main8
ThreadA get Value=ThreadA8
Main get Value=Main9
ThreadA get Value=ThreadA9
ThreadB get Value=ThreadB9
Main get Value=Main10
ThreadB get Value=ThreadB10
ThreadA get Value=ThreadA10
由结果可以看到有虽然有3个线程set()数据值但是每个线程都能取出属于自己的数据
3.4,类InheritableThreadLocal的使用
使用InheritableThreadLocal类可以在子线程中取得父线程继承下来的值
如果在子线程取得值的同时,主线程将InheritableThreadLocal中的值进行更改,那么子线程取到的值还是旧值