精通Java学习之路(三)
JDK 17 API中文文档:Java17中文文档 – API参考文档 – 全栈行动派 (qzxdp.cn)
说说自己的话:Java的基础和一些进阶我已经学完了,把java学完只是相当于在英语中学会读24个字母,还有许多模块需要学习。现在我在想是继续学java开发框架还是python大数据,毕竟我的专业是数据科学与大数据技术,学了java框架搭建再学下JavaWeb我就可以直接去面试上班了,而且JavaWeb中的Mysql,JDBC,HTML,CSS我已经学完了,阿巴阿巴阿巴…..但是python学了不仅可以搞大数据还可以搞ai,但是ai就业就比较难了。之前还准备学C++去搞游戏开发,算了,那又是个新的大领域….不过不慌我还是大二,暑假先把下学习要学的python给学了,然后去玩玩ai或者数据挖掘,争取自己搞个“挖矿机”,这可是我自己放假时的承若!
–异常处理–
异常检测基本结构:try{要检查的代码}catch(Exception e){出错时抛出的代码}
一个例子讲述异常,输入的int类型变量age错误时抛出异常:import java.util.Scanner;
public class xiancheng {
public static void main(String[] args) {
while (true) {
try {
Scanner sc = new Scanner(System.in);
System.out.println("请输入你的年龄:");
int age = sc.nextInt();
System.out.println("你的年龄是:" + age);
break;
} catch (Exception e) {
System.err.println("你TM再乱输试试???");//err表示错误输出,输出时会红色字体显示
}
}
}
}
运行结果:

–线程—
多线程(并发编程)
并发性:如果CPU是单核的,其实一个时刻只有一个进程在被执行,CPU会分时切换每个进程,因为切换的速度非常快,所以以为同时在进行。n核就是可以同时执行n个进程。一个进程可以包含多个线程,这就是多线程。
创建线程的三种方法:
1,定义一个类继承Thread线程类,重写run()方法,创建线程对象调用线程对象的start()方法启动线程。
2,定义一个线程任务类实现Runnable接口,重写run()方法,创建线程任务对象,把线程任务对象包装成线程对象,调用线程对象的start()方法启动线程
3,实现Callable接口(扩展)
Thread类:
多线程简单案例:main方法和run方法抢单核的cpu:public class xiancheng {
public static void main(String[] args) {
xiancheng2 THREAD =new xiancheng2();
THREAD.start();
for(int i=0;i<100;i++){
System.out.println("main线程输出"+i);
}
}
static class xiancheng2 extends Thread{
@Override
public void run(){
for(int i=0;i<100;i++){
System.out.println("子线程输出"+i);
}
}
}
}
运行结果:

可以看到跑的无规则,就是因为两个线程在抢一个CPU
Runnable接口就是Thread的高级版,Callable<>又是Runnable的高级版,Runnable的主要作用是空出父类来,Callable<>的主要作用是增加返回值的(return),线程的内容建议直接看代码,里面有亲手写的注释,好理解些
—线程安全问题模拟项目训练—
如果两个线程同时取钱(即两个线程同时共享一个资源):

就会产生如下情况:

