线程安全问题
线程安全,必然会带来性能的降低,线程安全,适合多线程访问。
线程不安全,相对于线程安全性能较高,线程不安全,适合单线程访问。
线程池
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() {
@Override
public 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() {
@Override
public 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<>();
@Override
public 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() {
@Override
public 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() {
@Override
public 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);
}
}