线程安全问题
线程安全,必然会带来性能的降低,线程安全,适合多线程访问。
线程不安全,相对于线程安全性能较高,线程不安全,适合单线程访问。
线程池
1、池的本质,就是一种“缓存”技术,是否要缓存一个对象,取决于该对象的创建成本,但是使用缓存技术也要考虑系统内存大小;缓存的本质就是牺牲空间换取时间,线程对象的成本较大,所以在适当的时候选择使用线程池。
2、使用线程池的原因:多线程运行时间,系统不断的启动和关闭新线程,成本非常高,会过渡消耗系统资源,以及过渡切换线程的危险,从而导致系统资源的崩溃,这时,线程池就是最好的选择。
3、线程池的概念:线程池就是预先在内存中创建好一些线程,它们的集合称为线程池,使用线程池可以很好的提高性能,线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动一个线程来执行这个任务,执行结束以后,该线程并不会死亡,而是再次返回线程池中成为一个空闲状态,等待执行下一个任务。
4、线程池的工作机制:在线程池的编程模式下,任务是提交给整个线程池,而不是直接提交给某个线程,线程池在拿到任务后,就在内部寻找是否有空闲的线程,如果有,则将任务交给这个空闲的线程;一个线程同时只能执行一个任务,但是可以同时向一个线程池提交多个任务。
package com.wlx.day12;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadPoolDemo
{public void threadpool()throws Exception{//创建线程池对象ExecutorService threadpool = Executors.newCachedThreadPool();//使用线程池中的线程threadpool.execute(new Runnable() {@Overridepublic void run() {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"---");}});//使用线程完成以后,让其空闲1000毫秒Thread.sleep(1000);//再次使用线程池threadpool.execute(new Runnable() {@Overridepublic void run() {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"***");}});//使用shutdown()关闭线程池threadpool.shutdown();}public static void main(String[] args)throws Exception{new ThreadPoolDemo().threadpool();}}
ThreadLocal
ThreadLocal采用了“以空间换时间”的方式,每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个变量,这样就避免了多个线程对共享数据/共享资源的操作发生错误的问题。
package com.wlx.day12;/*** 使用多线程完成飞机购票,某订票系统还剩5张票,现有3个人去买票,* 在买票时,会出现系统延迟的情况,请使用程序实现3人购票的情况。*/
public class Ticket implements Runnable
{//定义一个变量用于缓存剩余的5张票private Integer p = 5;//创建ThreadLocal对象private static final ThreadLocal<Integer> local = new ThreadLocal<>();@Overridepublic void run(){for (int i = 0; i < 10; i++){buyTicket();}}/*** 获取ThreadLocal中的对象* @return*/public Integer getTicket(){if(local.get() == null){local.set(p);}return local.get();}/*** 买票的方法*/private void buyTicket() {//获取ThreadLocal中的票数Integer count = getTicket();if(count > 0){try {System.out.println("系统延迟中...");Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"---买票"+(count--));local.set(count);}}public static void main(String[] args){Ticket t = new Ticket();//创建3个线程代表3个人Thread tom = new Thread(t,"tom");Thread jim = new Thread(t,"jim");Thread lilei = new Thread(t,"lilei");//3个人去买票tom.start();jim.start();lilei.start();}
}
volatile
volatile解决脏读(可见性),不会重排序,但是不能保证原子性,如果要保证原子性还是需要使用加锁,或使用ThreadLocal。
网络编程
端口号取值范围:0~65535,建议使用9999以上的端口号。
TCP编程
TCP编程,在发送和接收数据时,必须等待服务端启动后,才可以进行链接,然后才可以进行信息发送,发生数据时,不会出现丢失数据报包的情况。
服务端
package com.wlx.day12;import java.io.DataInputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;/*** 服务器端*/
public class Server
{public static void main(String[] args)throws Exception{//创建服务器端对象ServerSocket serverSocket = new ServerSocket(10002);System.out.println("等待客户端链接...");while (true){//等待客户端链接Socket s = serverSocket.accept();//创建字节输入流对象InputStream inputStream = s.getInputStream();//创建DataInputStream对象DataInputStream dis = new DataInputStream(inputStream);System.out.println("IP:"+s.getInetAddress()+"\t 说:"+dis.readUTF());}}
}
客户端
package com.wlx.day12;import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;/*** 客户端*/
public class Client
{public static void main(String[] args)throws Exception{//创建客户端对象Socket s = new Socket("192.168.219.1",10001);//创建字节输出流对象OutputStream os = s.getOutputStream();//创建DataOutputStream对象DataOutputStream dos = new DataOutputStream(os);//向服务端发送信息dos.writeUTF("hello server");}
}
图形化界面
package com.wlx.day12;import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.time.LocalDateTime;public class FrameServer
{public static void main(String[] args){//创建图形化界面的顶层窗口对象JFrame jf = new JFrame("聊天窗口");//创建聊天窗口的文本域窗口对象JTextArea jta = new JTextArea();//创建滚动条对象,并将jta添加进来JScrollPane jsp = new JScrollPane(jta);//创建一个JPanel对象JPanel jp = new JPanel();//创建文本框对象JTextField jtf = new JTextField(20);//创建按钮对象JButton jButton = new JButton("发送");jp.add(jtf);jp.add(jButton);//将jsp以及文本域添加到顶层窗口中,默认添加到中间位置jf.add(jsp);//添加到jf的最下面jf.add(jp,BorderLayout.SOUTH);//设置窗口大小jf.setSize(600,300);//设置显示位置jf.setLocation(300,300);//设置窗口可见jf.setVisible(true);//禁止最大化jf.setResizable(false);//设置窗口关闭按钮可以进行关闭jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);String ip = "192.168.219.1";int port = 10002;//添加文本框监听处理jtf.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {try {//创建客户端对象Socket s = new Socket(ip,port);//创建字节输出流对象OutputStream os = s.getOutputStream();//创建DataOutputStream对象DataOutputStream dos = new DataOutputStream(os);String msg = jtf.getText();jtf.setText("");//向服务端发送信息dos.writeUTF(msg);} catch (IOException ex) {ex.printStackTrace();}}});//为按钮添加监听jButton.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {try {//创建客户端对象Socket s = new Socket(ip,port);//创建字节输出流对象OutputStream os = s.getOutputStream();//创建DataOutputStream对象DataOutputStream dos = new DataOutputStream(os);String msg = jtf.getText();jtf.setText("");//向服务端发送信息dos.writeUTF(msg);} catch (IOException ex) {ex.printStackTrace();}}});try {//创建服务器端对象ServerSocket serverSocket = new ServerSocket(port);jta.append("服务器已启动,等待客户端链接...");jta.append("\n");while (true){//等待客户端链接Socket s = serverSocket.accept();//创建字节输入流对象InputStream inputStream = s.getInputStream();//创建DataInputStream对象DataInputStream dis = new DataInputStream(inputStream);jta.append(LocalDateTime.now()+" :"+s.getInetAddress()+" 说:"+dis.readUTF()+"\n");jta.append("\n");}} catch (IOException e) {System.out.println("出现异常了...");}}
}
UDP编程
UDP编程,在发送和接收数据时,不用等待链接后再发送,可以直接发送,数据报包丢失对于UDP是正常现象。
卖家
发送数据。
package com.wlx.day12;import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;/*** UDP卖家*/
public class Seller
{public static void main(String[] args)throws Exception{DatagramSocket ds = new DatagramSocket(10003);byte[] b = "hello".getBytes();InetAddress ia = InetAddress.getLocalHost();System.out.println(ia);//10004是买家的端口号DatagramPacket dp = new DatagramPacket(b,b.length,ia,10004);ds.send(dp);System.out.println("包裹已发出");}}
买家
接收数据
package com.wlx.day12;import java.net.DatagramPacket;
import java.net.DatagramSocket;/*** UDP买家*/
public class Buyer
{public static void main(String[] args)throws Exception{DatagramSocket ds = new DatagramSocket(10004);byte[] b = new byte[1024];DatagramPacket dp = new DatagramPacket(b,b.length);ds.receive(dp);String s = new String(b);System.out.println("接收到的包裹是:"+s);}
}
内部类(了解)
定义:
定义在类的内部的类就是内部类,即定义在一个类的类体中或一个方法中的类就是内部类。
实例内部类
静态内部类
局部内部类
匿名内部类
1.1、实例内部类
定义:
没有被static静态修饰的,直接定义在类体中的一个类就是实例内部类。
实例内部类的实例对象是在所在类的实例基础上进行创建的。
实例内部类中的实例方法,调用外部类中的实例成员可以通过如果下格式调用:
外部类名.this.实例成员
实例内部类中也可以编写静态成员。
实例内部类中的静态成员使用和外部类类似,不能在静态成员中使用this关键字。
1.2、静态内部类
定义:
被static静态修饰的,直接定义在类体中的一个类就是静态内部类。
静态内部类中在使用外部类的成员时,和前面一样,只有一点,不能直接在静态内部类中使用外部类的this关键字。
1.3、匿名内部类
格式:
new 子类或实现类()
{
子类类体/实现类类体
};
备注:
子类用的是父类的类名。
实现类用的是接口的类名。
备注:
匿名内部类中,除了不能写构造方法,其他都可以写,但是还是要使用多态的方式访问匿名内部类中的方法,由于属性没有多态,匿名内部类中的属性只能在匿名内部类中使用。
package com.wlx.day12;/*** 外部类*/
public class ClassDemo
{String username = "jim";static int uage = 678;String usersex = "man";static int userage = 89;/*** 定义一个实例内部类*/public class InnerClass{private static String username = "tom";private int userage = 123;public InnerClass(){System.out.println("InnerClass---");}public static void staticMethod(){ClassDemo cd = new ClassDemo();System.out.println("staticMethod---");System.out.println("uage---"+uage);System.out.println("usersex ---"+cd.usersex);System.out.println("cd.username ---"+cd.username);}public void noStaticMethod(){System.out.println("no Static Method ");System.out.println("uage---"+uage);System.out.println("usersex ---"+usersex);System.out.println("ClassDemo.this.username ---"+ClassDemo.this.username);System.out.println("ClassDemo.userage---"+ClassDemo.userage);}}public void outMethod(){//创建内部类对象InnerClass ic = new InnerClass();System.out.println(ic.userage);ic.noStaticMethod();//System.out.println(InnerClass.username);InnerClass.staticMethod();}public static class StaticInnerClass{private static String username2 = "tom";private int userage2 = 123;public StaticInnerClass(){System.out.println("InnerClass---");}public static void staticMethod2(){ClassDemo cd = new ClassDemo();System.out.println("staticMethod---");System.out.println("uage---"+uage);System.out.println("usersex ---"+cd.usersex);System.out.println("cd.username ---"+cd.username);}public void noStaticMethod2(String username2){ClassDemo cd = new ClassDemo();System.out.println("no Static Method ");System.out.println("uage---"+uage);System.out.println("usersex ---"+cd.usersex);System.out.println("cd.username ---"+cd.username);System.out.println("ClassDemo.userage---"+ClassDemo.userage);System.out.println(this.userage2);}}public void outMethod2(){StaticInnerClass sic = new StaticInnerClass();System.out.println(sic.userage2);sic.noStaticMethod2("");//System.out.println(InnerClass.username);StaticInnerClass.staticMethod2();}public void hello(int a){//定义局部变量,默认情况下,局部变量在被局部内部类使用时,被final修饰String str = "jbbl";int abc = a;class HelloWorld{private static String username = "tom";private int userage = 123;public HelloWorld(){System.out.println("InnerClass---");}public static void staticMethod(){ClassDemo cd = new ClassDemo();System.out.println("staticMethod---");System.out.println("uage---"+uage);System.out.println("usersex ---"+cd.usersex);System.out.println("cd.username ---"+cd.username);}public void noStaticMethod(){System.out.println("no Static Method ");System.out.println("uage---"+uage);System.out.println("usersex ---"+usersex);System.out.println("ClassDemo.this.username ---"+ClassDemo.this.username);System.out.println("ClassDemo.userage---"+ClassDemo.userage);System.out.println(str);System.out.println(abc);System.out.println(a);}}HelloWorld helloWorld = new HelloWorld();}public static void main(String[] args) {//创建实例内部类对象
// InnerClass innerClass = new ClassDemo().new InnerClass();//等价于ClassDemo cd = new ClassDemo();//创建实例内部类对象InnerClass innerClass = cd.new InnerClass();cd.outMethod();//创建匿名内部类InterfaceDemo id = new InterfaceDemo(){static int userage = 123;static String username = "tom";static{System.out.println("static----");}{System.out.println("8888");}public void abstractorMethod(){}public static void hello(){}public void world(){}};System.out.println(InterfaceDemo.userage);}}