Java网络编程——线程
一、为什么要使用多线程
1、耗时操作使用线程。可有效提高应用程序的响应
2、并行操作需要使用线程,如C/S服务器端并发响应请求
3、多CPU系统中,采用多线程提高CPU利用率
4、改善程序结构
二、使用线程时注意事项
1、不同的线程共享相同的内存,一个线程可能破坏另一个线程使用的变量或数据结构
2、十分注意资源的使用,防止产生死锁
三、运行线程的方法
1、派生Thread
重写run方法
import java.io.FileInputStream;
import java.io.IOException;
import java.security.*;
public class TestThread extends Thread{
private String fileName;
public TestThread(String fileName) {
this.fileName = fileName;
}
public void run() {
// TODO Auto-generated method stub
try{
FileInputStream in = new FileInputStream(fileName);
MessageDigest sha = MessageDigest.getInstance("SHA-256");
DigestInputStream din = new DigestInputStream(in, sha);
while(din.read() != -1);
din.close();
byte[] digest = sha.digest();
StringBuilder result = new StringBuilder(fileName);
result.append(":");
result.append(digest);
System.out.println(result);
}catch(IOException e){
System.out.println(e);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
String fileName = new String("D:\\workspace5\\TestThread\\file.txt");
Thread t = new TestThread(fileName);
t.start();
}
}
2、实现Runnable接口
import java.io.FileInputStream;
import java.io.IOException;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class TestRunnable implements Runnable{
private String fileName;
public TestRunnable(String fileName) {
this.fileName = fileName;
}
public void run() {
// TODO Auto-generated method stub
try{
FileInputStream in = new FileInputStream(fileName);
MessageDigest sha = MessageDigest.getInstance("SHA-256");
DigestInputStream din = new DigestInputStream(in, sha);
while(din.read() != -1);
din.close();
byte[] digest = sha.digest();
StringBuilder result = new StringBuilder(fileName);
result.append(":");
result.append(digest);
System.out.println(result);
}catch(IOException e){
System.out.println(e);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
TestRunnable tr = new TestRunnable("D:\\workspace5\\TestThread\\file.txt");
Thread t = new Thread(tr);
t.start();
}
}
四、从线程返回结果
1、轮询
主线程定期轮询,查看标志位。
import java.io.FileInputStream;
import java.io.IOException;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class ReturnDigest extends Thread{
private String mFileName;
private byte[] mDigest;
public ReturnDigest(String mFileName) {
this.mFileName = mFileName;
}
public void run() {
// TODO Auto-generated method stub
try{
FileInputStream in = new FileInputStream(mFileName);
MessageDigest sha = MessageDigest.getInstance("SHA-256");
DigestInputStream din = new DigestInputStream(in, sha);
while(din.read() != -1);
din.close();
mDigest = sha.digest();
}catch(IOException e){
System.out.println(e);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
public byte[] getDigest() {
return mDigest;
}
}
public class ReturnDigestUserInterface {
public static void main(String[] args) {
// calculate abstract
String mFileName = new String("D:\\workspace5\\TestThread\\file.txt");
ReturnDigest rd = new ReturnDigest(mFileName);
rd.start();
while(true){//主线程轮询查看标志位digest
byte[] digest = rd.getDigest();
if(digest != null){
StringBuilder result = new StringBuilder(mFileName);
result.append(":");
result.append(digest);
System.out.println(result);
break;
}
}
}
}
2、回调
轮询是无限循环,判断标志位,开销较大。回调:子线程快结束时调用主线程的方法,将结果传回主线程。
import java.io.FileInputStream;
import java.io.IOException;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class CallbackDigest implements Runnable{
private String mFileName;
public CallbackDigest(String mFileName) {
this.mFileName = mFileName;
}
public void run() {
// TODO Auto-generated method stub
try{
FileInputStream in = new FileInputStream(mFileName);
MessageDigest sha = MessageDigest.getInstance("SHA-256");
DigestInputStream din = new DigestInputStream(in, sha);
while(din.read() != -1);
din.close();
byte[] digest = sha.digest();
CallbackDigestUserInterface.receiveDigest(digest, mFileName);//回调主线程中的方法
}catch(IOException e){
System.out.println(e);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
}
public class CallbackDigestUserInterface {
public static void main(String[] args) {
// 计算摘要
CallbackDigest cb = new CallbackDigest("D:\\workspace5\\TestThread\\file.txt");
Thread t = new Thread(cb);
t.start();
}
public static void receiveDigest(byte[] digest, String mFileName) {
StringBuilder result = new StringBuilder(mFileName);
result.append(":");
result.append(digest);
System.out.println(result);
}
}
3、回调实例方法
回调的类有其回调对象的一个引用
import java.io.FileInputStream;
import java.io.IOException;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class InstanceCallbackDigest implements Runnable{
private String mFileName;
private InstanceCallbackDigestUserInstance mCallback;//回调对象的一个引用
public InstanceCallbackDigest(String mFileName,
InstanceCallbackDigestUserInstance mCallback) {
super();
this.mFileName = mFileName;
this.mCallback = mCallback;
}
public void run() {
// TODO Auto-generated method stub
try{
FileInputStream in = new FileInputStream(mFileName);
MessageDigest sha = MessageDigest.getInstance("SHA-256");
DigestInputStream din = new DigestInputStream(in, sha);
while(din.read() != -1);
din.close();
byte[] digest = sha.digest();
mCallback.receiveDigest(digest);//回调主线程中的方法
}catch(IOException e){
System.out.println(e);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
}
public class InstanceCallbackDigestUserInstance {
private String mFileName;
private byte[] mDigest;
public InstanceCallbackDigestUserInstance(String mFileName) {
super();
this.mFileName = mFileName;
}
public void calculate(){
InstanceCallbackDigest ic = new InstanceCallbackDigest(mFileName, this);
Thread t = new Thread(ic);
t.start();
}
void receiveDigest(byte[] digest) {
this.mDigest = digest;
System.out.println(this);
}
public static void main(String[] args) {
InstanceCallbackDigestUserInstance i = new InstanceCallbackDigestUserInstance("D:\\workspace5\\TestThread\\file.txt");
i.calculate();
}
}
4、Future、Callable和Executor
隐藏异步性的回调。
(1)创建一个ExecutorService,它会根据需要给你创建相应的线程。
(2)向ExecutorService提交Callable任务,每一个Callable会分别得到一个Future
(3)可向Future请求得到的结果
import java.util.concurrent.Callable;
public class FindMaxTask implements Callable<Integer>{
private int [] data;
private int start;
private int end;
public FindMaxTask(int[] data, int start, int end) {
super();
this.data = data;
this.start = start;
this.end = end;
}
public Integer call() throws Exception {
int max = Integer.MIN_VALUE;
for(int i = start; i< end; i++){
if(data[i] > max){
max = data[i];
}
}
return max;
}
}
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class MultithreadedMaxFinder {
public static int max(int data[]) throws InterruptedException, ExecutionException{
if(data.length == 1){
return data[0];
}
if(data.length == 0){
throw new IllegalArgumentException();
}
//划分两个任务
FindMaxTask task1 = new FindMaxTask(data, 0, data.length/2);
FindMaxTask task2 = new FindMaxTask(data,data.length/2 , data.length);
//创建ExecutorService
ExecutorService services = Executors.newFixedThreadPool(2);
Future<Integer> future1 = services.submit(task1);
Future<Integer> future2 = services.submit(task2);
return Math.max(future1.get(), future2.get());
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
int[] data = {68,3,5,9,14,37,45,1};
System.out.println(max(data));
}
}
五、同步
指定一个共享资源只能由一个线程独占访问来执行特定的语句序列
1、同步块
把需要同步的语句放入到同步块
synchronized(System.out){//对System.out对象同步
result.append(":");
result.append(digest);
System.out.println(result);
}
2、同步方法
public synchronized void writeEntry(String message){
Date d = new Date();
out.write(d.toString());
out.write('\t');
out.write(message);
out.write('\r\n');
}
3、同步的替代方法
(1)尽量使用局部变量而不是字段,局部变量不存在同步问题,包括方法的参数
(2)利用类的不可变性,private和final
(3)将非线程安全的类作为线程安全的类的一个私有字段
(4)对于Collection和List,有线程安全版本,Collections.synchronizedSet(foo)/Collections.synchronizedList(foo)/Collections.synchronizedMap(foo)
六、死锁
不必要的同步常常会导致死锁,要保证同步块尽量小,尽量不要一次同步多个对象。
七、线程调度
主要有两种线程调度机制:抢占式和协作式。每个线程都有一个优先级,Java中,10是最高优先级,0是最低优先级,5是默认优先级。
为了让其他线程有机会运行,线程有10中方式可以暂停或指示它准备暂停。
1、对I/O阻塞
当线程停下来等待它没有的资源时,就会发生阻塞。常见的阻塞有I/O阻塞和对锁的阻塞。
2、对同步对象阻塞
3、放弃
显示地放弃控制权,通过调用Thread.yield()方法。注意:放弃不会释放这个线程拥有的锁,在线程放弃时不应做任何同步。
4、休眠
不管是否是其他线程准备运行,休眠线程都会暂停。通过调用Thread.sleep()方法。注意:进入休眠的线程拥有其已经获得的锁,避免在同步方法和同步块中做休眠操作
5、连接另一个线程
当一个线程需要另一个线程的结果时,常常使用join()方法。使得一个线程在继续执行前等待另一个线程结束。
public class TestJoinSortThread extends Thread{
private double[] array;
public TestJoinSortThread(double[] array) {
this.array = array;
}
@Override
public void run() {
for(int i = 0; i < array.length; i++){
for(int j = i; j<array.length; j++){
if(array[i] > array[j]){
double temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
}
}
}
public class TestJoin {
public static void main(String[] args) {
double[] array = new double[10000];
//随机生成数组
for(int i = 0; i < array.length; i++){
array[i] = Math.random();
}
TestJoinSortThread st = new TestJoinSortThread(array);
st.start();
try{
st.join();//连接
System.out.println("MIN:" + array[array.length - 1]);
System.out.println("MAX:" + array[0]);
System.out.println("Middle:" + array[array.length/2]);
}catch(InterruptedException e){
}
}
}
6、等待一个对象
7、结束
8、被更高优先级线程抢占
9、被挂起
10、停止

浙公网安备 33010602011771号