重点:线程的实现和线程的同步
普通方法调用和多线程
自定义线程类继承 Thread类
重写 run() 方法,编写线程执行体
创建线程对象,调用 start() 方法启动线程
public class TestThread extends Thread{@Overridepublic void run() {
// run() 方法线程体for (int i = 0; i < 20; i++) {System.out.println("我在看代码");}}public static void main(String[] args) {//main线程,主线程// 创建一个线程对象TestThread testThread = new TestThread();// 调用start()方法开启线程testThread.start();for (int i = 0; i < 20; i++) {System.out.println("我在学多线程"+i);}}}
总结:注意,线程开启不一定立即执行,由cpu调度执行
新建package为lib,放入commons-io包,右键add as libarily
import org.apache.commons.io.FileUtils;import java.io.File;
import java.io.IOException;
import java.net.URL;public class TestThread2 extends Thread{//练习thread,实现多线程同步下载图片private String url;//网络图片地址private String name;//保存的文件名public TestThread2(String url,String name){this.url = url;this.name = name;}//下载图片线程的执行体@Overridepublic void run() {WebDownload webDownload = new WebDownload();webDownload.download(url,name);System.out.println("下载了文件名:"+ name);}public static void main(String[] args) {TestThread2 t1 = new TestThread2("https://pic.cnblogs.com/face/1418974/20181015113902.png","1.jpg");TestThread2 t2 = new TestThread2("https://pic.cnblogs.com/face/1418974/20181015113902.png","2.jpg");TestThread2 t3 = new TestThread2("https://pic.cnblogs.com/face/1418974/20181015113902.png","3.jpg");t1.start();t2.start();t3.start();}
}//下载器
class WebDownload{//下载方法public void download(String url,String name){try {FileUtils.copyURLToFile(new URL(url),new File(name));} catch (IOException e) {e.printStackTrace();System.out.println("IO异常,download方法出现问题");}}
}
定义MyRunnable 类实现 Runnable 接口
**实现run()**方法,编写线程执行体
创建线程对象,调用 **start()**方法启动线程
public class TestThread3 implements Runnable{//创建线程方式2:实现Runnable接口,重写run方法,执行线程需要丢入runnable接口实现类,调用start方法@Overridepublic void run() {// run 方法线程体for (int i = 0; i < 200; i++) {System.out.println("我在看代码-----");}}public static void main(String[] args) {// 创建runnable接口的实现类对象TestThread3 testThread3 = new TestThread3();//创建线程对象,通过线程对象来开启我们的线程,代理
// Thread thread = new Thread(testThread3);
// thread.start();new Thread(testThread3).start();for (int i = 0; i < 1000; i++) {System.out.println("我在学习多线程---"+i);}}
}
实现图片的多线程下载
import org.apache.commons.io.FileUtils;import java.io.File;
import java.io.IOException;
import java.net.URL;public class TestThread2 extends Thread{//练习thread,实现多线程同步下载图片private String url;//网络图片地址private String name;//保存的文件名public TestThread2(String url,String name){this.url = url;this.name = name;}//下载图片线程的执行体@Overridepublic void run() {WebDownload webDownload = new WebDownload();webDownload.download(url,name);System.out.println("下载了文件名:"+ name);}public static void main(String[] args) {TestThread2 t1 = new TestThread2("https://pic.cnblogs.com/face/1418974/20181015113902.png","1.jpg");TestThread2 t2 = new TestThread2("https://pic.cnblogs.com/face/1418974/20181015113902.png","2.jpg");TestThread2 t3 = new TestThread2("https://pic.cnblogs.com/face/1418974/20181015113902.png","3.jpg");new Thread(t1).start();new Thread(t2).start();new Thread(t3).start();}
}//下载器
class WebDownload{//下载方法public void download(String url,String name){try {FileUtils.copyURLToFile(new URL(url),new File(name));} catch (IOException e) {e.printStackTrace();System.out.println("IO异常,download方法出现问题");}}
}
小结
// 发现问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱
public class TestThread4 implements Runnable {//多个线程同时操作同一个对象//买火车票的例子//票数private int ticketNums = 10;@Overridepublic void run() {while (true){if(ticketNums<=0){break;}
// 模拟延时try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"--->拿到了第" + ticketNums-- + "票");}}public static void main(String[] args) {TestThread4 ticket = new TestThread4();new Thread(ticket,"小名").start();new Thread(ticket,"张三").start();new Thread(ticket,"李四").start();}
}
public class Race implements Runnable{//模拟龟兔赛跑// 胜利者private static String winner;@Overridepublic void run() {for (int i = 0; i <= 100; i++) {//模拟兔子睡觉if(Thread.currentThread().getName().equals("兔子") && i%10 == 0){try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}//判断比赛是否结束boolean flag = gameOver(i);if(flag){break;}System.out.println(Thread.currentThread().getName()+"跑了" + i + "步");}}//普安段是否完成比赛private boolean gameOver (int steps) {if( winner != null){return true;}{if(steps >= 100){winner = Thread.currentThread().getName();System.out.println("winner is" + winner);return true;}}return false;}public static void main(String[] args) {Race race = new Race();new Thread(race,"兔子").start();new Thread(race,"乌龟").start();}
}
多线程下载图片
package demo;import com.sun.org.apache.xpath.internal.operations.Bool;
import org.apache.commons.io.FileUtils;import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;public class TestCallable implements Callable {private String url;//网络图片地址private String name;//保存的文件名public TestCallable(String url,String name){this.url = url;this.name = name;}//下载图片线程的执行体@Overridepublic Boolean call() {WebDownload webDownload = new WebDownload();webDownload.download(url,name);System.out.println("下载了文件名:"+ name);return true;}public static void main(String[] args) throws ExecutionException, InterruptedException {TestCallable t1 = new TestCallable("https://pic.cnblogs.com/face/1418974/20181015113902.png","1.jpg");TestCallable t2 = new TestCallable("https://pic.cnblogs.com/face/1418974/20181015113902.png","2.jpg");TestCallable t3 = new TestCallable("https://pic.cnblogs.com/face/1418974/20181015113902.png","3.jpg");
// 1. 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(3);
// 2. 提交执行:Future r1 = ser.submit(t1);Future r2 = ser.submit(t2);Future r3 = ser.submit(t3);
// 3. 获取结果:boolean rs1 = r1.get();boolean rs2 = r2.get();boolean rs3 = r3.get();System.out.println(rs1);System.out.println(rs2);System.out.println(rs3);
// 4. 关闭服务:ser.shutdownNow();}
}//下载器
class WebDownload{//下载方法public void download(String url,String name){try {FileUtils.copyURLToFile(new URL(url),new File(name));} catch (IOException e) {e.printStackTrace();System.out.println("IO异常,download方法出现问题");}}
}
总结:
public class StaticProxy {public static void main(String[] args) {new Thread( ()-> System.out.println("我爱你")).start();You you = new You();WeddingCompay weddingCompay = new WeddingCompay(you);weddingCompay.HappyMarry();}
}interface Marry{void HappyMarry();
}//真实角色,你去结婚
class You implements Marry{@Overridepublic void HappyMarry() {// alt+inst 这就是真实对象System.out.println("要结婚了");}
}//代理角色,帮助你结婚
class WeddingCompay implements Marry{
// 代理谁 ----》 真实目标角色private Marry target;public WeddingCompay(Marry target) {this.target = target;}@Overridepublic void HappyMarry() {before();this.target.HappyMarry();after();}private void before() {//alt+entSystem.out.println("结婚之前,布置现场");}private void after() {System.out.println("结婚之后,收尾款");}}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-opsEVODF-1666515575310)(F:\study\javaStudy\study\src\3.png)]
理解Functional Interface(函数式接口)是学习java8 Lambda表达式的关键所在。
函数式接口的定义:
任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口。
public interface Runnable{public abstract void run();
}
对于函数式接口, 我们可以通过lambda表达式来创建该接口的对象。
package lambda;
/*
推导Lambda表达式*/
public class TestLambda {public static void main(String[] args) {ILike like = new Like();like.lambda();}
}// 1,定义一个函数式接口
interface ILike{void lambda();
}//2. 实现类
class Like implements ILike{@Overridepublic void lambda() {System.out.println(" i like lambda");}
}
package lambda;
/*
推导Lambda表达式*/
public class TestLambda {//3. 静态内部类static class Like2 implements ILike{@Overridepublic void lambda() {System.out.println("i like lambda2");}}public static void main(String[] args) {ILike like = new Like();like.lambda();like = new Like2();like.lambda();// 4.局部内部类class Like3 implements ILike{@Overridepublic void lambda() {System.out.println("i like lambda3");}}like = new Like3();like.lambda();//5. 匿名内部类,没有类的名称,必须借助接口或者父类like = new ILike() {@Overridepublic void lambda() {System.out.println("i like lambda4");}};like.lambda();// 6. 用lamdba简化like = ()-> {System.out.println("i like lambda5");};like.lambda();}
}// 1,定义一个函数式接口
interface ILike{void lambda();
}//2. 实现类
class Like implements ILike{@Overridepublic void lambda() {System.out.println(" i like lambda");}
}
package lambda;public class TestLamdba2 {public static void main(String[] args) {ILove love = (int a)-> {System.out.println("i love you" + a);};love.love(2);}
}interface ILove{void love(int a);
}
总结
package lambda;public class TestLamdba2 {public static void main(String[] args) {ILove love = null;
// 1. lambda表示简化// ILove love = (int a)-> {// System.out.println("i love you" + a);// };
// 简化1. 参数类型love = (a)->{System.out.println("i love you" + a);};// 简化2。简化括号love = a->{System.out.println("i love you" + a);};
// 简化3. 去掉花括号love = a-> System.out.println("i love you" + a);
// 总结:
// lambda表达式只能有一行代码的情况下才能简化为一行,如果有多行,那么就用代码 块包裹
// 前提是接口为函数式接口
// 多个参数也可以去掉参数类型,要去掉就都去掉,必须加括号love.love(521);}
}interface ILove{void love(int a);
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XVd1ZwCy-1666515575310)(F:\study\javaStudy\study\src\4.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lAS89Zj6-1666515575311)(F:\study\javaStudy\study\src\5.png)]
线程方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7jSbplLi-1666515575311)(F:\study\javaStudy\study\src\6.png)]
不推荐使用JDK提供的 stop()、destory()方法。【已废弃】
推荐线程自己停止下来
建议使用一个标志位进行终止变量当flag=false,则终止线程运行。
package state;//测试stop
//1. 建议 线程正常停止 ---> 利用次数,不建议死循环。
//2. 建议使用标志位 ---》 设置一个标志位
//3. 不要使用stop或者destory等过时或者JDK不建议使用的方法
public class TestStop implements Runnable{
// 1. 设置一个标识位private boolean flag = true;@Overridepublic void run() {int i = 0;while (flag){System.out.println("run……Thread" + i++);}}
// 2.设置一个公开的方法停止线程,转化标志位public void stop() {this.flag = false;}public static void main(String[] args) {TestStop testStop = new TestStop();new Thread(testStop).start();for (int i = 0; i < 1000; i++) {System.out.println("main" + i);if(i==900){
// 调用stop方法切换标志位,让线程停止testStop.stop();System.out.println("线程该停止了");}}}
}
package state;//模拟网络延时:放大问题的发生性
public class TestSleep implements Runnable {//多个线程同时操作同一个对象//买火车票的例子//票数private int ticketNums = 10;@Overridepublic void run() {while (true){if(ticketNums<=0){break;}
// 模拟延时try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"--->拿到了第" + ticketNums-- + "票");}}public static void main(String[] args) {TestSleep ticket = new TestSleep();new Thread(ticket,"小名").start();new Thread(ticket,"张三").start();new Thread(ticket,"李四").start();}
}
package state;import java.text.SimpleDateFormat;
import java.util.Date;//模拟倒计时
public class TestSleep2 {public static void main(String[] args) {
// try {
// tenDown();
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// 打印当前时间Date startTime = new Date(System.currentTimeMillis());//获取系统当前时间try {Thread.sleep(1000);System.out.println(new SimpleDateFormat("HH:MM:SS").format(startTime));startTime = new Date(System.currentTimeMillis());//更新当前时间} catch (InterruptedException e) {e.printStackTrace();}}
// 模拟倒计时public static void tenDown() throws InterruptedException {int num = 10;while (true){Thread.sleep(1000);System.out.println(num--);if(num<=0){break;}}}
}
package state;public class TestYield {public static void main(String[] args) {MyYielf myYielf = new MyYielf();new Thread(myYielf,"a").start();new Thread(myYielf,"b").start();}
}class MyYielf implements Runnable{@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"线程开始执行");Thread.yield();//礼让System.out.println(Thread.currentThread().getName()+"线程停止执行");}
}
package state;//测试join方法,想象为插队
public class TestJoin implements Runnable{@Overridepublic void run() {for (int i = 0; i < 1000; i++) {System.out.println("线程Vip来了" + i);}}public static void main(String[] args) throws InterruptedException {TestJoin testJoin = new TestJoin();Thread thread = new Thread(testJoin);thread.start();// 主线程for (int i = 0; i < 500; i++) {if(i==200){thread.join();//插队}System.out.println("main" + i);}}
}
package state;public class TestState {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread( ()-> {for (int i = 0; i < 5; i++) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("");});// 观察状态Thread.State state = thread.getState();System.out.println(state);//观察启动 后thread.start();//启动state = thread.getState();System.out.println(state);while (state != Thread.State.TERMINATED){//只要线程不终止,就一直输出状态Thread.sleep(1000);state = thread.getState();//更新线程状态System.out.println(state);}
// thread.start(); 结束的进程是不能在 被启动的}
}
package state;//测试线程的优先级
public class TestPriority {public static void main(String[] args) {
// 主线程默认优先级System.out.println(Thread.currentThread().getName()+ "---->" + Thread.currentThread().getPriority());MyPriority myPriority = new MyPriority();Thread t1 = new Thread(myPriority);Thread t2 = new Thread(myPriority);Thread t3 = new Thread(myPriority);Thread t4 = new Thread(myPriority);Thread t5 = new Thread(myPriority);Thread t6 = new Thread(myPriority);// 先设置优先级,在启动t1.start();t2.setPriority(1);t2.start();t3.setPriority(4);t3.start();t4.setPriority(Thread.MAX_PRIORITY);//MAX_PRIORITY=10t4.start();t5.setPriority(8);t5.start();t6.setPriority(7);t6.start();}
}class MyPriority implements Runnable{@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+ "---->" + Thread.currentThread().getPriority());}
}
package state;//测试守护线程
//上帝守护你
public class TestDaemon {public static void main(String[] args) {God god = new God();You you = new You();Thread thread = new Thread(god);thread.setDaemon(true);//默认是false表示是用户线程,正常的线程都是用户线程thread.start();//上帝守护线程启动new Thread(you).start();//你 用户线程启动}
}//上帝
class God implements Runnable{@Overridepublic void run() {while (true) {System.out.println("上帝保佑着你");}}
}//你
class You implements Runnable{@Overridepublic void run() {for (int i = 0; i < 36500; i++) {System.out.println("你一生呢个都开心的活着");}System.out.println("=====goodbye!========");}
}
package syn;//不安全的买票
//线程不安全有负数
public class UnsafeBuyTicket {public static void main(String[] args) {BuyTicket station = new BuyTicket();new Thread(station,"苦逼的我").start();new Thread(station,"牛逼的你们").start();new Thread(station,"可恶的黄牛党").start();}
}class BuyTicket implements Runnable{//票private int ticketNums = 10;boolean flag = true;//外部停止方式@Overridepublic void run() {// 买票while (flag) {try {buy();} catch (Exception e) {throw new RuntimeException(e);}}}private void buy() throws InterruptedException {//判断是否有票if(ticketNums <= 0){flag = false;return;}//模拟延时Thread.sleep(100);//买票System.out.println(Thread.currentThread().getName()+ "拿到" + ticketNums--);}
}
不安全的买票
线程不安全有负数
package syn;//不安全的取钱
//两个人去引号取钱,账户
public class UnsafeBank {public static void main(String[] args) {//账户Account account = new Account(100,"结婚基金");Drawing you = new Drawing(account,50,"你");Drawing girFriend = new Drawing(account,100,"girFriend");you.start();girFriend.start();}
}//账户
class Account{int money;//余额String name;// 卡名public Account(int money, String name) {this.money = money;this.name = name;}
}//银行:模拟取款
class Drawing extends Thread{Account account;//账户//取了多少钱int drawingMoney;//现在手里有 多少钱int nowMoney;public Drawing( Account account,int drawingMoney,String name){super(name);this.account = account;this.drawingMoney = drawingMoney;}@Overridepublic void run() {//判断有没有钱if(account.money-drawingMoney<0){System.out.println(Thread.currentThread().getName()+ "钱不够,取不了");return;}//sleep可以放大问题的发生性try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}//卡内余额 = 余额 - 你取的钱account.money = account.money - drawingMoney;//你手里的钱nowMoney = nowMoney + drawingMoney;System.out.println(account.name + "余额为" + account.money);//Thread.currentThread().getName() = this,getName()System.out.println(this.getName()+"手里的钱"+nowMoney);}
}
package syn;import java.util.ArrayList;
import java.util.List;//线程不安全的集合
public class UnsafeList {public static void main(String[] args) {List list = new ArrayList();for (int i = 0; i < 10000; i++) {new Thread(()->{list.add(Thread.currentThread().getName());}).start();}try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(list.size());}
}
package syn;//不安全的买票
public class UnsafeBuyTicket {public static void main(String[] args) {BuyTicket station = new BuyTicket();new Thread(station,"苦逼的我").start();new Thread(station,"牛逼的你们").start();new Thread(station,"可恶的黄牛党").start();}
}class BuyTicket implements Runnable{//票private int ticketNums = 10;boolean flag = true;//外部停止方式@Overridepublic void run() {// 买票while (flag) {try {buy();} catch (Exception e) {throw new RuntimeException(e);}}}//synchronized同步方法,锁的是thisprivate synchronized void buy() throws InterruptedException {//判断是否有票if(ticketNums <= 0){flag = false;return;}//模拟延时Thread.sleep(100);//买票System.out.println(Thread.currentThread().getName()+ "拿到" + ticketNums--);}
}
package syn;//不安全的取钱
//两个人去引号取钱,账户
public class UnsafeBank {public static void main(String[] args) {//账户Account account = new Account(100,"结婚基金");Drawing you = new Drawing(account,50,"你");Drawing girFriend = new Drawing(account,100,"girFriend");you.start();girFriend.start();}
}//账户
class Account{int money;//余额String name;// 卡名public Account(int money, String name) {this.money = money;this.name = name;}
}//银行:模拟取款
class Drawing extends Thread{Account account;//账户//取了多少钱int drawingMoney;//现在手里有 多少钱int nowMoney;public Drawing( Account account,int drawingMoney,String name){super(name);this.account = account;this.drawingMoney = drawingMoney;}//synchronized 默认锁的是this@Overridepublic void run() {//锁的对象就是变化的量,增删改synchronized (account){//判断有没有钱if(account.money-drawingMoney<0){System.out.println(Thread.currentThread().getName()+ "钱不够,取不了");return;}//sleep可以放大问题的发生性try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}//卡内余额 = 余额 - 你取的钱account.money = account.money - drawingMoney;//你手里的钱nowMoney = nowMoney + drawingMoney;System.out.println(account.name + "余额为" + account.money);//Thread.currentThread().getName() = this,getName()System.out.println(this.getName()+"手里的钱"+nowMoney);}}
}
package syn;import java.util.ArrayList;
import java.util.List;//线程不安全的集合
public class UnsafeList {public static void main(String[] args) {List list = new ArrayList();for (int i = 0; i < 10000; i++) {new Thread(()->{synchronized (list){list.add(Thread.currentThread().getName());}}).start();}try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(list.size());}
}
package syn;import java.util.concurrent.CopyOnWriteArrayList;//测试JUC安全类型的集合
public class TestJUC {public static void main(String[] args) {CopyOnWriteArrayList list = new CopyOnWriteArrayList();for (int i = 0; i < 10000; i++) {new Thread(()->{list.add(Thread.currentThread().getName());}).start();}try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(list.size());}
}
索格线程各自占有一些共享资源,并且相互等待其他线程占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形。某一个同步块同时拥有“两个以上对象的锁”时,就可能会发生“死锁”的问题。
package syn;//死锁:多个线程互相抱着对方需要的资源,然后相互僵持
public class DeadLock {public static void main(String[] args) {Makeup g1 = new Makeup(0,"灰姑娘");Makeup g2 = new Makeup(1,"白雪公主");g1.start();g2.start();}
}//口红
class Lipstick{}//镜子
class Mirror{}class Makeup extends Thread{//需要的资源只有一份,用static来保证只有一份static Lipstick lipstick = new Lipstick();static Mirror mirror = new Mirror();int choice;//选择String girName;//使用化妆品的人Makeup(int choice,String girName){this.choice = choice;this.girName = girName;}@Overridepublic void run() {super.run();//化妆\try {makeup();} catch (InterruptedException e) {e.printStackTrace();}}//化妆,互相持有对方的锁,就是需要拿到对方的资源private void makeup() throws InterruptedException {if(choice == 0){synchronized (lipstick){//获得口红的锁System.out.println(this.girName+ "获得口红的锁");Thread.sleep(1000);
// synchronized (mirror){//一秒钟后想获得镜子
// System.out.println(this.girName+ "获得镜子的锁");
// }}synchronized (mirror){//一秒钟后想获得镜子System.out.println(this.girName+ "获得镜子的锁");}}else {synchronized (mirror) {//获得镜子的锁System.out.println(this.girName + "获得镜子的锁");Thread.sleep(2000);
// synchronized (lipstick) {//2秒钟后想获得口红
// System.out.println(this.girName + "获得镜子的锁");
// }}synchronized (lipstick) {//2秒钟后想获得口红System.out.println(this.girName + "获得镜子的锁");}}}}
产生死锁的四个必要条件:
上面列出了死锁的四个必要条件,我们只要想办法破其中的任意一个或多个条件就可以避免死锁发生。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QxUPO2rA-1666515575321)(F:\study\javaStudy\study\src\15.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aud4waYN-1666515575321)(F:\study\javaStudy\study\src\16.png)]
package syn;import java.util.concurrent.locks.ReentrantLock;//测试Lock锁
public class TestLock {public static void main(String[] args) {TestLock2 testLock2 = new TestLock2();new Thread(testLock2).start();new Thread(testLock2).start();new Thread(testLock2).start();}
}class TestLock2 implements Runnable{int ticketNums = 10;//定义lock锁private final ReentrantLock lock = new ReentrantLock();@Overridepublic void run() {while (true){try {lock.lock();//加锁if (ticketNums>0){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(ticketNums--);}else{break;}} finally {lock.unlock();}}}
}
生产者消费者(producer-consumer)(有界缓冲区(bounded-buffer))模式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pa3DZKSy-1666515575322)(F:\study\javaStudy\study\src\18.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FZg6tIQC-1666515575322)(F:\study\javaStudy\study\src\19.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bVLpfTWX-1666515575323)(F:\study\javaStudy\study\src\21.png)]
package syn;//测试:生产者消费者模型---》 利用缓冲区解决:管程法//生产者,消费者,产品,缓冲区
public class TestPC {public static void main(String[] args) {SynContainer container = new SynContainer();new Productor(container).start();new Consumer(container).start();}}//生产者
class Productor extends Thread{SynContainer container;public Productor(SynContainer container){this.container = container;}//生产@Overridepublic void run() {for (int i = 0; i < 100; i++) {container.push(new Chicken(i));System.out.println("生产了"+i+"只鸡");}}
}//消费者
class Consumer extends Thread{SynContainer container;public Consumer(SynContainer container){this.container = container;}//消费@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println("消费了----》" + container.pop().id+"只鸡");}}
}//产品class Chicken{int id;//产品编号public Chicken(int id) {this.id = id;}
}//缓冲区class SynContainer{//需要一个容器大小Chicken[] chickens = new Chicken[10];//容器计数器int count = 0;//生产者放入产品public synchronized void push(Chicken chicken){//如果容器满了,就需要等待消费这消费if(count == chickens.length){//通知消费者消费,生产者等待try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}//如果没有满,我们就需要丢入产品chickens[count] = chicken;count++;//可以通知消费者消费了this.notifyAll();}//消费者消费产品public synchronized Chicken pop(){//判断能否消费if(count==0){//等待生产者生产。消费者等待try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}//如果可以消费count--;Chicken chicken = chickens[count];//吃完了,通知生产生产this.notifyAll();return chicken;}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1HmsgBmz-1666515575323)(F:\study\javaStudy\study\src\22.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zxZk6LkH-1666515575323)(F:\study\javaStudy\study\src\23.png)]package syn;//测试生产者问题2:信号灯法,标志位解决
public class TestPC2 {public static void main(String[] args) {Tv tv = new Tv();new Player(tv).start();new Watcher(tv).start();}
}//生产者--》演员
class Player extends Thread{Tv tv;public Player(Tv tv){this.tv = tv;}@Overridepublic void run() {for (int i = 0; i < 20; i++) {if(i%2==0){this.tv.play("快乐大本营");}else{this.tv.play("抖音");}}}
}//消费者---》观众
class Watcher extends Thread{Tv tv;public Watcher(Tv tv){this.tv = tv;}@Overridepublic void run() {for (int i = 0; i < 20; i++) {tv.watch();}}
}//产品---》节目
class Tv{//演员表演观众等待//观众观看演员等待String voice;//表演节目boolean flag = true;//表演public synchronized void play(String voice){if(!flag){try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("演员表演了"+voice);//通知观众观看this.notify();this.voice = voice;this.flag = !this.flag;}//观看public synchronized void watch(){if(flag){try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("观看了"+voice);//通知演员表演this.notify();this.flag = !this.flag;}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O23G4jPU-1666515575323)(F:\study\javaStudy\study\src\23.png)]
使用线程迟
package syn;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class TestPool {public static void main(String[] args) {//1, 创建服务,创建线程池// newFixedThreadPool 参数为:线程池大小ExecutorService service = Executors.newFixedThreadPool(10);service.execute(new MyThread());service.execute(new MyThread());service.execute(new MyThread());service.execute(new MyThread());//2.关闭链接service.shutdown();}
}class MyThread implements Runnable{@Overridepublic void run() {System.out.println(Thread.currentThread().getName());}
}
package syn;import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;//回顾总结线程的创建
public class ThreadNew {public static void main(String[] args) {new MyThread1().start();new Thread(new MyThread2()).start();FutureTask futureTask = new FutureTask(new MyThread3());new Thread(futureTask).start();try {Integer integer = futureTask.get();System.out.println(integer);} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}
}//1,继承Thread类
class MyThread1 extends Thread{@Overridepublic void run() {System.out.println("My Thread1");}
}//2. 实现RUnnable接口
class MyThread2 implements Runnable{@Overridepublic void run() {System.out.println("My Thread2");}
}//3. 实现callable接口
class MyThread3 implements Callable{@Overridepublic Integer call() throws Exception {System.out.println("My Thread3");return 100;}
}
注册Mysql服务
mysql -install
启动mysql
net start mysql
停止mysql
net stop mysql
修改默认账户密码
mysqladmin -u root password 1234
登录mysql
mysql -uroot -p1234
退出mysql
exit/quit
登录参数
mysql -u用户名 -p密码 -h要连接mysql的ip地址 -P端口号(3306)
卸载mysql
mysqld -remove mysql
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mzJpr8pW-1666515575324)(F:\study\javaStudy\study\src\25.png)]
概念:外键用来让两个表的数据之间建立链接,保证数据的一致性和完整性。
语法:
添加约束
-- 创建表时添加外键约束
CREATE TABLE 表名{列名 数据类型,……[CONSTRAINT][外键名称] FOREIGN KEY(外键列名) REFERENCES 主表(主表列名)
};
-- 建完表后添加外键约束
ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY(外键字段名称) REFERENCES主表名称(主表列名称);
删除约束
ALTER TABLE表名 DROP FOREIGN KEY 外键名称;
笛卡尔积:有A,B两个集合取A,B所有的组合情况(有无效数据)
消除无效数据
连接查询
内连接:相当于查询A B交集数据
--隐式内连接
SELECT 字段列表 FROM表1,表2... WHERE条件;
--显示内连接
SELECT字段列表FROM 表1[INNER]JOIN 表2 ON条件;
外连接
左外连接:相当于查询A表所有数据和交集部分数据
右外连接:相当于查询B表所有数据和交集部分数据
--左外连接
SELECT字段列表FROM表1 LEFT [ouTER] JOIN 表2 ON条件;
-右外连接
SELECT字段列表FROM表1 RIGHT [OUTER] JOIN 表2 ON条件;
子查询
子查询概念:
子查询根据查询结果不同,作用不同:
子查询根据查询结果不同,作用不同:
单行单列:作为条件值,使用=!= ><等进行条件判断
SELECT字段列表FROM 表 wHERE 字段名 = (子查询);
多行单列:作为条件值,使用in等关键字进行条件判断
SELECT字段列表 FROM 表 WHERE 字段名 in (子查询);
多行多列:作为虚拟表
SELECT字段列表 FROM(子查询)WHERE条件;
事务简介
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j0O7CRDk-1666515575325)(F:\study\javaStudy\study\src\30.png)]
事务四大特征
MySQL事务默认自动提交
--查看事务的默认提交方式
SELECT @@autocommit;
-- 1自动提交 0手动提交
--修改事务提交方式
set @@autocommit = 0;
JDBC就是使用Java语言操作关系型数据库的一套API
//1.注册非动
class.forName ( "com.mysql.jdbc.Driver" );
//2.获取连接对象
String url = "jdbc :mysql://127.0.0.1:3306/db1?usesSL=false" ;
String username = "root";
String password = "1234";
Connection conn = DriverHanager.getConnection(url,username,password);
//3.定义SQL
string sql = "update account set money = 2000 where id = 1";
//4.获取执行sql的对象
statement stmt = conn.createstatement();
//5.执行sql
int count = stmt.executeUpdate(sql);
// 6.处理结果
// System.out.println(count) ;
//7.释放资源
stmt.close();
conn.close();
JDBC概念
JDBC就是使用java语言操作关系型数据库的一套API
全称(Java DataBase Connectivity)Java数据库连接
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XmAlinsf-1666515575325)(F:\study\javaStudy\study\src\31.png)]
步骤
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z82c1ko1-1666515575325)(F:\study\javaStudy\study\src\32.png)]
package com.zjxweb;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;public class JDBCDemo {public static void main(String[] args) throws Exception {//1. 注册驱动//Class.forName("com.mysql.jdbc.Driver");//2. 获取连接String url="jdbc:mysql://127.0.0.1:3306/db1";String username="root";String password= "123456";Connection conn = DriverManager.getConnection(url,username,password);//3. 定义sql语句String sql = "update db set sum = 2 where id=8";//4. 获取执行sql的对象 StatementStatement stmt = conn.createStatement();//5. 执行sqlint count = stmt.executeUpdate(sql);//影响的行数//6. 处理结果System.out.println(count);//7.释放资源stmt.close();conn.close();}
}
DriverManager(驱动管理类)作用:
获取连接
static Connection getConnection (string url,string user,string password)
参数
url:连接参数
语法: jdbc:mysql:/lip地址(域名):端口号/数据库名称?参数键值对1&参数键值对2...
示例: jdbc:mysql://127.0.0.1:3306/db1
细节:
·如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称?参数键值对
·配置usesSL=false参数,禁用安全连接方式,解决警告提示String url="jdbc:mysql://127.0.0.1:3306/db1?useSSL=false";
user:用户名
password:密码
获取执行SQL的对象
Statement createStatement()
PreparedStatement prepareStatement (sql)
CallableStatement prepareCall (sql)
管理事务
Mysql事务管理
开启事务:BEGIN; /STARTTRANSACTION;
提交事务:COMMIT;
回滚事务:ROLLBACK;
MYSQL默认白动提交事冬
开启事务: setAutoCommit(boolean autoCommit): true为自动提交事务;false为手动提交事务,即为开启事务
提交事务:commit()
回滚事务:rollback()
package com.zjxweb;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;public class JDBCDemo {public static void main(String[] args) throws Exception {//1. 注册驱动Class.forName("com.mysql.jdbc.Driver");//2. 获取连接String url="jdbc:mysql://127.0.0.1:3306/db1?useSSL=false";String username="root";String password= "123456";Connection conn = DriverManager.getConnection(url,username,password);//3. 定义sql语句String sql = "update db set sum = 2 where id=8";String sql1 = "update db set sum = 2 where id=9";//4. 获取执行sql的对象 StatementStatement stmt = conn.createStatement();try {//开启事务conn.setAutoCommit(false);//5. 执行sqlint count = stmt.executeUpdate(sql);//影响的行数System.out.println(count);int count1 = stmt.executeUpdate(sql1);//影响的行数//6. 处理结果System.out.println(count1);//提交事务conn.commit();} catch (Exception e) {conn.rollback();}//7.释放资源stmt.close();conn.close();}
}
Statement作用
执行sql语句
int executeUpdate(sql):执行DML、DDL语句
返回值: (1)DML语句影响的行数(2) DDL语句执行后,执行成功也可能返回0
ResultSet executeQuery(sql):执行DQL语句
返回值: ResultSet结果集对象
ResultSet(结果集对象)作用:
封装了DQL查询语句的结果
ResultSet stmt.executeQuery(sql):执行DQL语句,返回ResultSet对象
获取查询结果
boolean next(): (1)将光标从当前位置向前移动一行(2)判断当前行是否为有效行
返回值:
true:有效行,当前行有数据
false:无效行,当前行没有数据
xXx getXxx(参数):获取数据
xXx:数据类型;如:int getInt(参数) ; String getString(参数)
参数:
. int:列的编号,从1开始
. String: 列的名称
使用步骤
//循环判断游标是否是最后一行末尾
while(rs.next()){//获取数据rs.getXxx(参数);
}
package com.zjxweb;import java.sql.*;public class JDBCDemo1 {public static void main(String[] args) throws Exception {//1. 注册驱动Class.forName("com.mysql.jdbc.Driver");//2. 获取连接String url="jdbc:mysql://127.0.0.1:3306/db1?useSSL=false";String username="root";String password= "123456";Connection conn = DriverManager.getConnection(url,username,password);//3. 定义sql语句String sql = "select * from db";//4. 获取执行sql的对象 StatementStatement stmt = conn.createStatement();//5.指向SQLResultSet rs = stmt.executeQuery(sql);//6. 处理结果。遍历rs中的所有数据//6.1 光标向下移动一行,并且判断当前行是否有数据while(rs.next()){//6.2 获取数据String data = rs.getString(1);String name = rs.getString(2);String local = rs.getString(3);System.out.println(data);System.out.println(name);System.out.println(local);System.out.println("---------------");}//释放资源rs.close();stmt.close();conn.close();}
}
案例
package com.zjxweb.pojo;public class Account {private String data;private String name;private String local;public String getData() {return data;}public void setData(String data) {this.data = data;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getLocal() {return local;}public void setLocal(String local) {this.local = local;}
// public String toString(){
// return data+name+local;
// }@Overridepublic String toString() {return "Account{" +"data='" + data + '\'' +", name='" + name + '\'' +", local='" + local + '\'' +'}';}
}
package com.zjxweb;import com.zjxweb.pojo.Account;import java.sql.*;
import java.util.ArrayList;
import java.util.List;/*** 查询account账户表数据,封装为Account对象中,兵并且存储到ArratList集合中* 1. 定义实体类Account* 2. 查询数据,封装到Account对象中* 3. 将Account对象存入ArrayList集合中*/public class JDBCDemo1 {public static void main(String[] args) throws Exception {//1. 注册驱动Class.forName("com.mysql.jdbc.Driver");//2. 获取连接String url="jdbc:mysql://127.0.0.1:3306/db1?useSSL=false";String username="root";String password= "123456";Connection conn = DriverManager.getConnection(url,username,password);//3. 定义sql语句String sql = "select * from db";//4. 获取执行sql的对象 StatementStatement stmt = conn.createStatement();//5.指向SQLResultSet rs = stmt.executeQuery(sql);//创建集合List list = new ArrayList<>();//6. 处理结果。遍历rs中的所有数据//6.1 光标向下移动一行,并且判断当前行是否有数据while(rs.next()){Account account = new Account();//6.2 获取数据String data = rs.getString(1);String name = rs.getString(2);String local = rs.getString(3);//赋值account.setData(data);account.setName(name);account.setLocal(local);//存入集合list.add(account);}System.out.println(list);//释放资源rs.close();stmt.close();conn.close();}
}
PreparedStatement作用
SQL注入
案列
package com.zjxweb;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;public class JDBCDemo6_UserLogin {public static void main(String[] args) throws Exception {//1. 注册驱动Class.forName("com.mysql.jdbc.Driver");//2. 获取连接String url="jdbc:mysql://127.0.0.1:3306/db1?useSSL=false";String username="root";String password= "123456";Connection conn = DriverManager.getConnection(url,username,password);String name = "zhanshan";
// String pwd = "123456";String pwd = "' or ' 1 '= ' 1 ";String sql = "select * from tb_user where username = '"+name+"' and password = '"+pwd+"'";// 获取stmt对象Statement stmt = conn.createStatement();//执行sqlResultSet rs = stmt.executeQuery(sql);//判断登录是否成功if(rs.next()){System.out.println("登录成功");}else {System.out.println("登录失败");}//7.释放资源rs.close();stmt.close();conn.close();}
}
解决sql注入问题
获取PreparedStatement对象
// SQL语句中的参数值,使用?占位符替代
String sql = "select * from user where username = ? and password = ?";
//通过Connection对象获取,并传入对应的sql语句
PreparedStatement pstmt = conn.prepareStatement(sql);
设置参数值
PreparedStatement对象: setXxx(参数1,参数2):给﹖赋值
Xxx:数据类型;如setlnt(参数1,参数2)
参数:参数1:?的位置编号,从1开始参数2:?的值
执行SQL
executeUpdate(); // executeQuery();:不需要再传递sql
案列
package com.zjxweb;import java.sql.*;public class JDBCDemo6_UserLogin {public static void main(String[] args) throws Exception {//1. 注册驱动Class.forName("com.mysql.jdbc.Driver");//2. 获取连接String url="jdbc:mysql://127.0.0.1:3306/db1?useSSL=false";String username="root";String password= "123456";Connection conn = DriverManager.getConnection(url,username,password);String name = "zhanshan";String pwd = "123456";
// String pwd = "' or ' 1 '= ' 1 ";
// String sql = "select * from tb_user where username = '"+name+"' and password = '"+pwd+"'";String sql = "select * from tb_user where username = ? and password = ?";// 获取stmt对象PreparedStatement pstmt = conn.prepareStatement(sql);// 设置 ? 的值pstmt.setString(1,name);pstmt.setString(2,pwd);//执行sqlResultSet rs = pstmt.executeQuery();//判断登录是否成功if(rs.next()){System.out.println("登录成功");}else {System.out.println("登录失败");}//7.释放资源rs.close();pstmt.close();conn.close();}
}
PreparedStatement原理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TAGkBC0k-1666515575325)(F:\study\javaStudy\study\src\33.png)]
log-output=FILE
general-log=1
general_log_file="D:\mysql.log"
slow-query-log=1
slow_query_log_file="D:\mysql_slow.log"
long_query_time=2
数据库连接池 是个容器,负责分配、管理数据库连接(Connection)
它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;
释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏好处:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9HM1yXSU-1666515575326)(/34.png)]
数据库连接池实现
Connection getConnection()
Druid使用步骤
解压apache-maven-3.6.1.rar既安装完成
配置环境变量MAVEN_HOME为安装路径的bin目录
配置本地仓库:修改conf/settings.xml中的为一个指定目录
配置阿里云私服:修改conf/settings.xml中的标签,为其添加如下子标签:
alimaven aliyun maven http://maven.aliyun.com/nexus/content/groups/public/ central
Maven坐标主要组成
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6dwrhZK8-1666515575327)(/41.png)]
使用坐标导入jar包
自动导入
environments:配置数据库连接环境信息。可以配置多个environment,通过default属性切换不同的environment
MyBatis核心配置文件的顶层结构如下:
configuration(配置)
properties(属性)
settings(设置)
otypeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory (对象工厂)o
plugins (插件)
oenvironments(环境配置)
environment(环境变量)
transactionManager (事务管理器)-
dataSource(数据源)
databaseldProvider (数据库厂商标识)
mappers(映射器)
设置别名(typeAliases)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6iIRvhwO-1666515575328)(/49.png)]
细节:配置各个标签时,需要遵守前后顺序
数据库表的字段名称―和―实体类的属性名称不一样,则不能自动封装数据
*起别名:对不一样的列名起别名,让别名和实体类的属性名一样
*缺点:每次查询都要定义一次别名*sql片段
* resultMap1.定义标签2.在
id,brand_name as brandName,company_name as companyName,ordered,description,status
resultMap
id:唯一标识
type:映射的类型,支持别名
#{ }∶会将其替换为?,为了防止SQL注入
${}:拼sql。会存在SQL注入问题
使用时机:
参数类型:parameterType:可以省略
select *from tb_brand where id = #{id};
特殊字符处理:
转义字符
CDATA区
select *from tb_brand where id#{id};
多条件
SQL语句会随着用户的输入或外部条件的变化而变化,我们称为动态SQL
select *from tb_brandwherestatus = #{status}and company_name like #{companyName}and brand_name like #{brandName}
MyBatis对动态SQL有很强大的支撑:
if:条件判断
test:逻辑表达式
问题
select *from tb_brand wherestatus = #{status} and company_name like #{companyName} and brand_name like #{brandName} select>
choose (when, otherwise)
trim (where, set)
foreach
单条件
从多条件中选择一个
choose (when, otherwise):选择,类似于Java中的switch语句
select *from tb_brandwhere status = #{status} company_name like #{companyName} brand_name like #{brandName} 1 =1
编写接口方法:Mapper接口 void add(Brand brand)
编写SQL语句:SQL映射文件
insert into tb_brand (brand_name, company_name, ordered, description, status)values ({brandName},#{companyName},#{ordered},#{description},#{status});
执行方法,测试
useGeneratedKeys="true" keyProperty="id"
insert into tb_order (payment,payment_type,status)values (#{payment},#{paymentType},#{status});
insert into tb_order_item (goods_name, goods_price, count,order_id)values (#{goodsName},#{goodsPrice},#{count,t#{orderld});
修改全部字段
编写接口方法: Mapper接口
void update(Brand brand);
参数:所有数据
结果: void
编写SQL语句:SQL映射文件
update tb_brandset brand_name = #{brandName},company_name = #{companyName},ordered = #{ordered},description = #{description},status = #{status}where id = #{id};
执行方法,测试
修改动态字段
编写SQL语句: SQL映射文件
执行方法,测试
update tb_brandbrand_name = #{brandName}, company_name = #{companyName}, ordered = #{ordered},description = #{description}, status = #{status} where id = #{id};
删除一个
编写接口方法: Mapper
void deleteByld(int id);
编写SQL语句:SQL映射文件
delete from tb_brand where id = #{id}
执行方法,测试
批量删除
编写接口方法: Mapper接口
void deleteBylds(@Param("ids") int[]ids);
编写SQL语句: SQL映射文件
delete from tb_brandwhere id in (?,?,?)
mybatis会将数组参数,封装为一个Map集合。
delete from tb_brandwhere id in#{id}
执行方法,测试
MyBatis 接口方法中可以接收各种各样的参数,MyBatis底层对于这些参数进行不同的封装处理方式
单个参数
多个参数:封装为Map集合
map.put( " arg0",参数值1)
map.put( " param1",参数值1)
map.put( "param2",参数值2)
map.put( "agr1",参数值2)
User select(@Param("username") String username,@Param("Mpassword")String password);
select *from tb_userwhereusername = #{username}and password = #{password};
MyBatis提供了ParamNameResolver类来进行参数封装
下一篇:股票接口怎样获取数据方便?