首页 > 编程语言 > 详细

线程高级与集合

时间:2021-03-17 23:57:35      阅读:30      评论:0      收藏:0      [点我收藏+]

线程高级与集合

线程高级

1 线程的优先级

线程启动后就纳入到线程调度器中统一管理,线程不能主动索取时间片,只能被动分配,调整线程的优先级可以最大程度的干涉线程调度器分配时间片的次数,原则上:优先级越高的线程获取时间片的次数越多

线程的优先级有10个等级,对应整数1-10,其中1是最低优先级,10为最高优先级,5是默认值

package cn.tedu.vip.thread;
/**
 * 线程的优先级Priority
 * 线程优先级分10级
 * 1~10 1是最低级的优先级,10是最高优先级 5是默认值
 * 线程启动后,线程运行的调度,由操作系统管理
 * 优先级的数值作为参考,交由操作系统
 * 原则上,优先级越高的线程获得的CPU时间越长
 * @author Tedu
 *
 */
public class PriorityDemo {

	public static void main(String[] args) {
		Thread min=new Thread() {
			public void run() {
				for (int i = 0; i < 1000; i++) {
					System.out.println("min");
				}
			}
		};
		Thread max=new Thread() {
			public void run() {
				for (int i = 0; i < 1000; i++) {
					System.out.println("max");
				}
			}
		};
		Thread nor=new Thread() {
			public void run() {
				for (int i = 0; i < 1000; i++) {
					System.out.println("nor");
				}
			}
		};
		//设置线程的优先级
		min.setPriority(Thread.MIN_PRIORITY);
		max.setPriority(Thread.MAX_PRIORITY);
		
		min.start();
		max.start();
		nor.start();
	}
}
2 线程休眠

static void sleep(long ms)

该方法会让运行这个方法的线程阻塞指定毫秒

超时后线程会自动回到RUNNABLE状态再次并发运行

package cn.tedu.vip.thread;

import java.util.Scanner;
/**
 * static void sleep(long ms)
 * 调用方法让当前线程暂停(阻塞)指定的时间(毫秒) 
 * 经过了指定的时间会继续线程运行
 * @author Tedu
 *
 */
public class SleepDemo {

