多线程并发时,多个线程同时操作同一个内存区域(变量),可能会导致的结果不一致的问题;所谓线程安全,指的是在多线程并发操作的时候能保证共享的变量数据一致
线程并发时需要保证线程安全,需要满足三大条件:
对于一条线程执行来说要保证,要么都成功,要么都失败;对于原子性操作可以通过加锁的方式实现;Synchronized和ReentrantLock保证线程的原子性
多条线程操作同一个变量时,需要保证其中一条线程对变量的修改,对其他线程也是可见的
对于每一条线程的执行,内部应该是有序的:
synchronized的关键字使用包含三种方式:
被synchronized鎖住的区域称之为临界区
由于线程并发,会出现共享变量的情况,如果使用同步锁,对象会被锁住,如果存在多个线程争抢资源时陷入僵局(多个线程在等待被对方线程占有的资源释放时陷入的无限等待状态),这种情况称之为死锁。死锁无法解决,只能尽量避免
public class DeathLock implements Runnable{
/**
* 使用静态修饰的原因是希望意向两个对象永远只存在一个实例(不论有多少Runnable对象)
*/
private static Object obj1 = new Object();
private static Object obj2 = new Object();
private int flag;
public DeathLock(int flag) {
this.flag = flag;
}
@Override
public void run() {
if(flag == 1) {
synchronized (obj1) {
System.out.println("线程1锁住了obj1");
synchronized (obj2) {
System.out.println("线程1锁住了obj2,结束。。。。");
}
}
}else if(flag == 2){
synchronized (obj2) {
System.out.println("线程2锁住了obj2");
synchronized (obj1) {
System.out.println("线程2锁住了obj1,结束。。。。");
}
}
}
}
public static void main(String[] args) {
DeathLock d1 = new DeathLock(1);
DeathLock d2 = new DeathLock(2);
new Thread(d1).start();
new Thread(d2).start();
}
}
提供了跟synchronized相同的功能,也可以对于的对象实现加锁,ReentrantLock粒度控制方面比synchronized更细,同时支持公平锁和非公平锁(默认),synchronized只支持非公平锁
注意事项:
- 锁的获取位置一般在try语句块之前
- 锁的释放一般放到finally语句块中
ReentrantLock和Synchronized的区别:
- ReentrantLock是一个实现类,后者是一个关键字
- ReentracntLock需要手动获取锁以及释放锁(粒度控制更细);后者自动加锁,临界区(synchronized锁住的区域)的代码执行完之后自动释放
- ReentrantLock支持公平锁以及非公平锁;sychronized只支持非公平锁
- synchronized一般用于少量代码同步,ReentrantLock一般用于大量的同步代码块
在前面的线程并发中,对于原子性的解决方案使用synchronized或lock实现同步;但是对于数据的可见性来说,我们还需要另外处理,关于可见性,实时的将全局变量的更新立即同步给其他线程。
线程运行时若需要限时等待,则可以通过sleep()方法设置休眠时间,但是对于一些不定时的等待需求sleep则无法实现;对于这种需求,java.lang.Object类中提供了用于实现等待和唤醒的方法:wait和notify;
在使用wait和notify的时候一定要求确保当前线程持有对象的监视器(对象锁)
wait()和sleep()区别?
- sleep是来自Thread类中的一个方法;wait是属于Object类的方法
- sleep是限时等待,在等待的时限到达时自动恢复;而wait是无限等待,必须要等到其他线程调用该对象上的notify方法(或notifyAll)才能继续执行
- sleep使用时不需要持有对象锁,而wait使用时必须确保当前线程持有该对象的对象锁,否则会有异常(java.lang.IllegalMonitorStateException)
网络协议就是计算器网络设备相互之间通信的标准(暗号),在互联网标准组织的推动下,将网络协议分为两种:
传输控制协议,是一个安全可靠的互联网协议,需要通信的主机之间需要先建立正确的链接,才能够进行通信,并且改协议能够保证数据传输稳定性(必须的保证信息发送到一台主机,由该主机确认之后才能发送下一条信息),另外该协议也能保证数据传输的有序性(先发送的信息一定先到达)。一般基于C/S架构,存在服务器客户端模式。
应用领域: 远程登录
User Diagram Protocol(用户数据报协议),是一个不安全的网络协议,不需要双方之间建立联系,也不保证信息传输的有序性(有可能后发消息先到),传输效率比TCP/IP更高.没有专门服务器和客户端,只有发送端和接收端
应用领域: 广播,视频会议
IndetAddress是位于java.net包中提供的用于表示ip地址和主机的类,常用方法:
Socket(套接字),实际上就是由IP地址跟端口号的结合,通过Socket对象可以实现两台主机之间的通信;Socket分为服务端Socket(java.net.ServerSocket),以及客户端Socket(java.net.Socket)
/**
* 服务器
* @author mrchai
*
*/
public class FileServer {
public static void main(String[] args) throws IOException {
//准备需要传输的文件对象
File target = new File("D:\\集团资料\\宣讲\\video\\云计算&大数据\\2017云栖.mp4");
try(
//创建并开启文件服务
ServerSocket ss = new ServerSocket(6789);
Socket s = ss.accept();
//创建文件的输入流并包装为缓冲流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(target));
//获取基于Socket的输出流
BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());
){
System.out.println("客户端连接:"+s.getInetAddress().getHostAddress());
System.out.println("开始传输....");
//每次读取的字节内容
int b = 0;
while((b = bis.read()) != -1) {
bos.write(b);
}
System.out.println("传输完成");
}catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 客户端
* @author mrchai
*
*/
public class FileClient {
public static void main(String[] args) throws UnknownHostException, IOException {
File f = new File("C:\\Users\\Administrator\\Desktop\\a.mp4");
try(
//连接指定ip指定端口服务
Socket s = new Socket("192.168.7.141",6666);
//获取基于Socket的输入流并包装为缓冲流
BufferedInputStream bis = new BufferedInputStream(s.getInputStream());
//获取目标文件的输出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(f));
){
System.out.println("开始接收...");
int b = 0;
while((b = bis.read()) != -1) {
bos.write(b);
}
System.out.println("接收完成!");
}catch (Exception e) {
e.printStackTrace();
}
}
}
HttpURLConnection用于获取一个http连接,是一个http客户端工具类,HttpURLConnection从URLConnection继承而来,通过该类可以读取服务端响应的数据(文本,视频,音频,图片等资源)。
对于大多数请求,主要是获取服务端文本数据,因此封装以下工具类用户获取网络资源:
/**
* HTTP工具类,可以通过该工具类轻松访问在线API地址
* 并获取响应的数据
* @author mrchai
*/
public class HttpUtils {
static class CallRemoteData implements Callable<String>{
private String url;
public CallRemoteData(String url) {
this.url = url;
}
@Override
public String call() throws Exception {
//根据提供的url地址构建一个URL对象
URL urlConn = new URL(url);
//打开连接
HttpURLConnection conn = (HttpURLConnection)urlConn.openConnection();
//设置请求方式
conn.setRequestMethod("GET");
//获取响应的状态码
int code = conn.getResponseCode();
if(code == HttpURLConnection.HTTP_NOT_FOUND) {
throw new Exception("请求的资源不存在!");
}
if(code == HttpURLConnection.HTTP_INTERNAL_ERROR) {
throw new Exception("服务器内部错误");
}
if(code != HttpURLConnection.HTTP_OK) {
throw new Exception("未知错误!");
}
BufferedReader br = null;
try {
//获取连接的输入流
InputStream is = conn.getInputStream();
InputStreamReader isr = new InputStreamReader(is,"utf-8");
br = new BufferedReader(isr);
//创建一个可变长字符串
StringBuffer sbf = new StringBuffer();
String str = "";
while((str = br.readLine()) != null) {
sbf.append(str).append("\r\n");
}
return sbf.toString();
}finally{
if(br != null)br.close();
//断开连接
conn.disconnect();
}
}
}
/**
* 根据提供的url地址字符串获取服务端响应回来的字符串数据
* @param url
* @return
*/
public static String getData(String url) {
try {
//创建Callable对象
Callable<String> call = new CallRemoteData(url);
FutureTask<String> task = new FutureTask<>(call); //Runnable
//创建并启动线程
new Thread(task).start();
//获取执行结果并返回
return task.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
String url = "http://music.softeem.top/list";
String json = HttpUtils.getData(url);
System.out.println(json);
}
}
JSON(JavaScript Object Notation)是一种与语言平台无关的数据交换格式,有着比XML更为轻量级的数据表现形式,是目前大多数互联网应用程序之间的数据交换格式的首选。
fastJSON是由阿里巴巴开源的JSON库,号称全世界最快的json插件,常用方法:
public static void main(String[] args) {
User u = new User();
u.setId(10);
u.setUsername("softeem");
u.setPassword("123456");
u.setVip(true);
//1.将Java对象转换为json字符串
String json =JSON.toJSONString(u);
System.out.println(json);
//2.将json字符串转化为Java对象
json = "{\"id\":20,\"password\":\"666\",\"username\":\"admin\",\"vip\":false}";
u = JSON.parseObject(json, User.class);
System.out.println(u);
//3.如何将集合转换为json字符串
List<User> list = new ArrayList<>();
list.add(new User(1, "softeem", "123456", true));
list.add(new User(2, "james", "000123", false));
list.add(new User(3, "kobe", "888123", false));
list.add(new User(4, "面筋哥", "666123", true));
json = JSON.toJSONString(list);
System.out.println(json);
//4.如何将一个json数组转换为一个Java集合
list = JSON.parseArray(json,User.class);
System.out.println(list);
//5.若解析的json字符串找不到对应的Java类时?
String json2 = "{\"sid\":10,\"name\":\"孙悟空\"}";
//将json字符串解析为Java对象;JSONObject实际上就是一个Map集合
JSONObject obj = JSON.parseObject(json2);
int sid = obj.getInteger("sid");
String name = obj.getString("name");
System.out.println(sid+"---"+name);
}
注解(Annotation),是jdk5之后新增一项技术,可以通过在Java类,方法,属性等元素上加上注解,实现一些特定功能:编译检查,丰富文档化注释的内容,实现项目特定程序配置。注解只需要少量代码的存在即可;注释即解释;注解通常不会影响程序的正常逻辑,只是一种标记,Java中的注解通常是给编译器进行识别的
jdk中包含一些内置的注解类型:
java中除了包含一些内置注解以外,还允许开发者自定义注解来解决程序配置问题,如果需要使用自定义注解,只需要使用@interface
创建即可:
public @interface MyAnno { }