一、 java基础
1. java多线程基础
1) 多线程的创建方式
1.多线程创建方式1
packagecom.qyl.springboot.controller;
/**
* 多线程的创建
* 方式一: 继承与Thread类
* 1.创建一个子类类继承Thread
* 2.在子类中重写Thread的run()方法(线程要做的事写到run()方法中)
* 3.创建该子类的对象 (在主线程里面创建)
* 4.通过此对象调用start方法
* 例子 : 遍历100以内的所有的偶数
*/
// 1.创建一个子类类继承Thread
classMyThreadextendsThread{
//2.在子类中重写Thread的run()方法 线程要做的事写到run()方法中
@Override
publicvoidrun() {
for(inti=0;i<100;i++) {
if(i%2==0){
System.out.println(i+"---"+Thread.currentThread().getName());
}
}
}
};
publicclassThreadTest{
publicstaticvoidmain(String[]args) {
// 3.创建该子类的对象
//主线程执行
MyThreadmyThread=newMyThread();
MyThreadmyThread1=newMyThread();
//4.通过此对象调用start方法
//创建出来的线程执行
/**
* 用start()方法启动 run()启动不了多线程效果
*/
myThread.start();
myThread1.start();//乡启动多个就多创建对象即可
for(inti=0;i<100;i++) {
if(i%2==0){
System.out.println(i+" ++++++++++++++-"+Thread.currentThread().getName());
}
}
//这种方法也可以 匿名子类
System.out.println("/////////////////");
newThread(){
@Override
publicvoidrun() {
for(inti=0;i<100;i++) {
if(i%2!=0){
System.out.println(Thread.currentThread().getName()+"////////////////"+i);
}
}
}
}.start();
}
}
输出结果:
48---Thread-0
78---Thread-1
50---Thread-0
18 ++++++++++++++-main
52---Thread-0
80---Thread-1
82---Thread-1
84---Thread-1
2. 多线程创建方式2
实现Runnable接口
1.创建一个实现了Run那边了接口的类
2.实现类去实现Runable中的抽象方法
3.创建实现类的对象
4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
5.通过Thread类的对象调用start()方法
packagecom.qyl.springboot.controller;
/**
* ### 2) 多线程创建方式2
*
* 实现Runnable接口
*
* 1.创建一个实现了Run那边了接口的类
* 2.实现类去实现Runable中的抽象方法
* 3.创建实现类的对象
* 4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
* 5.通过Thread类的对象调用start()方法
*/
//1.创建一个实现了Run那边了接口的类
classMyRunnableimplementsRunnable{
//2.实现类去实现Runable中的抽象方法
@Override
publicvoidrun() {
for(inti=0;i<100;i++) {
if(i%2==0){
System.out.println(""+i+" ++++++++++++++-"+Thread.currentThread().getName());
}
}
}
}
publicclassRunableTest{
publicstaticvoidmain(String[]args) {
//3.创建实现类的对象
MyRunnablem1=newMyRunnable();
//4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
Threadthread=newThread(m1);
thread.setName("liudeh");
thread.start();
Threadt2=newThread(m1);
t2.setName("liudsssssssssseh");
t2.start();
}
}
2.线程生命周期
创建 调用start()
就绪 sleep()时间到,join()结束,获取同步锁,notify()/notifyALL(),resume()
阻塞 失去cpu执行权,或者yield()
运行 sleep(),join(),等待同步锁,wait(),suspend()
死亡 执行完run(),调用stop(),出现Error/Exception且没处理
3.线程同步
1.同步代码块
/**
* 线程同步与安全
* 处理共享数据的时候会出现线程安全问题
* 加锁解决
* 4.在java中,通过同步机制来解决线程安全问题
* 方式一: 同步代码块
* synchronized (同步监视器){
* //需要被同步的代码 (吃操作共享数据的代码就是需要被同步的代码)
*}
* 说明: 1.(吃操作共享数据的代码就是需要被同步的代码)
* 2.同步监视器就是 锁 任何类的对象都能充当锁
* 锁的要求: 多个线程供用同一把锁
* 哪怕你用一个狗 dag对象都可以
* 方式二: 同步方法
*
*
* 5.同步的方式虽然解决了线程安全
* 缺点是同步代码块里面是单线程
*/
packagecom.qyl.java;
/**
* 开发一个类 实现Runnable 并重写 run() 方法
*/
classWindowimplementsRunnable{
privateinttitck=100;
Objectobject=newObject();
@Override
publicvoidrun() {
while(true){
//同步代码块
synchronized(object){
if(titck>0){
System.out.println(Thread.currentThread().getName()+" 卖票-----票号码为: "+titck);
titck--;
}else{
break;
}
}
}
}
}
/**
* 线程同步与安全
* 处理共享数据的时候会出现线程安全问题
* 加锁解决
* 4.在java中,通过同步机制来解决线程安全问题
* 方式一 : 同步代码块
* synchronized (同步监视器) {
* //需要被同步的代码 (吃操作共享数据的代码就是需要被同步的代码)
* }
* 说明: 1.(吃操作共享数据的代码就是需要被同步的代码)
* 2.同步监视器就是 锁 任何类的对象都能充当锁
* 锁的要求 : 多个线程供用同一把锁
* 哪怕你用一个狗 dag对象都可以
* 方式二 : 同步方法
*
*
* 5.同步的方式虽然解决了线程安全
* 缺点是同步代码块里面是单线程
*/
publicclassSyncThread{
publicstaticvoidmain(String[]args) {
Windowm1=newWindow();
Threadt1=newThread(m1);
Threadt2=newThread(m1);
Threadt3=newThread(m1);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
注意:implement Runnable 的时候 this(当前对象 ) 就可以当做锁 extends Threads的时候 不行
2.同步方法
// 1.一种情况可以直接把run改为同步的,但是要注意场景
classWindowimplementsRunnable{
privateinttitck=100;
Objectobject=newObject();
@Override
publicsynchronizedvoidrun() {
while(true){
//同步代码块
synchronized(this){
if(titck>0){
System.out.println(Thread.currentThread().getName()+" 卖票-----票号码为: "+titck);
titck--;
}else{
break;
}
}
}
}
}
1.同步方法解决Runnable
packagecom.qyl.java;
/**
* 开发一个类 实现Runnable 并重写 run() 方法
*/
classWindowimplementsRunnable{
privateinttitck=100;
Objectobject=newObject();
@Override
publicvoidrun() {
while(true){
sync();
}
}
privatesynchronizedvoidsync(){//同步监视器就是this
//同步代码块
if(titck>0){
try{
Thread.sleep(10);
}catch(InterruptedExceptione) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 卖票-----票号码为: "+titck);
titck--;
}
}
}
/**
* 线程同步与安全
* 处理共享数据的时候会出现线程安全问题
* 加锁解决
* 4.在java中,通过同步机制来解决线程安全问题
* 方式一 : 同步代码块
* synchronized (同步监视器) {
* //需要被同步的代码 (吃操作共享数据的代码就是需要被同步的代码)
* }
* 说明: 1.(吃操作共享数据的代码就是需要被同步的代码)
* 2.同步监视器就是 锁 任何类的对象都能充当锁
* 锁的要求 : 多个线程供用同一把锁
* 哪怕你用一个狗 dag对象都可以
* 方式二 : 同步方法
* 如果操作共享数据的代码完整的在一个方法中,我们可以将该方法做成同步的
* 1.一种情况可以直接把run改为同步的,但是要注意场景
*
* 5.同步的方式虽然解决了线程安全
* 缺点是同步代码块里面是单线程
*/
publicclassSyncThread{
publicstaticvoidmain(String[]args) {
Windowm1=newWindow();
Threadt1=newThread(m1);
Threadt2=newThread(m1);
Threadt3=newThread(m1);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
2.同步方法解决thread
packagecom.qyl.java;
classWindowsextendsThread{
privatestaticinttickets=100;
privatestaticObjectobject=newObject();
@Override
publicvoidrun() {
while(true) {
//同步代码块
show();
}
}
privatestaticsynchronizedvoidshow(){//加个static 就可以了
if(tickets>0) {
try{
sleep(10);
}catch(InterruptedExceptione) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 卖票-----票号码为: "+tickets);
tickets--;
}
}
}
publicclassextendsThread{
publicstaticvoidmain(String[]args) {
Windowsw1=newWindows();
Windowsw2=newWindows();
Windowsw3=newWindows();
w1.setName("窗口1");
w2.setName("窗口2");
w3.setName("窗口3");
w1.start();
w2.start();
w3.start();
}
}
4.Thread 的常用方法介绍:
1) start():
启动当前线程,调用当前线程的run()方法 。
2) run():
通常是重写Thread 的run()方法 将线程要做的事情声明在该方法中
3) currentThread() :
Thread 的静态方法 获取到当前线程
4)getName() setName():
给线程名字的方法
package com.qyl.springboot.controller;
class MyThread1 extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if(i % 2 == 0 ){
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//Thread.currentThread().setName("lalala"); 线程命名 方式1
System.out.println(Thread.currentThread().getName()+"*******"+i);
}
}
}
}
public class ThreadTest1 {
public static void main(String[] args) {
MyThread1 myThread1 = new MyThread1();
//线程命名 方式2
myThread1.setName("huren");
myThread1.start();
//给主线程命名
Thread.currentThread().setName("zongguanjun");
for (int i = 0; i < 100; i++) {
if(i % 2 == 0 ){
//Thread.currentThread().setName("lalala"); 线程命名
System.out.println(Thread.currentThread().getName()+"*******///////////"+i);
}
}
}
}
------------------------通过构造器命名线程
package com.qyl.springboot.controller;
class MyThread1 extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if(i % 2 == 0 ){
//Thread.currentThread().setName("lalala"); 线程命名 方式1
System.out.println(Thread.currentThread().getName()+"*******"+i);
}
if(i % 20 == 0){
yield();
}
}
}
//线程命名方式3 通过构造器命名
public MyThread1(String name){
super(name);
}
}
public class ThreadTest1 {
public static void main(String[] args) {
MyThread1 myThread1 = new MyThread1("sssssss");
//线程命名 方式2
myThread1.setName("huren");
myThread1.start();
//给主线程命名
Thread.currentThread().setName("zongguanjun");
for (int i = 0; i < 100; i++) {
if(i % 2 == 0 ){
//Thread.currentThread().setName("lalala"); 线程命名
System.out.println(Thread.currentThread().getName()+"*******///////////"+i);
}
}
}
}
5)yiel ():
释放当前cpu的执行权
6)join ():
在线程a中调用线程b的join()方法,此时线程a进入阻塞状态,等b执行完之后,a结束阻塞继续执行
7) stop():
强制结束该线程,不推荐使用
8) sleep(lang millitime):
当前线程睡眠指定的一段时间
9) isAlive()
判断当前线程是否存活
10)property()get,set
线程的优先级 1-10 默认5
public void newRunnableMethod () {
new Thread(new Runnable() {
@Override
public void run() {
// 存放所有openid
List openids = new ArrayList<>();
List usersGuid = new ArrayList<>();
SqlSession sqlSession = sqlSessionTemplate.getSqlSessionFactory().openSession(ExecutorType.BATCH, false);
Task_Info_News_ReadMapper mapper = sqlSession.getMapper(Task_Info_News_ReadMapper.class);
try{
// 组装info_news_read 表所需要的数据,并存入数据库中
for(int i = 0; i< list.size();i++){
String strUserGuid = (String)list.get(i).get("strUserGuid");
// 通过临时存放usersGuid,去重(对根据范围查出数据进行去重)
if(!usersGuid.contains(strUserGuid)){
usersGuid.add(strUserGuid);
// 获取openid
Object strOpenid = list.get(i).get("strOpenid");
// 如果openid不为空,则放入openids集合中(用来推送模板消息)
if(strOpenid != null){
openids.add(strOpenid.toString());
}
list.get(i).put("strGuid", UUID.randomUUID().toString());
list.get(i).put("strNewsGuid",info_News.getStrGuid());
list.get(i).put("strCreator",info_News.getStrCreator());
list.get(i).put("dtCreateTime", info_News.getDtCreateTime());
mapper.insertMap(list.get(i));
}
if(i % 1000 == 0 || i == list.size() - 1){
sqlSession.commit();
sqlSession.clearCache();
}
}
}catch (Exception e) {
logger.info("事物已回滚");
loggerErr.info("发布任务失败 ==>" + e.getMessage());
sqlSession.rollback();
e.printStackTrace();
}finally {
sqlSession.close();
}
PropertiesUtils utils = null;
try {
utils = new PropertiesUtils("global/param_hexi.properties");
} catch (Exception e) {
logger.info("global/param_hexi.properties 文件找不到");
e.printStackTrace();
return;
}
// 微信模板消息,任务的模板id
if(openids.size() > 0){
String template_id = utils.getProperty("template_id");
sendNewsToWebchatUser(template_id, openids, info_News);
}
}
}).start();
}
//不同的写法
package com.qyl.java;
/**
* 开发一个类 实现Runnable 并重写 run() 方法
*/
class MyThreadRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0){
System.out.println(Thread.currentThread().getName() + "=================" +i);
}
}
}
}
/**
* 线程同步与安全
*/
public class SyncThreadTest {
public static void main(String[] args) {
MyThreadRunnable m1 = new MyThreadRunnable();
Thread t1 = new Thread(m1);
t1.setName("子线程1");
t1.start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 != 0){
Thread.currentThread().setName("主线程");
System.out.println(Thread.currentThread().getName() + "=================" +i);
}
}
}
}).start();
}
}
5.使用同步机制将单例模式中的懒汉式改写为线程安全的
1.效率较低的方式
package com.qyl.java;
/**
* 使用同步机制将单例模式中的懒汉式改写为线程安全的
*/
public class BankTest {
}
class Bank{
private Bank(){}
private static Bank instance = null;
public static Bank getInstance(){ //加上synchronized就变成线程安全的了
synchronized () {
if (instance == null){
instance = new Bank();
}
return instance;
}
}
}
2.效率高点
package com.qyl.java;
/**
* 使用同步机制将单例模式中的懒汉式改写为线程安全的
*/
public class BankTest {
}
class Bank{
private Bank(){
}
private static Bank instance = null;
public static Bank getInstance(){ //加上synchronized就变成线程安全的了
if (instance == null) {
synchronized (Bank.class) {
if (instance == null) {
instance = new Bank();
}
}
}
return instance;
}
}
6.线程死锁问题
1.死锁案例
package com.qyl.java;
/**
* 演示死锁问题
*/
public class SiSuo {
public static void main(String[] args) {
StringBuffer s1 = new StringBuffer();
StringBuffer s2 = new StringBuffer();
new Thread(){
@Override
public void run() {
synchronized (s1){
s1.append("a");
s2.append("1");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (s2){
s1.append("b");
s2.append("2");
System.out.println(s1);
System.out.println(s2);
}
}
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (s2){
s1.append("c");
s2.append("3");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (s1){
s1.append("d");
s2.append("4");
System.out.println(s1);
System.out.println(s2);
}
}
}
}).start();
}
}
2.解决线程安全问题三 Lock锁
Inspection '在使用阻塞等待获取锁的方式中,必须在try代码块之外,并且在加锁方法与try代码块之间没有任何可能抛出异常的方法调用,避免加锁成功后,在finally中无法解锁。说明一:如果在lock方法与try代码块之间的方法调用抛出异常,那么无法解锁,造成其它线程无法成功获取锁。说明二:如果lock方法在try代码块之内,可能由于其它方法抛出异常,导致在finally代码块中,unlock对未加锁的对象解锁,它会调用AQS的tryRelease方法(取决于具体实现类),抛出IllegalMonitorStateException异常。说明三:在Lock对象的lock方法实现中可能抛出unchecked异常,产生的后果与说明二相同。' options
package com.qyl.java;
import java.util.concurrent.locks.ReentrantLock;
class WindowLock implements Runnable{
private int titck = 100;
//1.实例化一个lock
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true){
try {
lock.lock();
//2.调用lock()
if (titck> 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 卖票-----票号码为: " + titck );
titck--;
} else {
break;
}
} finally {
//调用解锁方法
lock.unlock();
}
}
}
}
public class LockTest {
public static void main(String[] args) {
WindowLock m1 = new WindowLock();
Thread t1 = new Thread(m1);
Thread t2 = new Thread(m1);
Thread t3 = new Thread(m1);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
7.wait() notify()
1.两个线程交叉打印100以内的数
package com.qyl.springboot.controller;
// 2个线程交叉打印100以内的数字
class WaitNotify implements Runnable{
private int number = 1;
@Override
public void run() {
while (true){
synchronized (this) {
//唤醒全部线程
//notifyAll();
//唤醒线程
notify();
if (number<=100){
//睡眠 不释放线程 不释放锁
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" ---打印了 : "+number);
number++;
//等待 但是释放线程 释放锁
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
break;
}
}
}
}
}
public class WaitNotifyTest {
public static void main(String[] args) {
WaitNotify w1 = new WaitNotify();
Thread t1 = new Thread(w1);
Thread t2 = new Thread(w1);
t1.setName("线程1 +++++: ");
t2.setName("线程2 -----: ");
t1.start();
t2.start();
}
}
2.wait(),notify(), notifyAll()
只能用在同步代码块或者方法中 lock不能用
并且调用者必须是同步监视器
这三个方法是定义在对象Object中的 所以都可以用对象调
3.sleep() 和wait()区别
相同点:都可以使得当前线程进入阻塞状态
不同点: 1) Thread类中声明Sleep() ,Object类中声明Wait()
2) sleep()不会释放锁,wait() 会
3.)sleep ()在任何场景都可调用,wait只能在同步代码中调用
8.测试题
1.存钱 ,两个人 公用一个账户,各村三次 每次1000
方法一:
package com.qyl.springboot.controller;
/**
* 测试
* 存钱 ,两个人 公用一个账户,各村三次 每次1000
* 1.多线程 : 两个人
* 2.共享数据 : 账户
* 所以有了线程安全问题
*/
public class AccountTest {
public static void main(String[] args) {
//定义初始化的账户
Account account = new Account(1000);
//声明两个对象 去开线程
Customer c1 = new Customer(account);
Customer c2 = new Customer(account);
c1.setName("甲");
c2.setName("乙");
c1.start();
c2.start();
}
}
/**
* 创建一个账户类
*/
class Account {
//声明账户余额
private double balance;
//构造器
public Account(double balance) {
this.balance = balance;
}
//往里面存钱的方法
//加synchronize 同步
public synchronized void cunQian (double atm){
if ((atm>0)){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("当前余额: "+balance);
balance += atm;
System.out.println(Thread.currentThread().getName()+" : 存了 "+atm+" 元,已到账.当前余额为"+balance+"员");
}
}
}
//用户类,继承Thread
class Customer extends Thread{
//注入 Account
private Account account;
//构造器
public Customer(Account account) {
this.account = account;
}
//子类重写父类Run方法
@Override
public void run() {
for (int i = 0; i < 3; i++) {
//调用存钱方法
account.cunQian(1000);
}
}
}
方法二:
package com.qyl.springboot.javaTest;
public class CustomerQyl extends Thread{
private AccountQyl accountQyl;
public CustomerQyl(AccountQyl accountQyl) {
this.accountQyl = accountQyl;
}
@Override
public void run() {
for (int i = 0; i < 4; i++) {
String s = accountQyl.Pay(1000);
System.out.println(Thread.currentThread().getName()+" *** "+s);
}
}
}
package com.qyl.springboot.javaTest;
public class AccountQyl {
private double balance;
public AccountQyl(double balance) {
this.balance = balance;
}
public synchronized String Pay (double atm){
if (atm>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("当前余额: "+balance);
balance += atm;
System.out.println(Thread.currentThread().getName()+" : 存了 "+atm+" 元,已到账.当前余额为"+balance+"员");
}
return "存钱成功";
}
}
package com.qyl.springboot.javaTest;
public class testSync {
public static void main(String[] args) {
AccountQyl accountQyl = new AccountQyl(300);
CustomerQyl c1 = new CustomerQyl(accountQyl);
CustomerQyl c2 = new CustomerQyl(accountQyl);
c1.setName("甲");
c2.setName("乙");
c1.start();
c2.start();
}
}
2.java 递归
package com.esint.jm.wx_hxpublic.util;
//递归 汉诺塔
public class QylDiGuiTest {
static int times = 0;
public static void move (int num , char source , char dest){
System.out.println("第"+(++times)+"步骤 "+num +" 号盘子从 "+source+" 柱子到 "+dest+"柱子");
}
public static void hnt (int n,char source,char mid,char dest){
if (1 == n){
move(n,source,dest);
}else {
hnt(n-1,source,dest,mid);
move(n,source,dest);
hnt(n-1,mid,source,dest);
}
}
public static void main(String[] args) {
hnt(5,'A','B','C');
}
}