线程的概念
线程是指在一个进程中执行的独立的、可调度的执行单元。在JAVA中,线程是Thread类的实例,可以通过继承Thread类或实现Runnable接口来创建线程。每个线程都有自己的执行路径和执行状态,可以共享进程的资源,例如内存、文件句柄等,因此线程之间的通信和协作比进程之间更加高效。
线程的结构
在JAVA中,线程的结构通常包括ID、程序计数器、寄存器集合、堆栈和状态等。线程ID是一个唯一标识符,用于区分不同的线程。程序计数器用于当前线程的执行的位置,寄存器集合用于保存线程的上下文信息,堆栈用于保存线程的局部变量和方法用栈,状态用于记录线程的执行状态,例如就行、运行、阻塞等。
线程的创建和启动
在JAVA中,线程的创建和启动可以通过继承Thread类或实现Runnable接口来实现。例如,可以创建一个继承Thread类的子类,并重写run()方法来定义线程的执行逻辑,让后通过start()方法来启动线程。也可以创建一个实现Runnable接口的类,并实现run()方法来定义线程的执行逻辑,然后通过创建Thread对象并传入Runnable对象来启动线程。
线程的同步和协作
在JAVA中,线程的并发执行可能会导致一些问题,例如竞态条件、死锁、饥饿等。为了避免这些问题,需要使用同步机制,例如锁、信号量、条件变量等,来保证线程之间的互斥和协作。
总结
线程的优点:
1、提高系统吞吐率 2、提高IO效率 3、充分利用多核资源
线程的缺点:
1、安全问题 2、资源竞争 3、上下文切换 4、可靠性
继承Thread类创建线程
在Java中,可以通过继承Thread类来创建线程。例如,可以创建一个继承Thread类的子类,并重写run()方法来定义线程的执行逻辑,然后通过调start()方法来启动线程。
线程的生命周期
在Java中,线程的生命周期通常包括新建、就绪、运行、阻塞和终止等状态。新建状态是指线程对象被创建但还没启动的状态;就绪状态是指线程已经准备好执行但还没获得CPU资源的状态;阻塞状态是指线程因为某些原因暂时停止执行的状态;终止状态是指线程完毕或者因为异常等原因被终止的状态。
线程的优先级
在Java中,每个线程都有一个优先级,用于指定线程在竞争CPU资源时的优先级。线程的优先级可以通过setPriority()方法来设置,取值范围为1~10,其中1为最低优先级,10为最高优先级。线程的优先级并不是绝对的,只是一个相对的概念,具体的调度顺序还受到操作系统和CPU的影响。
总结
继承Thread类是Java中多线程编程的一种常见方式,它可以提高程序的性能和响应速度,但也需要注意线程之间同步和协作,以避免出现问题。了解Java中继承Thread类的多线程编程,可以帮助我们更好地理解并发编程的本质,提高程序的可靠性性能。同时,了解线程的优先级、异常处理、停止和调试等方面的知识,可以帮助我们更好地掌握线程编程的技巧和方法。
Main
public class Main { public static void main(String[] args) { TestThread01 testThread01_1=new TestThread01(1,4,"张三"); TestThread01 testThraad01_2=new TestThread01(1,4,"李四"); testThread01_1.start(); testThraad01_2.start(); System.out.println("main Thread finished"); TestThread02 testThread02_1=new TestThread02(3,4,"王五"); System.out.println("线程状态:"+testThread02_1.getState()); testThread02_1.start(); System.out.println("线程状态:"+testThread02_1.getState()); try { Thread.sleep(3000); } catch (InterruptedException e) { throw new RuntimeException(e); } try { testThread02_1.join(); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println("线程状态:"+testThread02_1.getState()); System.out.println("main Thread finished"); TestThread02 testThread02_2=new TestThread02(3,4,"王五"); testThread02_2.setPriority(6); testThread02_2.start(); TestThread03 testThread03 = new TestThread03(); testThread03.setDaemon(true); testThread03.start(); System.out.println("main Thread finished"); } }
|
TestThread01
private int id; private int loopCount; public TestThread01(int pId,int pLoopCount,String pName) { this.id=pId; this.loopCount=pLoopCount; this.setName(pName); } @Override public void run() { for(int i=0;i<loopCount;i++) { System.out.println("Thread Name:"+Thread.currentThread().getName()+", loop:"+i); } }
|
TestThread02
public class TestThread02 extends Thread { private int id; private int loopCount; public TestThread02(int pId,int pLoopCount,String pName) { this.id=pId; this.loopCount=pLoopCount; this.setName(pName); } @Override public void run() { for(int i=0;i<loopCount;i++) { System.out.println("Thread Name:"+Thread.currentThread().getName()+", loop:"+i); try { Thread.sleep(2000); } catch (InterruptedException e) { throw new RuntimeException(e); } } } }
|
TestThread03
public class TestThread03 extends Thread { @Override public void run() { System.out.println("我是守护线程吗?"+this.isDaemon()); while(true) { try { Thread.sleep(100); } catch (InterruptedException e) { throw new RuntimeException(e); } } } }
|
实现Runnable接口
Runnable接口简介
1、Runnable接口位于java.lang包中
2、Runnable接口描述了一个可以作为线程运行的类
3、只包含一个方法:void run()
为什么要使用Runnable接口
1、适用于多线程编程
2、实现Runnable接口的类可以被多个线程执行
3、避免Java单继承限制
如何实现Runnable接口
1、创建一个类,实现Runnable接口
2、重写run()方法,定义线程要执行的任务
3、创建Thread对象,将Runnable实现类的实例作为参数传递
4、调用Thread对象的start()方法启动线程
Runnable接口与Thread类的关系
Runnable接口通常与Thread类一起使用
Thread类实现了Runnable接口
Thread类提供了更多线程管理功能,如设置优先级、线程状态查询等
总结
Runnable接口用于避免单继承局限性
实现Runnable接口的类更方便
与Thread类结合使用,实现灵活的线程管理
public class Main { public static void main(String[] args) { MyRunnable myRunnable = new MyRunnable(); myRunnable.run(); Thread thread01 = new Thread(myRunnable,"thread01"); thread01.start(); Thread thread02 = new Thread(myRunnable,"thread02"); thread01.start(); } }
|
线程启动
使用Thread类创建线程
实现Runnable接口或者继承Thread类
调用start()方法启动线程
线程调度
线程调度由操作系统管理
Java提供了线程优先级设置
优先级范围:1(最低)到10(最高)
线程挂起
使用Thread.sleep方法让线程暂停
使用TimeUnit类让线程暂停
使用LockSupport让线程变相暂停(注意:先解除休眠再休眠不会进入死循环)
import java.util.concurrent.locks.LockSupport;
public class Main { public static void main(String[] args) { MyThread t1 = new MyThread(); MyThread t2 = new MyThread(); MyThread t3 = new MyThread(); t1.setName("a"); t2.setName("b"); t3.setName("c"); t1.setPriority(1); t2.setPriority(5); t3.setPriority(10); t1.start(); t2.start(); t3.start(); MyThreadSleep myThreadSleep = new MyThreadSleep(); myThreadSleep.start(); MyThread t4 = new MyThread(); t4.start();
try { Thread.sleep(4000); } catch (InterruptedException e) { throw new RuntimeException(e); } LockSupport.unpark(t4); MyThread t5 = new MyThread(); t5.start(); LockSupport.unpark(t5); } }
|
总结
线程调度:操作系统管理,Java提供优先级设置
线程挂起:说了3种
线程间的通信
Object:wait()、notify()和notifyAll方法
wait()来自Object
sleep不会释放锁,需要捕捉异常;wait会释放锁
使用synchronized和volatile关键字
volatile修饰变量,多个角色操作同一个变量
synchronized修饰代码段,可以是块,也可以是方法
Condition: await()、signal()和signalAll()方法
使用方法跟wait雷同,升级版
优势1:多个condition条件
优势2:不担心唤醒和等待的顺序问题
Main
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;
public class Main { public static void testWait() { Supermarket sm= new Supermarket();
Thread[] producers = new Thread[1];
for(int i=0;i< producers.length;i++) { producers[i] = new Thread(new Producter(sm),new String(i+"号生产者")); producers[i].start(); }
Thread[] consumers = new Thread[3]; for(int i=0;i< consumers.length;i++) { consumers[i] = new Thread(new Consumer(sm),new String(i+"号消费者")); consumers[i].start(); } } public static void testCondition() { final Lock lock = new ReentrantLock(); final Condition condition01 = lock.newCondition(); final Condition condition02 = lock.newCondition(); final Condition condition03 = lock.newCondition();
new Thread(()->{ System.out.println("线程执行第一步"); lock.lock(); try { condition01.await(); System.out.println("condition01 wake"); Thread.sleep(3000); condition02.signal(); condition03.await(); System.out.println("condition03 wake"); }catch (InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); } }).start(); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } lock.lock(); try { condition01.signal(); condition02.await(); System.out.println("condition02 wake"); Thread.sleep(5000); condition03.signal();
} catch (InterruptedException e) { throw new RuntimeException(e); } finally { lock.unlock(); }
} public static void main(String[] args) { testCondition(); } }
|
Supermarket
public class Supermarket { private int curGoodCount = 0; private int maxGoodCount = 10; public synchronized void add() { if(curGoodCount<maxGoodCount) { curGoodCount++; System.out.println(Thread.currentThread().getName()+"在加紧生产,货架现有"+curGoodCount+"个商品"); notify(); } else { try { wait(); } catch (InterruptedException e) { throw new RuntimeException(e); } } } public synchronized void sub() { if(curGoodCount>0) { curGoodCount--; System.out.println(Thread.currentThread().getName()+"又来购物了,还剩"+curGoodCount+"还剩"+curGoodCount+"个商品"); notify(); } else { try { wait(); } catch (InterruptedException e) { throw new RuntimeException(e); } }
}
}
|
Producer
public class Producter implements Runnable { Supermarket supermarket; public Producter(Supermarket su) { supermarket = su; }
@Override public void run() { while(true) { try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } supermarket.add(); }
} }
|
Consumer
public class Consumer implements Runnable { Supermarket supermarket; public Consumer(Supermarket su) { supermarket = su; } @Override public void run() { while(true) { try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } supermarket.sub(); }
} } public class Consumer implements Runnable { Supermarket supermarket; public Consumer(Supermarket su) { supermarket = su; } @Override public void run() { while(true) { try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } supermarket.sub(); }
} }
|
InetAddress类概述
用途:InetAddress类用于网络编程,它提供了IP地址和主机名之间的映射。
特点:InetAddress类没有公共构造函数,我们只能通过类的方法来创建其对象。
InetAddress类的使用
构造:getByAddress(String host):此方法接受一个主机名作为参数,返回该主机名的InetAddress对象。
使用:getHostAddress():此方法返回Inetddress对象的主机名。如果对象是通过IP地址创建的,那么它将返回IP地址作为主机名。
getLocalHost():此方法返回一个InetAddress对象,该对象表示本地主机。它是获取本地主机名和IP地址的一种快捷方式。
其它:
配合NetworkInterface使用
看不懂、、、对计算机 网络没有概念
import java.net.*; import java.util.Enumeration; import java.util.List;
public class Main { public static String macBytes2String(byte[] buf) { if(buf==null) { return null; } StringBuffer sb = new StringBuffer(); for(int i=0;i<buf.length;i++) { if(i!=0) { sb.append("-"); } String tmp = Integer.toHexString(buf[i]&0xFF); if(tmp.length()==1) { sb.append("0"); } sb.append(tmp); } return sb.toString().toUpperCase(); } public static void main(String[] args) throws UnknownHostException, SocketException { InetAddress inet01 = InetAddress.getByName("www.baidu.com"); System.out.println("Host Name:" + inet01.getHostName()); System.out.println("IP Address:" + inet01.getHostAddress());
InetAddress inet02 = InetAddress.getByName("36.155.132.76"); System.out.println("Host Name:" + inet02.getHostName()); System.out.println("IP Address:" + inet02.getHostAddress());
InetAddress inet03 = InetAddress.getLocalHost(); System.out.println("Host Name:" + inet03.getHostName()); System.out.println("IP Address:" + inet03.getHostAddress()); Enumeration<NetworkInterface> networkInerfaces = NetworkInterface.getNetworkInterfaces(); while(networkInerfaces.hasMoreElements()) { NetworkInterface nif = networkInerfaces.nextElement(); Enumeration<InetAddress>inetAddresses=nif.getInetAddresses(); while(inetAddresses.hasMoreElements()) { InetAddress addr = inetAddresses.nextElement(); List<InterfaceAddress> interfaceAddress=nif.getInterfaceAddresses(); System.out.println(interfaceAddress); System.out.println(nif.getDisplayName()); System.out.println(nif.getName()); System.out.println(addr.getHostName()); System.out.println(addr.getAddress()); byte[] mac = nif.getHardwareAddress(); System.out.println(macBytes2String(mac)); System.out.println(); } } } }
|
URL的概念
URL(统一资源定位器)是互联网上用于秒速信息位置的一种方式。
他是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。
结构:https://blog.csdn.net/xinzi123/arcle/detail8?w=445&id=5
协议://域名:端口/目录/目录?参数1=参数1值&参数2=参数2值
URL的创建
在Java中,我们可以使用Java.net.URL类来创建URL对象
同步请求
同步请求是指客户端发出请求后,必须等待服务器回应才能进行下一步操作。
在此期间,客户端不能做其他操作,这种方式的好处是编程模型简单,缺点是等待服务器响应的时间可能会很长
总结
URL是互联网上的资源地址,java提供了URL类来操作URL
import java.io.*; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.util.stream.Collectors; import java.util.stream.Stream;
public class Main {
public static void get() { try { URL url = new URL("https://www.baidu.com"); HttpURLConnection conn=(HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setRequestProperty("Accept-Charset","UTF-8"); conn.setRequestProperty("Accept","text/html"); conn.connect(); int responseCode = conn.getResponseCode(); if(responseCode==HttpURLConnection.HTTP_OK) { InputStream inputStream=conn.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream,"UTF-8"); Stream<String> stringStream = new BufferedReader(inputStreamReader).lines(); String streamToString = stringStream.collect(Collectors.joining()); System.out.println(streamToString); } else { System.out.println("error Code:"+responseCode); }
} catch (MalformedURLException e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } } public static void post() { try { URL url = new URL("https://www.baidu.com"); HttpURLConnection conn=(HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setRequestProperty("Accept-Charset","UTF-8"); conn.setRequestProperty("Accept","text/html"); conn.setDoInput(true); conn.setDoOutput(true); conn.setUseCaches(false); conn.connect(); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream())); writer.write("ddfsadfasdf"); writer.close();
int responseCode = conn.getResponseCode(); if(responseCode==HttpURLConnection.HTTP_OK) { InputStream inputStream=conn.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream,"UTF-8"); Stream<String> stringStream = new BufferedReader(inputStreamReader).lines(); String streamToString = stringStream.collect(Collectors.joining()); System.out.println(streamToString); } else { System.out.println("error Code:"+responseCode); }
} catch (MalformedURLException e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } } public static void downloadFile() { try { URL url = new URL("https://www.baidu.com"); HttpURLConnection conn=(HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setRequestProperty("Accept-Charset","UTF-8"); conn.setRequestProperty("Accept","text/html"); conn.connect(); int responseCode = conn.getResponseCode(); if(responseCode==HttpURLConnection.HTTP_OK) { InputStream inputStream=conn.getInputStream(); File dir = new File("downloadFileDir"); if(!dir.exists()) { dir.mkdirs(); } File file = new File(dir,"fname.html"); FileOutputStream fos = new FileOutputStream(file); byte[] buffer = new byte[1024]; int len=-1; while((len=inputStream.read(buffer))!=-1) { fos.write(buffer,0,len); } fos.close(); } else { System.out.println("error Code:"+responseCode); }
} catch (MalformedURLException e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } } public static void main(String[] args) { downloadFile(); } }
|
异步请求
Okhttp3的优点
提供了对HTTP/2和SPDY的支持,这使得对同一个主机发出的所有请求都可以共享相同的套接字连接
如果HTTP/2和SPDY不可用,OkHttp会使用连接池来复用连接以提高效率
提供了对GZIP的默认支持来降低传输内容的大小
提供了对HTTP响应的缓存机制,可以避免不必要的网络请求
当网络出现问题是,Okhttp会自动重试一个主机的多个IP地址
Okhttp3使用教程
GRADLE引入包
创建OkHttpClient实例
简单来说,通过OkHttpClient可以发送一个Http请求,并读取该Http请求的响应,它是一个生产Call的工厂。
此外,收益于一个共享的响应缓存/线程池/复用的连接等因素,绝大多数应用使用一个OkHttpClient实例,便可以满足整个应用的Http请求
GET和POST
HTTP头部的设置和读取
HTTP头的数据结构是Map<String,List>类型。也就是说,对于每个HTTP头,可能有多个值。但是大部分HTTP头都只有一个值,只有少部分HTTP头允许多个值
import okhttp3.*; import org.jetbrains.annotations.NotNull;
import javax.net.ssl.*; import java.io.IOException; import java.security.SecureRandom; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Map; import java.util.concurrent.TimeUnit; public class MyNet { private OkHttpClient okHttpClient; public MyNet() { OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); clientBuilder.readTimeout(20, TimeUnit.SECONDS); clientBuilder.connectTimeout(5,TimeUnit.SECONDS); clientBuilder.writeTimeout(60,TimeUnit.SECONDS);
clientBuilder.sslSocketFactory(mySslSocketFactory()); clientBuilder.hostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String s, SSLSession sslSession) { return true; } }); okHttpClient = clientBuilder.build(); } class TrustAllCerts implements X509TrustManager { @Override public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
} @Override public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
} @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } } private SSLSocketFactory mySslSocketFactory() { SSLSocketFactory sslSocketFactory = null; try { SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null,new TrustManager[]{new TrustAllCerts()},new SecureRandom()); sslSocketFactory = sslContext.getSocketFactory();
}catch (Exception ee) { ee.printStackTrace(); } return sslSocketFactory; } public interface MyCallBack { void success(Call call,Response response) throws IOException; void failed(Call call ,IOException e); } public void get(String url,MyCallBack myCallBack) { Request.Builder builder = new Request.Builder(); Request request = builder.get().url(url).build(); Call call = okHttpClient.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(@NotNull Call call, @NotNull IOException e) { myCallBack.failed(call,e);
}
@Override public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { myCallBack.success(call,response);
} }); } public void post(String url, Map<String,String> bodyMap, MyCallBack myCallBack) { } }
|
Socket通信
Socket通信基本步骤
创建Socket
绑定Socket到地址和端口
监听连接
接受连接
发送和接受数据
关闭Socket
Main
import java.io.InputStream; import java.io.OutputStream; import java.net.DatagramSocket; import java.net.ServerSocket; import java.net.Socket;
public class Main { public static void main(String[] args) {
if (false){ try{
ServerSocket serverSocket = new ServerSocket(7890); System.out.println("服务器已启动。");
Socket socket = serverSocket.accept(); System.out.println("客户端已经连接:" + socket.getInetAddress().getHostAddress());
InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream();
byte[] buffer = new byte[1024]; while (true){ int bytesRead = inputStream.read(buffer); String requestData = new String(buffer, 0, bytesRead); System.out.println("接收到客户端发送的数据:" + requestData); if (requestData.equals("exit")){ break; } }
String responseData = "hello client!"; outputStream.write(responseData.getBytes()); System.out.println("向客户端发送响应数据:" + responseData);
socket.close();
}catch (Exception ee){ ee.printStackTrace(); } }
ServerSocket serverSocket = null; Socket socket = null; try{
serverSocket = new ServerSocket(7890); System.out.println("服务器已启动。");
while (true){ socket = serverSocket.accept(); System.out.println("客户端已经连接:" + socket.getRemoteSocketAddress() + ", 端口:" + socket.getPort());
new Thread(new Service(socket)).start(); }
}catch (Exception ee){ ee.printStackTrace(); } } }
|
Clientv1
import java.io.InputStream; import java.io.OutputStream; import java.net.Socket;
public class Clientv1 { public static void main(String[] args) { try{ Socket socket = new Socket("192.168.31.106", 7890);
InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream();
String send1 = "hello server"; outputStream.write(send1.getBytes()); System.out.println("向服务器发送数据:" + send1);
String send2 = "exit"; outputStream.write(send2.getBytes()); System.out.println("向服务器发送数据:" + send2);
byte[] buff = new byte[1024]; int bytesRead = inputStream.read(buff); String recv = new String(buff, 0, bytesRead); System.out.println("接收到服务器返回数据:" + recv);
socket.close();
}catch (Exception ee){ ee.printStackTrace(); }
} }
|
Service
import java.io.*; import java.net.Socket;
public class Service implements Runnable{ private InputStream input = null; private OutputStream output = null; private Socket s = null;
private ObjectOutputStream oos= null; private PrintWriter pw = null;
public Service(Socket s) { this.s = s; }
@Override public void run() { try { input = s.getInputStream(); output = s.getOutputStream();
oos = new ObjectOutputStream(output); oos.writeInt(1314); oos.writeDouble(13.14); Student st = new Student("张三", 567); oos.writeObject(st); oos.flush();
} catch (IOException e) { throw new RuntimeException(e); } } }
|
Student
public class Student { private String sName; private int sId; public Student(String sName, int sId) { this.sName = sName; this.sId = sId; } @Override public String toString() { return "Student{" + "sName='" + sName + '\'' + ", sId=" + sId + '}'; } }
|