java多线程的学习
Java多线程编程
Thread类实现多线程
class MyThread extends Thread{
private String title;
public MyThread(String title){
this.title = title;
}
@Override
public void run(){
for (int x = 0; x < 10; x ++){
/*try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
System.out.println(this.title + "运行,x=" + x);
}
}
}
public class ThreadDemo {
public static void main(String[] args){
new MyThread("线程A").start();
new MyThread("线程B").start();
new MyThread("线程C").start();
/*MyThread mt = new MyThread("线程A");
mt.start();
mt.start();*/
}
}
Runnable接口实现多线程
class MyRunnable implements Runnable{
private String title;
public MyRunnable(String title){
this.title = title;
}
@Override
public void run(){
for (int x = 0; x < 10; x ++){
/*try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
System.out.println(this.title + "运行,x=" + x);
}
}
}
public class ThreadRunnableDemo {
public static void main(String[] args){
/*new MyRunnable("线程A").start();
new MyRunnable("线程B").start();
new MyRunnable("线程C").start();*/
//MyRunnable mt = new MyRunnable("线程A");
//mt.start();
Thread thread = new Thread(new MyRunnable("线程对象"));
thread.start();
/* for (int x = 0; x < 3; x++) {
String title = "线程对象-" + x;
new Thread(()->{
for (int y = 0; y < 10; y++) {
System.out.println(title + "运行,y=" + y);
}
}).start();
}*/
}
}
Thread与Runnable的关系
卖票:
class MyThreadTicket implements Runnable{//线程的主体类
private int ticket = 10;
@Override
public void run() {//线程的主体方法
for (int x = 0; x < 100; x++) {
if(this.ticket > 0){
System.out.println("卖票,ticket = " + this.ticket --);
}
}
}
}
public class ThreadTicket {
public static void main(String[] args){
MyThreadTicket mt = new MyThreadTicket();
new Thread(mt).start();//第一个线程启动
new Thread(mt).start();//第二个线程启动
new Thread(mt).start();//第三个线程启动
}
}
Callable接口实现多线程
public static void main(String[] args) throws Exception{
Thread thread = new Thread(()->{
System.out.println("***72小时的疯狂我需要睡觉补充精力。");
try {
Thread.sleep(1000);
System.out.println("***睡足了,继续工作。");
} catch (InterruptedException e) {
//e.printStackTrace();
System.out.println("敢打扰我睡觉。");
}
});
thread.start();
Thread.sleep(1000);
if(!thread.isInterrupted()){
System.out.println("我偷偷的打扰一下");
thread.interrupt();
}
}
多线程运行状态
线程常用操作方法
多线程的主要操作方法都在Thread类中定义
线程的命名和取得
多线程的运行状态是不确定的,所以要获取线程,就需要根据线程的名称来获取。Thread类提供有线程名称的处理。
- 构造方法:public Thread(Runnable target, String name);
- 设置名字:public final void setName(String name);
- 取得名字:public final String getName();
- 获取当前线程:public static Thread currentThread();
class MyThreadName implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
public class ThreadName {
public static void main(String[] args) throws Exception{
MyThreadName mt = new MyThreadName();
new Thread(mt, "线程A").start();
new Thread(mt).start();
new Thread(mt, "线程C").start();
}
}
线程休眠
Thread.sleep(1000);
线程中断
xxxThread.isInterrupted();
xxxThread.interrupt();
线程强制执行
xxxThread.join()
线程礼让
Thread.yield();
线程优先级
Thread.yield();
线程的同步与死锁
在多线程的处理中,可以利用Runnable描述多个线程操作的资源,而Thread秒速每一个线程对象,于是当多个线程访问同一资源的时候如果处理不当,就会产生错误的操作。
同步问题引出
下面编写一个简单的卖票程序,将创建若干个线程对象实现卖票的处理操作。
范例:实现卖票操作:
class MyThreadTicket implements Runnable{//线程的主体类
private int ticket = 10;
@Override
public void run() {//线程的主体方法
while (true){
if(this.ticket > 0){
System.out.println(Thread.currentThread().getName() + "卖票,ticket = " + this.ticket --);
} else {
System.out.println("---------票已经卖光了");
break;
}
}
}
}
public class ThreadTicket {
public static void main(String[] args){
MyThreadTicket mt = new MyThreadTicket();
new Thread(mt, "票贩子A").start();//第一个线程启动
new Thread(mt, "票贩子B").start();//第二个线程启动
new Thread(mt, "票贩子C").start();//第三个线程启动
}
}
此时的程序将创建3个线程对象,并且这三个线程对象将进行10张票的出售,此时的程序在进行卖票处理的时候并没有任何的问题(假象),下面可以模拟一下卖票中的延迟操作。
class MyThreadTicket implements Runnable{//线程的主体类
private int ticket = 10;
@Override
public void run() {//线程的主体方法
while (true){
if(this.ticket > 0){
try {
Thread.sleep(10);//模拟网络延迟
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖票,ticket = " + this.ticket --);
} else {
System.out.println("---------票已经卖光了");
break;
}
}
}
}
public class ThreadTicket {
public static void main(String[] args){
MyThreadTicket mt = new MyThreadTicket();
new Thread(mt, "票贩子A").start();//第一个线程启动
new Thread(mt, "票贩子B").start();//第二个线程启动
new Thread(mt, "票贩子C").start();//第三个线程启动
}
}
加了短短的延迟后,问题出现了。实际这个问题一直都在。要解决这个问题,就必须使用同步,所谓的同步就是指多个操作在同一个时间段内只能有一个线程进行,其它的线程要等待此线程完成之后才可以继续执行。
线程同步处理
解决同步问题的关键是锁。锁是指当某一个 线程执行操作的时候,其它线程外面等待。如果要想在程序中实现锁的功能,就可以使用synchronized关键字来实现,利用此关键字可以定义同步方法或同步代码块,在同步代码块的操作里面的代码只允许一个线程执行。
1、利用同步代码块进行处理:
synchronized(同步对象){
同步代码操作;
}
一般要进行同步对象处理的时候可以采用当前对象this进行同步。
范例:利用同步代码块解决数据访问问题
class MyThreadTicket implements Runnable{//线程的主体类
private int ticket = 10;
@Override
public void run() {//线程的主体方法
while (true){
synchronized(this){//每次执行只允许一个线程执行
if(this.ticket > 0){
try {
Thread.sleep(10);//模拟网络延迟
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖票,ticket = " + this.ticket --);
} else {
System.out.println("---------票已经卖光了");
break;
}
}
}
}
}
public class ThreadTicket {
public static void main(String[] args){
MyThreadTicket mt = new MyThreadTicket();
new Thread(mt, "票贩子A").start();//第一个线程启动
new Thread(mt, "票贩子B").start();//第二个线程启动
new Thread(mt, "票贩子C").start();//第三个线程启动
}
}
加入同步处理后,程序的整体性能下降了。
2、利用同步方法解决:只需要在方法的定义上使用synchronized关键字即可。
class MyThreadTicket implements Runnable{//线程的主体类
private int ticket = 10;
public synchronized boolean sale(){
if(this.ticket > 0){
try {
Thread.sleep(10);//模拟网络延迟
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖票,ticket = " + this.ticket --);
return true;
} else {
System.out.println("---------票已经卖光了");
return false;
}
}
@Override
public void run() {//线程的主体方法
while (this.sale()){
}
}
}
public class ThreadTicket {
public static void main(String[] args){
MyThreadTicket mt = new MyThreadTicket();
new Thread(mt, "票贩子A").start();//第一个线程启动
new Thread(mt, "票贩子B").start();//第二个线程启动
new Thread(mt, "票贩子C").start();//第三个线程启动
}
}
在日后学习java类库的时候,系统中许多的类上使用的同步处理采用的都是同步方法。
线程死锁
死锁是在进行多线程同步的处理中有可能产生的一种问题,所谓的死锁指的是若干个线程彼此互相等待的状态。线面通过一个简单的代码来观察一下死锁的表现形式
范例:死锁的展示
public class DeadLock implements Runnable {
private Jian jj = new Jian();
private XiaoQiang xq = new XiaoQiang();
@Override
public void run() {
jj.say(xq);
}
public DeadLock() {
new Thread(this).start();
xq.say(jj);
}
public static void main(String[] args){
new DeadLock();
}
}
class Jian {
public synchronized void say(XiaoQiang xq) {
System.out.println("贱説:此路是我开,要想从此过,留下买路财");
xq.get();
}
public synchronized void get() {
System.out.println("贱説:拿到了钱,此路让开");
}
}
class XiaoQiang {
public synchronized void say(Jian jj){
System.out.println("小强説:想要留下买路财,先让我过去");
jj.get();
}
public synchronized void get() {
System.out.println("小强説:逃过去了");
}
}
现在死锁造成的主要原因是,彼此都在相互等待着,等待着对方先让出资源。死锁实际是开发中出现的不确定的状态,有的时候如果代码处理不当,则会不定期出现死锁。这是属于正常开发中的调试问题。
若干个线程访问同一资源时一定要进行同步处理,而过多的同步会造成死锁。
综合实战: “生产者-消费者”模型
在多线程的开发过程中最为著名的案例就是生产者与消费者操作,该操作的主要流程如下:
- 生产者负责内容的生产;
- 每当生产者完成一项完整的信息之后消费者要从这里取走信息;
- 如果生产者没有生产完成则消费者需要等待它生产完成,如果消费者还没有对信息进行消费,则生产者应该等待消费者完成消费后,再继续生产。
生产者与消费者基本程序模型
可以将生产者与消费者定义成两个独立的线程类对象,但是对于现在生产的数据,可以使用如下的流程:
- 数据一:title = 张三、content = 帅气;
- 数据二:title = 李四、content = 漂亮;
既然生产者和消费者是两个独立的线程,那么这两个独立的线程之间就需要一个数据的保存集中点,那么可以单独定义一个Message类实现数据的保存。
![image-20210222210007226](Screenshot from 2021-02-22 21-02-29.png)
范例:实现数据的基本结构
class Producer implements Runnable {
private Message msg;
public Producer(Message msg) {
this.msg = msg;
}
@Override
public void run() {
for (int x = 0; x < 100; x++) {
if (x % 2 == 0) {
this.msg.setTitle("张三");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.msg.setContent("帅气");
} else {
this.msg.setTitle("李四");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.msg.setContent("漂亮");
}
}
}
}
class Consumer implements Runnable {
private Message msg;
public Consumer(Message msg) {
this.msg = msg;
}
@Override
public void run() {
for (int x = 0; x < 100; x++) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.msg.getTitle() + " - " + this.msg.getContent());
}
}
}
class Message {
private String title;
private String content;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
public class ThreadMessage {
public static void main(String[] args) throws Exception {
Message msg = new Message();
new Thread(new Producer(msg)).start(); //启动生产者线程
new Thread(new Consumer(msg)).start(); //启动消费者线程
}
}
通过这个代码执行发现有两个问题:
- 数据不同步了;
- 出现重复生产和重复取出
解决生产者-消费者同步问题
首先解决数据的额同步问题,最简单的方式是使用synchronized关键字定义同步代码块或同步方法。于是这个时候对于同步的处理就可以直接在Message类中完成。
范例: 解决同步操作
class Producer implements Runnable {
private Message msg;
public Producer(Message msg) {
this.msg = msg;
}
@Override
public void run() {
for (int x = 0; x < 100; x++) {
if (x % 2 == 0) {
this.msg.set("张三", "帅气");
} else {
this.msg.set("李四", "漂亮");
}
}
}
}
class Consumer implements Runnable {
private Message msg;
public Consumer(Message msg) {
this.msg = msg;
}
@Override
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(this.msg.get());
}
}
}
class Message {
private String title;
private String content;
public synchronized void set(String title, String content) {
this.title = title;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.content = content;
}
public synchronized String get() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
return this.title + " - " + this.content;
}
}
public class ThreadMessage {
public static void main(String[] args) throws Exception {
Message msg = new Message();
new Thread(new Producer(msg)).start(); //启动生产者线程
new Thread(new Consumer(msg)).start(); //启动消费者线程
}
}
在进行同步处理的时候可定需要有个同步的处理对象,那么此时肯定要将同步操作交由Message类处理是最合适的。这个时候数据可以保持一致了,但是重复问题依然存在。
利用Object类解决重复操作
如果想解决生产者和消费者的问题,那么最好的解决方案就是使用等待与唤醒机制。对于等待和唤醒机制,主要依靠的是Object类中提供的方法处理的。
- 等待机制:
- 死等:public final void wait() throws InterruptedException;
- 设置等待时间:public final void wait(long timeout) throws InterruptedException;
- 设置等待时间:public final void wait(long timeout, int nanos) throws InterruptedException;
- 唤醒第一个等待线程: public final void notify();
- 唤醒全部等待线程:public final void notifyAll();
如果此时有若干个等待线程的话,notify()表示唤醒第一个等待的,而其它的线程继续等待。而notifyAll()会唤醒所有等待线程,哪个线程的优先级高,就有可能先执行。
对于当前的问题的主要解决方案,应该通过Message类来完成处理。
范例: 修改Message类
class Message {
private String title;
private String content;
private boolean flag = true; //表示生产或消费的形式
// flag = true;允许生产,但不允许消费
// flag = false;允许消费,但不允许生产
public synchronized void set(String title, String content) {
if(!this.flag){ //无法进行生产,应该等待被消费
try {
super.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.title = title;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.content = content;
this.flag = false;//已经生产过了
super.notify();//唤醒等待的线程
}
public synchronized String get() {
if(this.flag){
try {
super.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
return this.title + " - " + this.content;
}finally {
this.flag = true;//继续生产
super.notify();//唤醒等待线程
}
}
}
class Producer implements Runnable {
private Message msg;
public Producer(Message msg) {
this.msg = msg;
}
@Override
public void run() {
for (int x = 0; x < 100; x++) {
if (x % 2 == 0) {
this.msg.set("张三", "帅气");
} else {
this.msg.set("李四", "漂亮");
}
}
}
}
class Consumer implements Runnable {
private Message msg;
public Consumer(Message msg) {
this.msg = msg;
}
@Override
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(this.msg.get());
}
}
}
public class ThreadMessage {
public static void main(String[] args) throws Exception {
Message msg = new Message();
new Thread(new Producer(msg)).start(); //启动生产者线程
new Thread(new Consumer(msg)).start(); //启动消费者线程
}
}
这种处理形式就是进行多线程开发过程中最原始的处理方案,整个的等待、同步、唤醒机制都由开发者自行通过原生的代码实现控制。
多线程的深入话题
优雅的停止线程
在多线程的操作中如果要启动多线程肯定使用的是Thread类中的start()方法,而如果对于多线程需要进行停止处理,Thread类原本提供有stop()方法。但是对于这个方法从JDK1.2版本开始,就已经将其废除了。而且一直到现在也不建议出现在你的代码中。还有几个方法也过时了:
- 停止多线程:public void stop();
- 销毁多线程:public void destroy();
- 挂起多线程:public final void suspend();
- 恢复挂起的线程执行:public final void resume();
之所以废除这些方法,主要是因为这些方法有可能导致线程的死锁。如果想实现线程的停止,需要通过一种柔和的方式来进行
范例:实现线程的柔和停止
public static boolean flag = true;
public static void main(String[] args) throws Exception {
new Thread(()->{
long num = 0;
while (flag) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在运行、num= " + num ++ );
}
}, "执行线程").start();
Thread.sleep(200);
flag = false;
}
万一现在有其它的线程在控制这个flag的内容,那么这个时候对于线程的停止也不是立刻停止的。
后台守护线程
在Thread类里面提供有如下的守护线程的操作方法:
- 设置为守护线程:public final void setDaemon(boolean on);
- 判断是否为守护线程:public final boolean isDaemon();
范例: 使用守护线程
public static void main(String[] args) throws Exception {
Thread userThread = new Thread(() -> {
for (int x = 0; x < 10; x++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在运行、x = " + x);
}
}, "用户线程");
Thread daemonThread = new Thread(() -> {
for (int x = 0; x < Integer.MAX_VALUE; x++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在运行、x = " + x);
}
}, "守护线程");
daemonThread.setDaemon(true); //设置守护线程
userThread.start();
daemonThread.start();
}
可以发现所有的守护线程都是围绕在用户线程的周围。如果程序执行完毕了,守护线程也就消失了,在JVM里最大的守护线程就是GC线程。
程序执行中GC线程会一直存在,如果程序执行完毕,GC线程也将消失。
volatile关键字
在多线程的定义中,volatile关键字主要是在属性定义上使用,表示此属性为直接数据操作,而不进行副本的拷贝处理。
![](Screenshot from 2021-02-22 22-46-51.png)
class MyThreadTicket implements Runnable{
private volatile int ticket = 10;
@Override
public void run() {
synchronized (this) {
while (this.ticket > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖票,ticket = " + this.ticket--);
}
}
}
}
public class ThreadTicket {
public static void main(String[] args){
MyThreadTicket mt = new MyThreadTicket();
new Thread(mt, "票贩子A").start();
new Thread(mt, "票贩子B").start();
new Thread(mt, "票贩子C").start();
}
}
面试题:请解释volatile与synchronized的区别?
- volatile主要用在属性上,而synchronized用在代码块和方法上;
- volatile无法描述同步处理,用来直接操作内存,避免的拷贝副本。而synchronized是实现同步的。
多线程综合案例
数字加减
public class ThreadMath {
public static void main(String[] args) {
Resource res = new Resource();
AddThread at = new AddThread(res);
SubThread st = new SubThread(res);
new Thread(at, "加法线程 - A").start();
new Thread(at, "加法线程 - B").start();
new Thread(st, "减法线程 - X").start();
new Thread(st, "减法线程 - Y").start();
}
}
class AddThread implements Runnable {
private Resource resource;
public AddThread(Resource resource){
this.resource = resource;
}
@Override
public void run() {
for (int x = 0; x < 50; x++) {
try {
this.resource.add();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
class SubThread implements Runnable {
private Resource resource;
public SubThread(Resource resource){
this.resource = resource;
}
@Override
public void run() {
for (int x = 0; x < 50; x++) {
try {
this.resource.sub();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
class Resource {//定义一个操作资源的类
private int num = 0;//这个要进行加减操作的数据
private boolean flag = true; //加减的切换
//flag = true;表示可以进行加法操作,但是不能进行减法操作
//flag = false;表示可以进行减法操作,但是不能进行加法操作
public synchronized void add() throws Exception {
if(!this.flag){
super.wait();
}
Thread.sleep(100);
this.num ++;
System.out.println("【加法操作 - "+ Thread.currentThread().getName() +"】num=" + this.num);
this.flag = false;
super.notifyAll();//唤醒全部等待线程
}
public synchronized void sub() throws Exception {
if(this.flag) {
super.wait();
}
Thread.sleep(200);
this.num --;
System.out.println("【减法操作 - "+ Thread.currentThread().getName() +"】num=" + this.num);
this.flag = true;
super.notifyAll();
}
}
这个是经典的多线程开发操作
生产电脑
设计一个生产电脑和搬运电脑的类,要求生产出一台电脑就搬走一台电脑,如果没有新的电脑生产出来,则搬运工要等待新电脑生成;如果生产出的电脑没有搬走,则要等待电脑搬走之后再生产,并统计出生产的电脑数量。
class Producer1 implements Runnable {
private Resource1 resource;
public Producer1(Resource1 resource){
this.resource = resource;
}
@Override
public void run() {
for (int x = 0; x < 50; x++) {
try {
this.resource.make();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
class Consumer1 implements Runnable {
private Resource1 resource;
public Consumer1(Resource1 resource){
this.resource = resource;
}
@Override
public void run() {
for (int x = 0; x < 50; x++) {
try {
this.resource.get();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
class Computer {
private static int count = 0;
private String name;
private double price;
public Computer(String name, double price){
this.name = name;
this.price = price;
count ++;
}
public String toString() {
return "【第"+ count +"台电脑】-名字:" + this.name + "、价格:" + this.price;
}
}
class Resource1 {
private Computer computer;
private boolean flag = true;
public synchronized void make() throws Exception {
if(this.computer != null){ //已经生产了
super.wait();
}
Thread.sleep(100);
this.computer = new Computer("Dell电脑", 5000.0);
System.out.println("【生产电脑】" + this.computer);
super.notifyAll();
}
public synchronized void get() throws Exception {
if(this.computer == null){
super.wait();
}
Thread.sleep(10);
System.out.println("【取走电脑】" + this.computer);
this.computer = null;//已经取走了
super.notifyAll();
}
}
public class ThreadComputer {
public static void main(String[] args){
Resource1 res = new Resource1();
new Thread(new Producer1(res)).start();
new Thread(new Consumer1(res)).start();
}
}
竞争抢答
实现一个竞拍抢答程序:要求设置三个抢答者(三个线程),而后同时发出抢答指令,并给出抢答结果提示。
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
class MyVie implements Callable<String> {
private boolean flag = false;//抢答处理
@Override
public String call() throws Exception {
synchronized (this){
if(!this.flag){
this.flag = true;
return Thread.currentThread().getName() + "抢答成功";
}else {
return Thread.currentThread().getName() + "抢答失败";
}
}
}
}
public class ThreadVie {
public static void main(String[] args) throws Exception {
MyVie vie = new MyVie();
FutureTask<String> taskA = new FutureTask<>(vie);
FutureTask<String> taskB = new FutureTask<>(vie);
FutureTask<String> taskC = new FutureTask<>(vie);
new Thread(taskA, "竞赛者A").start();
new Thread(taskB, "竞赛者B").start();
new Thread(taskC, "竞赛者B").start();
System.out.println(taskA.get());
System.out.println(taskB.get());
System.out.println(taskC.get());
}
}
Java基础类库
String类是我们想用的类
- 每一个字符串的常量都属于String类的匿名对象,并且不可更改;
- String有两个常量池:静态常量池、运行时常量池;
- String类对象实例化建议使用直接赋值的形式完成,这样可以直接将对象保存到对象池之中,方便下次使用。
StringBuffer类是线程安全的,而StringBuilder是非线程安全的。
-
追加数据:public StringBuffer append(数据类型 a);
-
插入数据:public StringBuffer insert(int offset, 数据类型 b);
-
删除指定范围的数据:public StringBuffer delete(int start, int end);
-
字符串内容翻转:public StringBuffer reverse();
CharSequence
它是描述字符串结构的接口
- 获取指定索引字符:public char charAt(int index);
- 获取字符串长度:public int length();
- 截取部分字符串:public CharSequence subSequence(int start, int end);
AutoCloseable接口
Runtime类
Runtime是描述运行时的状态,唯一一个与JVM运行状态有关的类,且默认提供一个该类的实例化对象。
- 获取最大可用内存空间:public long maxMemory();默认的配置为本机系统内存的1/4
- 获取可用内存空间:public long totalMemory();默认的配置是为本机系统内存的1/64
- 获取空闲内存空间:public long freeMemory();
- 手工进行GCC处理:public void gc();
System类
- 数组拷贝:public static void arraycopy(Object src, int srcPos,Object dest, int destPos, int length);
- 获取当前的日期时间数值:public static long currentTimeMillis();
- 进行垃圾回收:public static void gc();
Cleaner 类
import java.lang.ref.Cleaner;
class Member implements Runnable {
public Member() {
System.out.println("【构造方法】");
}
@Override
public void run() {//执行清除的时候执行的操作
System.out.println("相当于C++中的【析构方法】");
}
}
class MemberCleaning implements AutoCloseable {
private static final Cleaner cleaner = Cleaner.create();//创建清除处理
private Member member;
private Cleaner.Cleanable cleanable;
public MemberCleaning() {
this.member = new Member();
this.cleanable = this.cleaner.register(this, this.member);//注册使用的对象
}
@Override
public void close() throws Exception {
this.cleanable.clean();//启动多线程
}
}
public class MyCleaner {
public static void main(String[] args){
try(MemberCleaning mc = new MemberCleaning()) {
}catch (Exception e){}
}
}
在新一代的清除回收处理的过程中,更多的情况下考虑的是多线程的使用,即:为了防止有可能造成的延迟处理,所以许多对象回收前的处理都是单独通过一个线程完成的。
日期操作
SimpleDateFormat
public static void main(String[] args) {
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
String str = sdf.format(date);
System.out.println(str);
}
2021-02-28 10:12:20.647
字符串转date
String birth = "2020-02-12";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = sdf.parse(birth);
System.out.println(date);
正则
常用正则标记
-
【数量:单个】字符匹配
-
任意字符:表示由任意字符组成;
-
\\:匹配“\”;
-
\n:匹配换行;
-
\t:匹配制表符;
-
-
【数量:单个】字符集
-
“[abc]”:表示可能是字母a、b、c中的任意一个
-
“[^abc]”:表示任意字符,除了a、b、c
-
“[a-zA-Z]”:表示任意字符,不区分大小写
-
“[0-9]”:数字
String str = "a"; String regex = "[a-zA-Z]"; System.out.println(str.matches(regex));
-
-
【数量:单个】简化字符集
- .:表示任意一个字符
- \d:等价于[0-9]
- \D:等价于[^0-9]
- \s:匹配任意的一位空格,可能是空格、换行、制表符
- \S:匹配任意的非空格
- \w:匹配字母、数字、下划线,等价于[a-zA-Z_0-9]
- \W:匹配非字母、数字、下划线,等价于[[^^a-zA-Z_0-9]]
-
边界匹配
- ^:匹配边界开始
- $:匹配边界结束
-
数量表示,默认情况下,只有加了数量才可以匹配多位字符
- 表达式?:可以出现0次或1次
- 表达式*:可以出现0次、1次或多次
- 表达式+:可以出现1次或多次
- 表达式{n}:表达式的长度正好为n次
- 表达式{n,}:表达式的长度为n次以上
- 表达式{n,m}:表达式的长度为n~m次
-
逻辑表达式:可以连续多个正则
- 表示式X表达式Y:X表达式之后紧跟上Y表达式
- 表达式X|或表达式Y:有一个满足表达式即可
- (表达式):为表达式设置一个整体的描述,可以为整体描述设置数量单位。
String类对正则的支持
No | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public boolean matches(String regex); | 普通 | 将指定字符串进行正则判断 |
2 | public String replaceAll(String regex, String replacement); | 普通 | 替换全部 |
3 | public String replaceFirst(String regex, String replacement); | 普通 | 替换全部 |
4 | public String[] split(String regex); | 普通 | 正则拆分 |
5 | public String[] split(String regex, int limit); | 普通 | 正则拆分 |
java.util.regex包支持
String str = "INSET INTO dept(deptno,dname,loc) VALUES(#{deptno}, #{dname}, #{loc})";
String regex = "#\\{\\w+\\}";
Pattern pat = Pattern.compile(regex);
Matcher mat = pat.matcher(str);
while(mat.find()){
System.out.println(mat.group(0).replaceAll("#|\\{|\\}",""));
}
ThreadLocal
public class MyThreadLocal {
public static void main(String[] args){
new Thread(()->{
Message msg = new Message();
msg.setInfo("第一个线程");
Channel.setMessage(msg);
Channel.send();
}, "消息发送A").start();
new Thread(()->{
Message msg = new Message();
msg.setInfo("第二个线程");
Channel.setMessage(msg);
Channel.send();
}, "消息发送B").start();
new Thread(()->{
Message msg = new Message();
msg.setInfo("第三个线程");
Channel.setMessage(msg);
Channel.send();
}, "消息发送C").start();
}
}
class Channel {
private static final ThreadLocal<Message> THREADLOCAL = new ThreadLocal<>();
private Channel(){};
public static void setMessage(Message m){
THREADLOCAL.set(m);
}
public static void send(){
System.out.println("【"+ Thread.currentThread().getName() +"、消息发送】" + THREADLOCAL.get().getInfo());
}
}
class Message {
private String info;
public void setInfo(String info){
this.info = info;
}
public String getInfo(){
return info;
}
}