package com.qyl.springboot.controller;// 1.创建一个子类类继承Threadclass MyThread extends Thread{ //2.在子类中重写Thread的run()方法 线程要做的事写到run()方法中 @Override public void run() { for (int i = 0; i < 100; i++) { if (i % 2 == 0){ System.out.println(i+"---"+Thread.currentThread().getName()); } } }};public class ThreadTest { public static void main(String[] args) { // 3.创建该子类的对象 //主线程执行 MyThread myThread = new MyThread(); MyThread myThread1 = new MyThread(); //4.通过此对象调用start方法 //创建出来的线程执行 myThread.start(); myThread1.start();//乡启动多个就多创建对象即可 for (int i = 0; i < 100; i++) { if (i % 2 == 0){ System.out.println(i+ " ++++++++++++++-"+Thread.currentThread().getName()); } }//这种方法也可以 匿名子类 System.out.println("/"); new Thread(){ @Override public void run() { for (int i = 0; i < 100; i++) { if (i % 2 != 0){ System.out.println(Thread.currentThread().getName()+""+ i ); } } } }.start(); }}
输出结果:
48---Thread-078---Thread-150---Thread-018 ++++++++++++++-main52---Thread-080---Thread-182---Thread-184---Thread-1
2、多线程创建方式2实现Runnable接口
1.创建一个实现了Run那边了接口的类
2.实现类去实现Runable中的抽象方法
3.创建实现类的对象
4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
5.通过Thread类的对象调用start()方法
package com.qyl.springboot.controller;//1.创建一个实现了Run那边了接口的类class MyRunnable implements Runnable{ //2.实现类去实现Runable中的抽象方法 @Override public void run() { for (int i = 0; i < 100; i++) { if (i % 2 == 0){ System.out.println(""+i+ " ++++++++++++++-"+Thread.currentThread().getName()); } } }}public class RunableTest { public static void main(String[] args) { //3.创建实现类的对象 MyRunnable m1 = new MyRunnable(); //4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象 Thread thread = new Thread(m1); thread.setName("liudeh"); thread.start(); Thread t2 = new Thread(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.同步的方式虽然解决了线程安全 * 缺点是同步代码块里面是单线程 */
package com.qyl.java;class Window implements Runnable{ private int titck = 100; Object object = new Object(); @Override public void run() { while (true){ //同步代码块 synchronized(object){ if (titck>0){ System.out.println(Thread.currentThread().getName()+" 卖票-----票号码为: " + titck ); titck--; }else { break; } } } }}public class SyncThread { public static void main(String[] args) { Window m1 = new Window(); 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(); }}
注意:implement Runnable 的时候 this(当前对象 ) 就可以当做锁 extends Threads的时候 不行
2.同步方法// 1.一种情况可以直接把run改为同步的,但是要注意场景class Window implements Runnable{ private int titck = 100; Object object = new Object(); @Override public synchronized void run() { while (true){ //同步代码块 synchronized(this){ if (titck>0){ System.out.println(Thread.currentThread().getName()+" 卖票-----票号码为: " + titck ); titck--; }else { break; } } } }}
1.同步方法解决Runnablepackage com.qyl.java;class Window implements Runnable{ private int titck = 100; Object object = new Object(); @Override public void run() { while (true){ sync(); } } private synchronized void sync (){ //同步监视器就是this //同步代码块 if (titck>0){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" 卖票-----票号码为: " + titck ); titck--; } }}public class SyncThread { public static void main(String[] args) { Window m1 = new Window(); 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(); }}
2.同步方法解决threadpackage com.qyl.java;class Windows extends Thread { private static int tickets = 100; private static Object object = new Object(); @Override public void run() { while (true) { //同步代码块 show(); } }private static synchronized void show(){ //加个static 就可以了 if (tickets > 0) { try { sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " 卖票-----票号码为: " + tickets); tickets--; }}}public class extendsThread { public static void main(String[] args) { Windows w1 = new Windows(); Windows w2 = new Windows(); Windows w3 = new Windows(); 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
//不同的写法
package com.qyl.java;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;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+"员"); } }}//用户类,继承Threadclass 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'); }}