多线程基础 、 TCP通信
* 当一个方法被synchronized修饰后,那么
* 该方法称为同步方法,即:多个线程不能同时
* 进入到方法内部执行。
package day10;
/**
* 当多线程并发操作同一资源时,由于线程切换的不确定
* 性,可能导致执行顺序的混乱,严重时可能导致系统
* 瘫痪。
* @author adminitartor
*
*/
public class SyncDemo1 {
public static void main(String[] args) {
final Table table = new Table();
Thread t1 = new Thread(){
public void run(){
while(true){
int bean = table.getBean();
Thread.yield();//模拟线程切换
System.out.println(getName()+":"+bean);
}
}
};
Thread t2 = new Thread(){
public void run(){
while(true){
int bean = table.getBean();
Thread.yield();//模拟线程切换
System.out.println(getName()+":"+bean);
}
}
}; t1.start();
t2.start();
}
} class Table{
private int beans = 20;
/**
* 当一个方法被synchronized修饰后,那么
* 该方法称为同步方法,即:多个线程不能同时
* 进入到方法内部执行。
* 在方法上使用synchronized那么锁对象为
* 当前方法所属对象,即:this
* @return
*/
public synchronized int getBean(){
if(beans==0){
throw new RuntimeException("没有豆子了!");
}
Thread.yield();//模拟线程切换
return beans--;
}
}
SyncDemo1.java
* 有效的缩小同步范围可以在保证并发安全的前提下
* 提高并发效率。
package day10;
/**
* 有效的缩小同步范围可以在保证并发安全的前提下
* 提高并发效率。
* @author adminitartor
*
*/
public class SyncDemo2 {
public static void main(String[] args) {
final Shop shop = new Shop();
Thread t1 = new Thread(){
public void run(){
shop.buy();
}
};
Thread t2 = new Thread(){
public void run(){
shop.buy();
}
};
t1.start();
t2.start();
}
} class Shop{
public void buy(){
try {
Thread t = Thread.currentThread();
System.out.println(t.getName()+":正在挑选衣服...");
Thread.sleep(5000);
/*
* 多个线程要保证同步执行代码的前提是
* 这里看到的"同步监视器"即:上锁的对象
* 是同一个。
*/
synchronized(this){
System.out.println(t.getName()+":正在试衣服...");
Thread.sleep(5000);
}
System.out.println(t.getName()+":结账离开");
} catch (Exception e) {
e.printStackTrace();
} }
}
SyncDemo2.java
* 每个类在被JVM加载时,JVM都会创建一个且只创建
* 一个Class类型的实例来表示它。所以,每个类在
* JVM内部都有唯一的一个Class类型的实例对应,而
* 静态方法就是将该Class的实例上锁的。
package day10;
/**
* 静态方法若使用synchronized修饰后,那么该方法
* 一定具有同步效果。
* 静态方法的同步监视器对象为当前类的类对象。
* 类对象:Class类型的实例。
* 每个类在被JVM加载时,JVM都会创建一个且只创建
* 一个Class类型的实例来表示它。所以,每个类在
* JVM内部都有唯一的一个Class类型的实例对应,而
* 静态方法就是将该Class的实例上锁的。
* @author adminitartor
*
*/
public class SyncDemo3 {
public static void main(String[] args) {
Thread t1 = new Thread(){
public void run(){
Foo.dosome();
}
};
Thread t2 = new Thread(){
public void run(){
Foo.dosome();
}
};
t1.start();
t2.start();
}
} class Foo{
public synchronized static void dosome(){
try {
Thread t = Thread.currentThread();
System.out.println(t.getName()+":正在运行dosome方法...");
Thread.sleep(5000);
System.out.println(t.getName()+":运行dosome方法完毕!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
SyncDemo3.java
* 互斥锁
package day10;
/**
* 互斥锁
* 当使用Synchronized修饰多段不同代码,但是同步
* 监视器对象是同一个的时候,那么这些代码间就具有
* 了互斥性,同一时间不能同时执行这些代码。
* @author adminitartor
*
*/
public class SyncDemo4 {
public static void main(String[] args) {
final Boo boo = new Boo();
Thread t1 = new Thread(){
public void run(){
boo.methodA();
}
};
Thread t2 = new Thread(){
public void run(){
boo.methodB();
}
};
t1.start();
t2.start();
}
} class Boo{
public void methodA(){
synchronized(this){
try {
Thread t = Thread.currentThread();
System.out.println(
t.getName()+":正在执行A方法");
Thread.sleep(5000);
System.out.println(
t.getName()+":执行A方法完毕");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void methodB(){
synchronized(this){
try {
Thread t = Thread.currentThread();
System.out.println(t.getName()+":正在执行B方法");
Thread.sleep(5000);
System.out.println(t.getName()+":执行B方法完毕");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
SyncDemo4.java
* 死锁
package day10;
/**
* 死锁
* 双方都持有自己的锁,但都要求对方先释放锁时
* 出现死锁现象。
* @author adminitartor
*
*/
public class SyncDemo5 {
public static void main(String[] args) {
final Coo coo = new Coo();
Thread t1 = new Thread(){
public void run(){
coo.methodA();
}
};
Thread t2 = new Thread(){
public void run(){
coo.methodB();
}
};
t1.start();
t2.start();
}
} class Coo{
private Object lockA = new Object();
private Object lockB = new Object(); public void methodA(){
try {
Thread t = Thread.currentThread();
synchronized (lockA) {
System.out.println(t.getName()+"正在运行A方法");
Thread.sleep(5000);
methodB();
System.out.println(t.getName()+"运行A方法完毕");
} } catch (Exception e) {
}
}
public void methodB(){
try {
Thread t = Thread.currentThread();
synchronized (lockB) {
System.out.println(t.getName()+"正在运行B方法");
Thread.sleep(5000);
methodA();
System.out.println(t.getName()+"运行B方法完毕");
} } catch (Exception e) {
}
}
}
SyncDemo5.java
* 使用Collections的静态方法可以将现有的集合
* 或Map转换为线程安全的
package day10; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set; /**
* 使用Collections的静态方法可以将现有的集合
* 或Map转换为线程安全的
* @author adminitartor
*
*/
public class Sync_API {
public static void main(String[] args) {
/*
* 线程安全的集合自身的add,remove等方法
* 都是同步的,并且之间也有互斥。
* 但是并不与迭代器遍历互斥。所以若并发
* 同时遍历和增删元素,迭代器依然会抛出
* 异常。
* 所以,迭代器与集合元素操作间要自行维护
* 互斥关系。
*
* ArrarList,LinkedList都不是线程安全的
*/
List<String> list
= new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
System.out.println(list);
//将给定的集合转换为一个线程安全的集合
list = Collections.synchronizedList(list); System.out.println(list); Set<String> set
= new HashSet<String>(list);
set = Collections.synchronizedSet(set);
System.out.println(set); Map<String,Integer> map
= new HashMap<String,Integer>();
map.put("语文", 100);
map.put("数学", 99);
map.put("英语", 98); map = Collections.synchronizedMap(map);
System.out.println(map);
}
}
Sync_API.java
* 线程池
package day10; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; /**
* 线程池
* 线程池有两个主要作用:
* 1:控制线程数量
* 2:重用线程
* @author adminitartor
*
*/
public class ThreadPool_Demo {
public static void main(String[] args) {
ExecutorService threadPool
= Executors.newFixedThreadPool(2); for(int i=0;i<5;i++){
Runnable runn = new Runnable(){
public void run(){
Thread t = Thread.currentThread();
System.out.println(t.getName()+":正在运行任务...");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(t.getName()+":运行任务完毕!");
}
};
threadPool.execute(runn);
} //结束线程池
threadPool.shutdown();
System.out.println("线程池结束了!");
}
}
ThreadPool_Demo.java
案例一:
* 封装了TCP通讯的Socket
* 使用它可以与服务端建立连接,并进行通讯
package chat; import java.net.Socket; /**
* 聊天室客户端
* @author adminitartor
*
*/
public class Client {
/*
* 封装了TCP通讯的Socket
* 使用它可以与服务端建立连接,并进行通讯
*
*/
private Socket socket;
/**
* 构造方法,用来初始化客户端
*/
public Client() throws Exception{
/*
* 实例化Socket的过程就是连接服务端的
* 过程。若连接失败,这里会抛出异常
*
* 构造方法的两个参数:
* 1:服务端计算机的IP地址
* 2:服务端应用程序的端口
*/
socket = new Socket(
"localhost",8088
);
}
/**
* 客户端开始工作的方法
*/
public void start(){ } public static void main(String[] args) {
try {
Client client = new Client();
client.start();
} catch (Exception e) {
e.printStackTrace();
System.out.println("客户端运行失败!");
}
}
}
Client.java
* 聊天室服务端
package chat; import java.net.ServerSocket;
import java.net.Socket; /**
* 聊天室服务端
* @author adminitartor
*
*/
public class Server {
/*
* 运行在服务端的ServerSocket,主要作用:
* 1:向操作系统申请服务端口,客户端就是通过
* 这个端口与服务端程序建立连接的
* 2:监听服务端口,一旦客户端连接了,就会创建
* 一个Socket以便与该客户端交互
*/
private ServerSocket server; /**
* 构造方法,用来初始化服务端
* @throws Exception
*/
public Server() throws Exception{
/*
* 初始化,并申请服务端口,若该端口被
* 其他应用程序占用,这里会抛出异常
*/
server = new ServerSocket(8088);
} public void start(){
try {
/*
* ServerSocket提供方法:
* Socket accept()
* 该方法是一个阻塞方法,直到一个客户端
* 通过申请的端口连接上,这里才会返回
* 返回的是一个Socket实例,通过该实例
* 即可与刚连接的客户端交互。
*/
System.out.println("等待客户端连接...");
Socket socket = server.accept();
System.out.println("一个客户端连接了!"); } catch (Exception e) {
e.printStackTrace();
}
} public static void main(String[] args) {
try {
Server server = new Server();
server.start();
} catch (Exception e) {
e.printStackTrace();
System.out.println("服务端运行失败!");
}
}
}
Server.java