1.线程
线程创建
1.继承Thread类实现多线程
run()为线程类的核心方法,相当于主线程的main方法,是每个线程的入口
2.覆写Runnable()接口实现多线程,而后同样覆写run().推荐此方式
a.覆写Runnable接口实现多线程可以避免单继承局限
b.当子类实现Runnable接口,此时子类和Thread的代理模式
(子类负责真是业务的操作,thread负责资源调度与线程创建辅助真实业务。
3.通过线程池启动多线程
4.覆写Callable接口实现多线程(JDK1.5)
a.核心方法叫call()方法,有返回值
b.有返回值为Object类型
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class MyThread implements Callable<String> {
private int count = 20;
@Override
public String call() throws Exception {
for (int i = count; i > 0; i--) {
// Thread.yield();//线程让步,从执行状态到就绪状态
System.out.println(Thread.currentThread().getName()+"当前票数:" + i);
}
return "sale out";
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
Callable<String> callable =new MyThread();
FutureTask <String>futureTask=new FutureTask<>(callable);
Thread mThread=new Thread(futureTask);
Thread mThread2=new Thread(futureTask);
Thread mThread3=new Thread(futureTask);
// mThread.setName("hhh");
mThread.start();
mThread2.start();
mThread3.start();
System.out.println(futureTask.get());
}
synchronized锁
锁代码块
建议使用共享资源为锁对象
对于实例方法建议使用this为锁对象
synchronized(this)
{
代码块
}
对于静态方法建议使用字节码(类名.class)为锁对象
synchronized(类名.class)
{
代码块
}
锁方法
实例方法锁默认使用 this 为锁对象
public synchronized void drawMoney(double money)
{
方法体
}
静态方法默认使用 类名.class 为锁对象
Lock锁
为了更清晰的表达如何加锁和释放锁
Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作
Lock是接口不能直接实例化,采用它的实现类ReentrantLock来构建锁对象
//建议在创建锁对象的时候加final,当final修饰变量的时候该变量只能被赋值一次
//因此锁不容易被撬开,锁对象唯一且不可替换
private final Lock lock=new ReentrantLock();
############################
//上锁
lock.lock();
代码
//解锁,通常放在finally里面
//因为无论是否出现异常都会执行解锁操作
finally
{
lock.unlock();
}
线程通信
线程通信就是线程之间相互发送数据,线程通信通常通过共享一个数据的方式实现
线程间会根据共享数据的情况决定自己该怎么做,以通知其他线程该如何去做
线程通信常见模型:
生产者与消费者模型:生产者生产数据,消费者消费数据
要求:
生产者线程生产完数据之后,唤醒消费者,然后等待;消费者消费完数据之后,唤醒生产者,然后等待
线程池
线程池就是一个可以复用线程的技术,即:
线程池,本质上是一种对象池,用于管理线程资源。
在任务执行前,需要从线程池中拿出线程来执行。
在任务执行完成之后,需要把线程放回线程池。
通过线程的这种反复利用机制,可以有效地避免直接创建线程所带来的坏处。
线程池的优点:
1.降低资源的消耗。线程本身是一种资源,创建和销毁线程会有CPU开销;
创建的线程也会占用一定的内存。
2.提高任务执行的响应速度。任务执行时,可以不必等到线程创建完之后再执行。
3.提高线程的可管理性。线程不能无限制地创建,需要进行统一的分配、调优和监控。
RejectedExecutionHandler handler任务拒绝策略
线程池处理Runnable、Callable任务
处理Runable任务
//创建线程池对象
ExecutorService pool=new ThreadPoolExecutor(3,5,6,
TimeUnit.SECONDS,new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
//给任务让线程池处理
Runnable target=new MyRunnable();
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);
//关闭线程池
pool.shutdownNow();//立即关闭,即使任务没有完成,丢失任务(一般不使用)
pool.shutdown();//等待全部任务执行完毕后再关闭(推荐使用)
处理Callable任务
Future是java 1.5引入的一个interface,可以方便的用于异步结果的获取。
//创建线程池对象
ExecutorService pool=new ThreadPoolExecutor(3,5,6,
TimeUnit.SECONDS,new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
//给任务让线程池处理
Future<String > f1=pool.submit(new MyCallable(100));//f1,2,3,4为未来任务对象
Future<String > f2=pool.submit(new MyCallable(200));
Future<String > f3=pool.submit(new MyCallable(300));
Future<String > f4=pool.submit(new MyCallable(400));
//获取结果
String rs = f1.get();
Executors工具类构建线程池对象(不建议使用)
ExecutorService pool=Executors.newFixedThreadPool(3);
OOM,内存溢出,来源于java.lang.OutOfMemoryError。当JVM因为没有足够的内存来为对象分配空间并且垃圾回收器也已经没有空间可回收时,就会抛出这个error。
并发和并行
正在运行的程序(软件)就是一个进程,线程属于进程,多个线程就是并发与并行同时进行的
并发:cpu分时轮询的执行线程
并行:同一时刻同时在执行
网络编程
Socket是ip地址和端口结合协议
一种地址与端口结合描述协议
涵盖了TCP/UDP
网络通信三要素:ip地址、端口、协议
本机IP:127.0.0.1或loaclhost
端口号:唯一标识正在计算机设备上运行的进程(程序),范围为0到65535
端口类型:
1.周知端口:0到1023 被预先定义的知名应用占用:HTTP 80 ,FTP 21
2.注册端口:1024到49151 分配给用户进程或某些应用程序 Tomcat 8080,MySQL3306
3.动态端口:49152到65535 一般不固定分配某种进程,而是动态分配
TCP协议:传输控制协议
使用TCP协议,双方必须先建立连接,它是面向连接的可靠通信协议
传输前采用三次握手方式建立连接,所以是可靠的
在连接中可进行大数据量的传输
连接、发送数据都需要确认、且传输完毕后,还需要释放已经建立连接,通信效率较低
TCP协议使用场景:
文件下载、金融等数据通信
三次握手:
UDP:用户数据报协议
是一个简单的面向数据报的传输层协议。
提供的是非面向连接的、不可靠的数据流传输。
将数据源IP、目的地IP和端口封装为数据包
每个数据包在64KB内
发送不管对方是否准备好,接收方也不确认
可以广播发送,发送结束无需释放资源,开销小,速度快
使用场景:
语音通话、视频会话
UDP编程
1.数据包
2.发送/接收对象
代码实现:
//客户端
package com.itheima.netprog;
import java.io.IOException;
import java.net.*;
/*
UDP编程客户端
进程之间通过Socket进行通信,Socket封装了
(ip,端口号,协议)三元组
*/
public class ClientDemo1 {
public static void main(String[] args) throws Exception {
//创建发送端对象
DatagramSocket socket=new DatagramSocket();
//创建数据包对象
/*
DatagramPacket对象第一个参数:封装要发送的数据
参数二:发送数据的大小
参数三:服务端ip地址
参数四:服务器的端口
*/
//编码,getBytes()方法将字符进行编码,即将字符串对象转为字节数组
byte[] buffer="我爱你中国".getBytes();
DatagramPacket packet=new DatagramPacket(buffer,buffer.length, InetAddress.getLocalHost(),8888);
//发送数据包出去
socket.send(packet);
//关闭,释放内存
socket.close();
}
}
########################################
//服务器端,在运行时,先运行服务器端,再运行客户端
package com.itheima.netprog;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/*
服务器端
*/
public class SeverDemo1 {
public static void main(String[] args) throws Exception{
//创建接收对象,对象包含接收端的端口号
DatagramSocket socket=new DatagramSocket(8888);
//创建数据包对象
byte[] buffer=new byte[1024];
DatagramPacket packet=new DatagramPacket(buffer,buffer.length);
//等待接收数据
socket.receive(packet);
//接受完之后取出数据,收多少取多少
int len=packet.getLength();
//解码
String re=new String(buffer,0,len);
System.out.println(re);
socket.close();
}
}
TCP编程
TCP客户端:
代码实现:
package com.itheima.netprog;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
/*
TCP编程,客户端
*/
public class Client1 {
public static void main(String[] args) throws Exception {
//创建Socket通信管道请求与服务器端连接
Socket socket=new Socket("127.0.0.1",7777);
//从socket通信管道中得到一个字节输出流,负责发送数据
OutputStream os=socket.getOutputStream();
//将字节流封装为打印流
PrintStream ps=new PrintStream(os);
//发送数据
ps.println("我是TCP客户端");
//把用户程序中的缓冲区数据强制写入到文件或内存变量并清空缓冲区。
ps.flush();
}
}
TCP服务器端
代码实现:
package com.itheima.netprog;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/*
TCP编程服务器端
*/
public class Sever1 {
public static void main(String[] args) throws Exception {
//注册端口, ServerSocket为注册端口类
ServerSocket serverSocket=new ServerSocket(7777);
//调用accept方法等待客户端的socket请求,建立socket通信管道
Socket socket=serverSocket.accept();
//从socket通信管道中得到一个字节输入流
InputStream is=socket.getInputStream();
BufferedReader bs=new BufferedReader(new InputStreamReader(is));
//读取消息
String msg;
while ((msg =bs.readLine())!=null)
{
System.out.println(socket.getRemoteSocketAddress()+msg);
}
}
}
Junit单元测试:
注意:
1.测试方法必须是公开的、无参的、无返回值的方法
2.使用@Test注解
断言:进行预期结果的正确性测试
Assert.assertEquals("测试结束提示的消息","期待值(要和输出结果一模一样)","实际值")
反射
反射是对于任何一个class类,在运行的时候都可以直接得到这个类全部成分
在运行时候,可以直接得到这个类的
1.构造器对象:Constructor
2.成员变量:filed
3.类的方法对象:method
这种运行时动态获取类信息以及动态调用类中成分的能力成为java的反射机制
反射的关键:
第一步先得到编译后的class类对象,然后就可以得到class的全部成分
作用:反射就是在运行的时候获取类的字节码文件,然后可以解析类中的全部成分
反射的步骤
1.获取Class类对象(类的字节码文件,也就是xxx.class),如此才可以解析类的全部成分
获取Class类的对象的三种方式
//第一种
Class c1=Class.forName("全类名");
//第二种
Class c2=类名.class;
//第三种
Class c3=对象.getClass();
c.getSimpleName() 获取当前类名
2.获取构造器对象
//第一步获取类对象
Class c=Student.class;
//提取类中的全部构造器对象(public修饰的构造器)
Constructor[] constructors=c.getConstructors();
//遍历构造器
for(Constructor con:constructors)
{
//getName()获取构造器名,getParameterCount()获取构造器参数个数
System.out.println(con.getName()+"======>"+con.getParameterCount());
}
运行结果:
对于非public的构造器,需要打开权限(暴力反射),然后再创建对象。
反射可以破环封装性,私有的也可以执行了
获取成员变量对象并使用
步骤和获取构造器对象步骤一样
获取完class对象c后,c再调用上述方法获取成员变量
getName() 获取成员变量名
get(类的对象) 获取成员变量的值
getType()方法用于获取对象类型
Class c=Student.class;
Field f=c.getDeclaredField("age");
System.out.println(f.getName()+"====>"+f.getType());
反射的作用:
绕过编译阶段为集合添加数据
因为反射是作用在运行时候的技术,此时集合的泛型将不能产生约束了,此时是可以为集合存入其他任意类型的元素的
根据对象获取类的全部信息
XML
xml文件的数据检索技术:XPath