java多线程——资源互斥访问
本文首先介绍一下java实现多线程的几种方式,再介绍一下如何实现资源的互斥访问,最后介绍生产环境中线程池的应用。
好,下面上货。
一、创建线程的两种方式:
1、继承Thread类。
package com.xueyoucto.xueyou;
public class FirstThread extends Thread {
public FirstThread() {
}
public FirstThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
}
}
2、实现Runnable接口。
package com.xueyoucto.xueyou;
public class SecondThread implements Runnable {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
}
}
主程序中调用:
package com.xueyoucto.xueyou;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* Hello world!
*/
public class App {
public static int[] GLOBALARRAY = new int[50];
public static ThreadLocal<String[]> TLGLOBALARRAY = new ThreadLocal<String[]>() {
@Override
protected String[] initialValue() {
String[] temp = new String[50];
for (int i = 0; i < temp.length; i++) {
temp[i] = String.valueOf(i + 100);
}
return temp;
}
};
static {
for (int i = 0; i < GLOBALARRAY.length; i++) {
GLOBALARRAY[i] = i;
}
}
public static void main(String[] args) {
//使用继承的方式创建线程
FirstThread firstThread = new FirstThread("aaThread");
FirstThread firstThread2 = new FirstThread("bbThread");
firstThread.start();
firstThread2.start();
//实现Runnable接口
new Thread(new SecondThread(), "cc-Thread").start();
new Thread(new SecondThread(), "dd-Thread").start();
}
}
输出的数据是穿插的,每个线程都会有对应的输出。
二、资源的互斥访问
1、synchornized(){}方式
package com.xueyoucto.xueyou;
public class ReadPublicArray implements Runnable {
@Override
public void run() {
synchronized (App.GLOBALARRAY) {
for (int i = 0; i < App.GLOBALARRAY.length; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + App.GLOBALARRAY[i]);
}
}
}
}
2、lock方式
package com.xueyoucto.xueyou;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockReadPublicArray implements Runnable {
//这里的锁应该是所有的线程使用同一个锁
public static Lock lock = new ReentrantLock();
@Override
public void run() {
lock.lock();
try {
for (int i = 0; i < App.GLOBALARRAY.length; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + App.GLOBALARRAY[i]);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
3、ThreadLocal方式
package com.xueyoucto.xueyou;
public class ThreadLocalReadPublicArray implements Runnable {
@Override
public void run() {
String[] tlArray = App.TLGLOBALARRAY.get();
//在此修改每一个元素的值
for (int i = 0; i < tlArray.length; i++) {
tlArray[i] += "==" + Thread.currentThread().getName() + "==";
}
//打印的时候发现,是不一样的,所以ThreadLocal对每一个线程都给了一个副本
for (int i = 0; i < tlArray.length; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + tlArray[i]);
}
}
}
在主程序中使用
package com.xueyoucto.xueyou;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* Hello world!
*/
public class App {
public static int[] GLOBALARRAY = new int[50];
public static ThreadLocal<String[]> TLGLOBALARRAY = new ThreadLocal<String[]>() {
@Override
protected String[] initialValue() {
String[] temp = new String[50];
for (int i = 0; i < temp.length; i++) {
temp[i] = String.valueOf(i + 100);
}
return temp;
}
};
static {
for (int i = 0; i < GLOBALARRAY.length; i++) {
GLOBALARRAY[i] = i;
}
}
public static void main(String[] args) {
//互斥访问synchornized
new Thread(new ReadPublicArray(),"Thread-first").start();
new Thread(new ReadPublicArray(),"Thread-second").start();
//采用lock的方式对进行代码块进行锁定
new Thread(new LockReadPublicArray(), "Thread-first").start();
new Thread(new LockReadPublicArray(), "Thread-second").start();
//采用ThreadLocal的方式实现线程对同一变量的互斥访问,本质上是TheadLocal为每一个线程产生了一个副本
new Thread(new ThreadLocalReadPublicArray(), "Thread-first").start();
new Thread(new ThreadLocalReadPublicArray(), "Thread-second").start();
}
}
运行结果:
每个线程访问之后才会执行下一个线程,能够实现对静态变量的互斥访问。
三、线程池的常规使用(jkd1.5+)
具体的应用还可以在java.util.concurrent包中去看
package com.xueyoucto.xueyou;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* Hello world!
*/
public class App {
public static int[] GLOBALARRAY = new int[50];
public static ThreadLocal<String[]> TLGLOBALARRAY = new ThreadLocal<String[]>() {
@Override
protected String[] initialValue() {
String[] temp = new String[50];
for (int i = 0; i < temp.length; i++) {
temp[i] = String.valueOf(i + 100);
}
return temp;
}
};
static {
for (int i = 0; i < GLOBALARRAY.length; i++) {
GLOBALARRAY[i] = i;
}
}
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(3);
pool.execute(new SecondThread());
pool.execute(new SecondThread());
pool.execute(new SecondThread());
pool.shutdown(); // Disable new tasks from being submitted
try {
//判断是否所有线程已经执行完毕
boolean isFinish = pool.awaitTermination(1,TimeUnit.MILLISECONDS);
System.out.println(isFinish +"==========================");
//如果没有执行完
if(!isFinish){
//线程池执行结束 不在等待线程执行完毕,直接执行下面的代码
pool.shutdownNow();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
}
//只给线程池中的线程1毫秒,然后就继续执行
System.out.println("it is ok !!!");
}
}
从结果可以看出,线程池中的线程还没有结束,下面的System.out.println("it is ok !!!");就已经被打印出来了。这里实现了对线程池中的线程进行控制。
需要注意的是这几个方法:
pool.shutdown();//不允许其他线程加入线程池
pool.shutdownNow();//停止线程池的阻塞,执行线程池后的代码。
pool.waitTermination();//判断在参数时间内,线程池中的线程是否全部执行完,执行完返回true,没执行完,返回false。