这就需要一个一个排队来取,即把共享资源进行上锁,让每次只能有一个线程运行,这时就需要调用synchronized(锁对象){//访问共享资源的代码}来实现,这里的锁对象可以是任意的,即:
—线程池—
为防止线程的过多(因为一个人对象就要创建一个线程),就诞生了线程池,无需创建线程销毁线程,降低资源消耗,在线程池中放一些固定的线程,同一个线程可以创建多个任务。
创建线程池:ExecutorService pools =Executors.newFixedThreadPool(线程池数量);
创建线程:Thread/Runnable xxx =new MyThread/MyRunnable();//这里的My是自己的线程类名
调用线程池:pools.submit(xxx);
Callable接口的线程的线程池调用:
Future<数据类型> 对象名称 =线程池.submit(new Callable接口类(变量n));
Callable类:static class MyCallable implements Callable{有参构造变量n…}
volatile关键字:
问题:多个线程访问共享变量是会出现一个线程修改变量值后其他线程看不到最新的线程值。
例子:
@Override
public void run(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
flag=true;
System.out.println(“flag is become TRUE”);
}
VolatileDemo v1=new VolatileDemo();
//子线程修改flag的值
v1.start();
//主线程
while (true){
if(v1.isFlag()){
System.out.println(“Going..”);
}
}
这时控制台不会输出Going…因为主线程直接拿的是一开始的false,并不是经过子线程修改过后的true

解决办法:1,加锁sysnchronized(xxx.class){},因为加锁会清空工作内容,读取最新的内存出来。2,直接给变量加上volatile修饰,volatile修饰后,一旦这个变量改变,所有线程都可以读取最新值。volatile变量的流程图:

一个方式是清空再读取,一个直接修改
—原子性与原子类—
原子性(就是线程安全问题)
问题:假如转账给别人,如果转账失败报错,但是对方钱这个变量已经变了。
可以用下面这些代码来测试,有时候就不会加到10000
@Override
public void run() {
for(int i=1;i<=100;i++){
count++;
System.out.println(“count==>”+count);
}
}
Runnable r1=new MyRunnable();
for(int i=1;i<=100;i++){
//启动100个线程,执行100次任务
new Thread(r1).start();
}
volatile只能保证可见性,不能保证原子性,直接用synchonized加锁,锁住任务结束后再进行下一个任务,而不是两个任务同时执行。
由于加锁机制性能差(因为是一个一个进行,会堵塞线程),所以就诞生了原子类。创建原子;类对象:AtomicInteger xxx=new AtomicInteger();
输出原子类结果:xxx.incrementAndGet()。原理是先进行任务再取结果
原子类的CAS机制介绍:就是多个线程会先同时进行任务,然后一个线程的结果会优先返回最新值到原来开始拿的值,其他线程会再次检测原来开始拿的值,如果发现其值与最开始取的值不一样则会报废刚刚执行的值再拿新的值进行任务。
线程学习时的代码
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
public class xiancheng {
static class MyThread extends Thread{
public MyThread(String name){
//public Thread(String name):父类的有参构造器
super(name);
}
public MyThread(){}
@Override
public void run(){
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+”输出”+i);
//currrentThread为获取当前执行的线程
}
}
}
static class MyRunnable implements Runnable{
/**
* Runnable接口优点:拥有Runnable的所有优点,线程任务类(MyRunnable)只是实现Runnable接口,还可以继承其他类(Thread类是继承的得嘛)
* 同一个线程任务对象可以被包装成多个线程对象(意思就是可以分类)
* 适合多个相同程序代码的线程去共享同一个资源
* 线程池可以放入实现Runnable或Callable线程任务对象
*/
@Override
public void run() {
for (int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName()+”输出”+i);//currentThread方法为获取当前线程
}
}
}
static class MyCallable implements Callable<String>{
//这个Callable接口也需要重写,不过不是run方法咯
@Override
public String call() throws Exception {
int sum=0;
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+”输出”+i);
sum+=i;
}
System.out.println(Thread.currentThread().getName()+”执行结果为”+sum);
return null;
}
}
public static void main(String[] args) throws InterruptedException {
Thread THREAD =new MyThread();//无参构造的线程
THREAD.start();
THREAD.setName(“一号线程”);//设置线程名字,默认为Thread-0
System.out.println(THREAD.getName());//获取线程名字
Thread THREAD2 = new MyThread(“二号线程”);//有参构造的线程
THREAD2.start();
Runnable TARGET=new MyRunnable();//多态的写法,前面写接口,后面写接口类名,这几个线程都是这么写
Thread THREAD3= new Thread(TARGET,”三号线程”);
/**
* Thread类中存在public Thread(Runnable target)和public Thread(Runnable target,String name)的构造方法
* 所以要先创建Runnable的对象,把线程任务对象包装成线程对象,且可以指定线程名称
*/
THREAD3.start();
new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName()+”输出”+i);
}
System.out.println(Thread.currentThread().getName()+”输出完毕”);
}
}).start();//匿名内部类写法
Callable<String> CALL =new MyCallable();//创建Callable任务对象
FutureTask<String> TASK =new FutureTask<String>(CALL);
/**把Callable任务对象包装成一个未来任务对象
* –public FutureTask(Callable<v> callable)
* 未来对象就是个Runnable线程对象,这样就可以被包装成线程对象
* 未来任务对象可以再线程执行完毕后去得到线程执行结果
*/
Thread THREAD4 =new Thread(TASK,”四号线程”);//创建Thread线程对象
THREAD4.start();
//下面的try是取线程执行的结果,如果没有跑完,就会让出CPU等线程执行结束再取结果
try {
String rs= TASK.get();
System.out.println(rs);
}catch (Exception e){
e.printStackTrace();
}
Thread m =Thread.currentThread();//currentThread获取当前线程,这个代码在哪个线程就是那个线程,这里是main线程
/**
* 因为在Thread类中,currentThread的方法为static类型,所以可以直接用对象或类名调用
* 原:public static Thread currentThread(){…}
*/
m.setName(“最强线程”);//默认为main
System.out.println(m.getName());
for(int i=0;i<10;i++){
System.out.println(“main线程输出”+i);
}
//sleep间隔方法
for (int i=0;i<5;i++){
System.out.println(i);
try {
Thread.sleep(1000);//隔1000ms再输出数据,加上for就是每隔1s输出一个数据
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
线程安全问题实战代码
账户信息类+实现主类:
package xianchenganquan;
public class zhanghu {
private String card;
private double money;
public zhanghu() {
}
public zhanghu(String card, double money) {
this.card = card;
this.money = money;
}
public void drawMoney(double money) {
String name = Thread.currentThread().getName();
synchronized (“随便”) {
if (this.money >= money) {//左边的money是账户的,右边的是取的钱
System.out.println(“余额足够,尊敬的” + name + “已为你取出” + money);
this.money -= money;
System.out.println(name + “取完钱后,” + card + “卡还剩” + this.money);
} else {
System.out.println(name + “来取钱,余额不足”);
}
}
}
public String getCard() {
return card;
}
public void setCard(String card) {
this.card = card;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
public static void main(String[] args) {
//创建一个共享取钱账户
zhanghu people1 =new zhanghu(“ICBC”,10000);
//创建两个Thread对象模拟线程取钱
Thread HEPNGAN = new xianchenganquan.DrawThread(“何平安”,people1);
HEPNGAN.start();
Thread HEHAO =new xianchenganquan.DrawThread(“何浩”,people1);
HEHAO.start();
}
}
DrawThread类:
package xianchenganquan;
public class DrawThread extends Thread{
private zhanghu zh;
public DrawThread(){}
public DrawThread(String name,zhanghu zh){
super(name);//调用Thread父类中的name
this.zh=zh;
}
@Override
public void run(){
zh.drawMoney(10000);
}
}
线程池学习代码
import java.util.concurrent.*;
public class ThreadPools {
public static Object ziyuan1=new Object();//创建资源
public static Object ziyuan2=new Object();
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
synchronized (ziyuan1){//死锁代码形式上通常要进行锁的嵌套访问
System.out.println(“线程1占用资源1,请求资源2”);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (ziyuan2){
System.out.println(“线程1成功占用资源2”);
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (ziyuan2){
System.out.println(“线程2占用资源2,请求资源1”);
try {
Thread.sleep(1000);//先睡1s
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (ziyuan1){
System.out.println(“线程2成功占用资源1”);
}
}
}
}).start();
//创建一个线程池,固定数量为3
ExecutorService pools =Executors.newFixedThreadPool(3);
Runnable target= new MyRunnable();
pools.submit(target);
pools.submit(target);
pools.submit(target);
pools.submit(target);
pools.shutdown();//等运行结束后关闭线程池
//Callable的线程池
ExecutorService pools2 =Executors.newFixedThreadPool(3);
Future<String> t1 =pools2.submit(new MyCallable(100));
Future<String> t2 =pools2.submit(new MyCallable(200));
Future<String> t3 =pools2.submit(new MyCallable(300));
Future<String> t4 =pools2.submit(new MyCallable(400));
try {
String rs1=t1.get();
String rs2=t2.get();
String rs3=t3.get();
String rs4=t4.get();
System.out.println(rs1);
System.out.println(rs2);
System.out.println(rs3);
System.out.println(rs4);
}catch (Exception e){
e.printStackTrace();
}
}
private static class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 1; i < 3; i++) {
System.out.println(Thread.currentThread().getName() + “–>” + i);
}
}
}
static class MyCallable implements Callable{
private int n;
public MyCallable(int n){
this.n=n;
}
@Override
public Object call() throws Exception {
int sum=0;
for (int i=1;i<n;i++){
sum=sum+i;
}
return Thread.currentThread().getName()+”结果为”+sum;
}
}
}
volatile和原子类学习代码
import java.util.concurrent.atomic.AtomicInteger;
//问题:多个线程访问共享变量是会出现一个线程修改变量值后其他线程看不到最新的线程值。
public class VolatileDemo extends Thread{
private volatile boolean flag=false;
public boolean isFlag()/*布尔变量的get方法为is*/ {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
@Override
public void run(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
flag=true;
System.out.println(“flag is become TRUE”);
}
public static void main(String[] args) {
VolatileDemo v1=new VolatileDemo();
//子线程修改flag的值
v1.start();
//主线程
synchronized (VolatileDemo.class){
if(v1.isFlag()){
System.out.println(“Going..”);
}
}
Runnable r1=new MyRunnable();
for(int i=1;i<=100;i++){
//启动100个线程,执行100次任务
new Thread(r1).start();
}
//原子类的方法
Runnable r2=new MyRunnable2();
for(int i=1;i<=100;i++){
new Thread(r2).start();
}
}
static class MyRunnable implements Runnable{
private volatile int count;
@Override
public void run() {
for(int i=1;i<=100;i++){
count++;
System.out.println(“count==>”+count);
}
}
}
static class MyRunnable2 implements Runnable{
private AtomicInteger atomicInteger=new AtomicInteger();
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
for (int i = 1; i <= 100; i++) {
System.out.println(“count===>” + atomicInteger.incrementAndGet());
}
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?