	public static void main(String[] args) {
		try {
			/*
			 * 用户输入一个整数 每隔1秒输入一个数字,以用户输入的数字作为第一个数 每输出一次递减1,直到输出0
			 */
			Scanner scan = new Scanner(System.in);
			System.out.println("程序开始");
			System.out.println("请输入一个数字");
			int num = scan.nextInt();
			while (num > 0) {
				System.out.println(num);
				Thread.sleep(1000);
				num--;
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("程序结束");
	}
}
3 休眠唤醒

当一个线程调用sleep方法处于阻塞状态的过程中该线程的interrupt()方法被调用时,sleep方法就会抛出中断异常,此时等同于中断了睡眠阻塞

package cn.tedu.vip.thread;
/**
 * sleep方法能够阻塞线程指定时间
 * 如果正在sleep的线程调用了interrupt()方法。线程的阻塞状态会取消
 * @author Tedu
 */
public class SleepDemo2 {

	public static void main(String[] args) {
		Thread princess=new Thread() {
			public void run() {
				System.out.println("公主被巫师诅咒,一直睡着。。。。");
				try {
					Thread.sleep(999999);
				} catch (InterruptedException e) {
					//如果sleep没到时间就被取消。就会发生异常,运行catch里的代码
					System.out.println("公主醒来了!");
				}
				System.out.println("王子和公主过上了幸福的生活");
			}
		};
		Thread prince=new Thread() {
			public void run() {
				System.out.println("王子开始亲公主");
				for (int i = 0; i < 5; i++) {
					System.out.println("么么哒!");
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				System.out.println("完事!");
				//唤醒公主的线程
				princess.interrupt();
			}

		};
		prince.start();
		princess.start();
	}	
}
4 守护线程

守护线程又称为后台线程,默认创建出来的线程都是普通线程,只有调用了setDaemon()方法才会将一个普通线程转换为守护线程

创建和使用上守护线程和普通线程一样,但是结束时机上不同

当一个进程中所有普通线程都结束时,进程就会结束,此时所有正在运行的守护线程会被强制停止

package cn.tedu.vip.thread;
/**
 * 守护线程
 * 守护线程又称后台线程,默认情况下每条线程都是普通线程
 * 只有调用了setDawmon方法之后,才能将一个线程设置为守护线程
 * 普通线程必须执行完任务序列后才能结束
 * 后台线程除了正常线程的结束方式外,在设置它为后台线程的线程结束时
 * 这个后台程序也会结束
 * @author Tedu
 *
 */
public class DaemonThreadDemo {

	public static void main(String[] args) {
		//创建普通线程
		Thread t=new Thread() {
			public void run() {
				for (int i = 0; i < 5; i++) {
					System.out.println("Hello World!!!");
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				System.out.println("程序运行完毕");
			}
		};
		//编写后台线程
		Thread gc=new Thread() {
			public void run() {
				while(true) {
					System.out.println("GC开始回收");
					try {
						Thread.sleep(500);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		};
		//必须先设置守护线程,才能运行,否则报错
		t.start();
		gc.etDaemon(true);
		gc.start();
	}
}
5 线程插队

join方法

join是可以协调多个线程同步运行的方法

它允许调用该方法的线程进入阻塞状态,直到该方法所属的线程结束后方可解除阻塞

同步运行:多个线程执行存在

异步运行:多个线程各干各的(并发操作本身就是异步)

package cn.tedu.vip.thread;
/**
 * join方法
 * join方法是一条线程的运行插入到另一条线程之前的操作
 * 被插队的线程进入阻塞状态,直到插队线程运行完毕之后阻塞状态才能取消
 * 
 * 同步线程:多个线程执行存在先后顺序
 * 异步线程:多个线程并发运行
 * @author Tedu
 *
 */
public class JoinDemo {
	//public static boolean isFinish=false;

	public static void main(String[] args) {
		Thread dw=new Thread() {
			public void run() {
				System.out.println("开始下载图片");
				for (int i = 0; i < 100; i++) {
					System.out.println("downloading...:"+i+"%");
					try {
						Thread.sleep(30);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				//isFinish=true;
				System.out.println("图片下载完毕");
			}
		};
		Thread show=new Thread() {
			public void run() {
				System.out.println("开始显示文字");
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println("show:等待图片下载完毕......");
				//运行到这里,当前线程会阻塞,等待dw线程运行完毕之后才会 开始运行
				try {
					dw.join();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				//if(isFinish) {
				System.out.println("show:开始显示图片");
				//}else {
				System.out.println("show:图片显示完毕");
				//}
			}
		};	
		dw.start();
		show.strt();
	}
}
6 线程同步

多线程并发安全问题

当多个线程并发对同一个临界资源操作时,可能由于线程切换时机,不确定导致程序和设计的执行方式出现混乱,从而引发程序业务错误甚至瘫痪。

临界资源:应该只能被单线程操作的资源

package cn.tedu.vip.thread;
/**
 * 多线程并发安全问题 当多个线程并发对同一个临界资源操作时,可能由于线程切换时机不确定导致 程序和设计的执行方式出现混乱,从而引发程序业务错误甚至瘫痪
 * 
 * 临界资源:应该只能被单线程操作的资源
 * 
 * @author Tedu
 *
 */
public class SyncDemo {

	public static void main(String[] args) {
		Bank b = new Bank();

		Thread alipay = new Thread() {
			public void run() {
				for (int i = 0; i < 8; i++) {
					b.getMonry();
					Thread.yield();//主动放弃CPU时间,让微信有几率(增加)交替执行
				}
			}
		};
		Thread wechat = new Thread() {
			public void run() {
				for (int i = 0; i < 8; i++) {
					b.getMonry();
					Thread.yield();
				}
			}
		};
		alipay.setName("支付宝");
		wechat.setName("微信");

		alipay.start();
		wechat.start();
	}
}

class Bank {
	private int money = 36000;

	/*
	 * 从银行账户中取钱 编写方法使用synchronized修饰,该方法称之为同步方法 即:多个线程不能同时运行这个方 法内部的代码
	 * 
	 * 临界资源就不会同时被多个线程使用 解决了并发安全问题
	 */
	public synchronized void getMonry() {// 定义一个方法,排队处理
		if (money >= 5000) {
			System.out.println(Thread.currentThread().getName() + ":成功取5000元");
			money -= 5000;
		}
		System.out.println(Thread.currentThread().getName() + "银行还有" + money + "元");
	}
}
7 同步块优化线程同步

有效的缩小同步范围是可以在保证并发安全的前提下提高并发效率的。

语法:synchronized(同步监视器对象){

需要多线程同步执行的代码片段

}

package cn.tedu.vip.thread;
/**
 * 使用同步代码块
 * 有效的缩小线程同步运行的范围,在可以保证安全的情况下,提高并发效率
 * 
 * synchronixed(同步对象){
 * 同步范围
 * }
 * @author Tedu
 *
 */
public class SyncDemo2 {

	public static void main(String[] args) {
		Shop s=new Shop();//实例化对象
		Thread hlw=new Thread() {
			public void run() {
				s.buy();	
			}
		};
		Thread sj=new Thread() {
			public void run() {
				s.buy();
			}
		};
		hlw.setName("葫芦娃");
		sj.setName("蛇精");
		hlw.start();
		sj.start();
	}
}
class Shop{
	/*
	 * 使用同步方法,试衣服和挑衣服不能同时执行
	 * 使用同步代码块,指定临界资源为当前Shop对象
	 */
	//public synchronized void buy() {
	public void buy() {
		Thread t=Thread.currentThread();
		System.out.println(t.getName()+"开始挑衣服");
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		synchronized(this) {
			System.out.println(t.getName()+"开始进入试衣间");
			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println(t.getName()+"离开试衣间,结账离开");
	}
}
8 静态线程同步

静态方法上使用的同步监视器对象为当前类的类对象,即:Class的一个实例

java中每个被JVM加载的类都有且只有唯一的一个类对象与之对应,因此静态方法锁该对象就一定具有同步效果

获取一个类的对象可以用:类名.class方式得到

package cn.tedu.vip.thread;
/**
 * 静态同步方法,和静态方法中的同步块
 * @author Tedu
 *
 */

public class SyncDemo3 {

	public static void main(String[] args) {
		Boo b=new Boo();//实例化对象
		Thread t1=new Thread() {
			public void run() {
				b.dosome();
			}
		};
		Thread t2=new Thread() {
			public void run() {
				b.dosome();
			}
		};
		t1.start();
		t2.start();

	}

}
class Boo{
	//public static synchronized void dosome() {
	public static void dosome() {
		synchronized(Boo.class) {//静态方法没有关键字,Boo.class锁的是这个类
		Thread t=Thread.currentThread();
		System.out.println(t.getName()+"执行方法xxxx");
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(t.getName()+"方法执行完毕");
		}
	}
}
9 互斥锁

当使用synchronized锁定多个代码片段,并且指定的同步监视器对象是同一个时,这些代码片段就是互斥的,多个线程不能同时执行他们。

package cn.tedu.vip.thread;
/**
 * 互斥锁
 * 当一个类的两个不同方法不能同时运行时
 * 就可以使用synchronized修饰它们,形成互斥锁
 * 这样即使多个线程调用这两个方法,可不能同时运行了
 * @author Tedu
 *
 */
public class SyncDemo4 {

	public static void main(String[] args) {
		Foo f=new Foo();
		Thread t1=new Thread() {
			public void run() {
				f.abc();
			}
		};
		Thread t2=new Thread() {
			public void run() {
				f.xyz();
			}
		};
		t1.setName("孙悟空的气管");
		t2.setName("孙悟空的食道");
		t1.start();
		t2.start();
	}
}
class Foo{
	public synchronized void abc() {
		Thread t=Thread.currentThread();
		System.out.println(t.getName()+"开始吸气。。。。");
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(t.getName()+"吸气完毕。。。。");
	} 
	public synchronized void xyz() {
		Thread t=Thread.currentThread();
		System.out.println(t.getName()+"开始吞咽。。。。");
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(t.getName()+"吞咽完毕");
	}
}

多线程聊天室

1 客户端代码:(含群聊聊天室)
package socket;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.util.Scanner;
/**
 * 聊天室客户端
 * 
 * @author Tedu
 *
 */
public class Client {
	/*
	 * java.net.Socket Socket封装了TCP通信细节,我们只需要明确IP地址和端口号 向服务器发送输出流输入流即可
	 */
	private static Socket socket;

	public Client() {
		try {
			/*
			 * 实例化Socket需要两个参数 1:服务器端IP地址 2:服务器端,端口号
			 * 
			 * 只要实例化成功就是连接成功 如果连接失败就会出现异常
			 * 
			 * 端口号是计算机提供服务的接口的编码 Ip地址是计算机在网络中的所在位置
			 */

			System.out.println("开始连接");
			socket = new Socket("localhost", 8088);
			System.out.println("连接成功");
		} catch (Exception e) {
			e.printStackTrace();
		}
		/*
		 * 开始搜索cmd 然后在弹出的界面中输入ipconfig观察本机的ip地址
		 */
	}

	public void start() {
		/*
		 * 要想发出信息,需要从socket对象中获得输出流 
		 * OutputStream getOutputStream();
		 */
		try {
			//启动接收服务器消息的线程
			ServerHandler handler=new ServerHandler();
			Thread t=new Thread(handler);
			t.start();

			OutputStream os = socket.getOutputStream();
			OutputStreamWriter osw = new OutputStreamWriter(os, "utf-8");// 把os转换成字符流
			BufferedWriter bw = new BufferedWriter(osw);// 转换成缓冲流
			PrintWriter pw = new PrintWriter(bw, true);// 自动调用

			Scanner scan = new Scanner(System.in);
			while (true) {
				String msg = scan.nextLine();
				pw.println(msg);// 输出一行
			}

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	public static void main(String[] args) {
		Client client = new Client();
		client.start();
	}
	/*
	 * 编写一个内部类
	 * 用于创建接收服务器端信息的线程
	 */
	class ServerHandler implements Runnable{
		public void run() {
			try {
				InputStream is=(InputStream) socket.getInputStream();
				InputStreamReader isr=new InputStreamReader(is,"utf-8");
				BufferedReader br=new BufferedReader(isr);

				String msg=null;
				while((msg=br.readLine())!=null) {
					System.out.println(msg);
				}
			} catch (Exception e) {

			}
		}
	}	
}
2 服务器代码:(含群聊聊天室)
package socket;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
import java.util.concurrent.BrokenBarrierException;
/**
 * 聊天室服务器端
 * 
 * @author Tedu
 *
 */
public class Server {
	/*
	 * java.net.ServerSocket 服务器端运行这个类的对象 1:能申请服务端口 2:能够监听客服端发送过来的信息
	 */
	private ServerSocket server;
	/*
	 * 定义一个输出流数组,保存的是指向各个客户端的输出流
	 */
	private PrintWriter[] allout= {};

	public Server() {
		try {
			/*
			 * 实例化ServerSocket对象 向操作系统申请端口 如果这个端口被占用会发生异常
			 */
			System.out.println("服务器正在启动");
			server = new ServerSocket(8088);
			System.out.println("服务器启动完毕");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void start() {
		/*
		 * Socket accept() 该方法是一个阻塞方法,调用后,程序暂停等待客服端的信息发送过来 没有信息过来就一直等
		 */
		try {
			while(true) {
				System.out.println("等待客户端连接");
				Socket socket = server.accept();
				System.out.println("一个客户端连接了");
				/*
				 * 循环等待客户端连接
				 * 每个客户端连接会得到这个客户端的socket
				 * 获得之后开启新的线程,将socket对象赋给新线程
				 * 启动线程
				 */
				ClientHandler handler=new ClientHandler(socket);
				Thread t=new Thread(handler);
				t.start();

				/*
				 * 从socket中获得输入流 InputStream getInputStream()
				 */
				//InputStream is = socket.getInputStream();
				//InputStreamReader isr = new InputStreamReader(is, "utf-8");
				//BufferedReader br = new BufferedReader(isr);

				/*
				 * 这里需要使用下面的方式来读取客户端发送过来的内容
				 * br.readLine()这个方法在不同系统下运行时会有不同的反应
				 * windows系统,客户端断开会发生异常
				 * linux系统客户端的断开返回空,当返回null时保证循环能够结束
				 */
				//String msg;
				//while((msg=br.readLine())!=null) {
				//	System.out.println(msg);
				//}

				//while(true) {
				//String msg = br.readLine();
				//System.out.println(msg);
				//}
			}

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	public static void main(String[] args) {
		Server server = new Server();
		server.start();
	}
	/*
	 * 创建一个内部类,专门处理客户端请求
	 */
	class ClientHandler implements Runnable{
		//客户端发送过来的socket对象
		private Socket socket;

		//客户端的地址信息
		private String host;

		public ClientHandler(Socket socket) {
			this.socket=socket;
			this.host=socket.getInetAddress().getHostAddress();
		}
		public void run() {
			PrintWriter pw=null;
			/*
			 * 获得客户端发送过来数据的输入流
			 */
			try {
				InputStream is=socket.getInputStream();
				InputStreamReader isr=new InputStreamReader(is,"utf-8");
				BufferedReader br=new BufferedReader(isr);
				/*
				 * 获得发送到客户端信息的输出流
				 */
				OutputStream os=socket.getOutputStream();
				OutputStreamWriter osw=new OutputStreamWriter(os,"utf-8");
				BufferedWriter bw=new BufferedWriter(osw);
				pw=new PrintWriter(bw,true);

				synchronized (allout) {
					//这个代码是有新客户端连接时运行一次的
					//目的是将这个pw放入输出流数组中
					//1:数组扩容
					allout=Arrays.copyOf(allout, allout.length+1);
					//2:将当前获得的输出流放入数组中
					allout[allout.length-1]=pw;
				}
				System.out.println(host+"上线了,当前在线人数:"+allout.length);


				/*
				 * 使用BUfferedReader的ReadLine方法获得客户端发送过来的数据
				 * 并循环获取,直到客户端终止运行
				 * windows系统会发生异常,linux会返回null
				 */
				String msg=null;
				while((msg=br.readLine())!=null) {
					System.out.println(host+"说:"+msg);
					//便利输出流数组,向所有客户端输出信息
					synchronized (allout){
						for (int i = 0; i < allout.length; i++) {
							allout[i].println(host+"说:"+msg);
						}
					}					
				}
			} catch (Exception e) {
			}finally {
				//客户端断开后的操作
				//将当前客户端的输出流从数组中移除
				synchronized (allout) {
					for (int i = 0; i < allout.length; i++) {
						if(allout[i]==pw) {
							allout[i]=allout[allout.length-1];
							allout=Arrays.copyOf(allout, allout.length-1);//缩容
							break;
						}
					}
				}
				System.out.println(host+"下线了,当前在线人数:"+allout.length);
				try {
					socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}	
}

集合框架概述

1 什么是集合框架

java.util.Collection接口以及其子接口和众多实现类

集合框架具备了能够存放各种元素的功能,类似数组

2 为什么需要集合框架

因为集合除了能保存各种元素之外,还封装了对数组元素维护的基本方法

3 集合的分类

Collection下面有两个常用的子接口:

java.util.List:线性表,是一组可以放置重复元素的合集,特点是有序,可以通过下标操作元素,这一点与数组相同,常用实现类:ArrayList, LinkedList

java.util.Set:不可重复集,大部分实现类是无序的,常用实现类:HashSet

4 基本使用
package collection;

import java.util.ArrayList;
import java.util.Collection;
/**
 * 集合框架
 * java.util.Collection接口
 * 能够存放多个元素,类似数组,但是比数组更加方便的操作元素
 * 而且没有长度限制
 * 
 * Java.util.list:有序集合的父接口
 * java.util.set:无序计划的父接口
 * @author Tedu
 *
 */
public class CollectionDemo1 {

	public static void main(String[] args) {
		/*
		 * 集合的基本使用
		 */
		Collection c=new ArrayList();
		/*
		 * 新增操作
		 * booleam add(object obj)
		 * 返回真表示添加成功,否则返回假
		 */
		c.add("one");
		c.add("two");
		c.add("three");
		c.add("four");
		c.add("five");
		
		//输出数组元素
		System.out.println(c);
		
		/*
		 * int size();
		 * 返回当前集合中元素的个数
		 */
		int size=c.size();
		System.out.println("元素个数:"+size);
		
		/*
		 * boolean isEmpty()
		 * 判断是否为空集合,意思是集合中没有任何元素
		 */
		boolean isEmpty=c.isEmpty();
		System.out.println("是空集:"+isEmpty);
		
		/*
		 * 	void clear()
		 * 清空集合中所有元素
		 */
		c.clear();
		System.out.println("集合已清空");
		isEmpty=c.isEmpty();
		System.out.println("是空集:"+isEmpty);
		size=c.size();
		System.out.println("元素个数:"+size);
	}
}

线程高级与集合

原文:https://www.cnblogs.com/xiongchenglong/p/14552937.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!