线程
Thread
- 一、程序
- 1.一段静态代码(静态)
- 二、进程
- 1.动态的,有开始,有结束;
- 2.程序的一次执行过程,
- 3.操作系统调度分配资源的最小单位;
- 三、线程
- 1.进程细化为线程
- 2.内部的一条执行路径;
- 3.多线程,
- 4.CPI调度、分配的最小单位;
- 5.多线程间可以通信,一个进程共享一个堆;(会有安全问题);
- 6.每个进程有一个独立的内存空间;
- 7.线程调度
- (1)分时调度;(平均分配每个时间)
- (2)抢占式调度;(java);提高响应,设置优先级;
并行:
- 1、多个事件,同一时间发生;
- 2、多条指令在多个cpu上同时执行;(多核)
- 3、三个大厨在炒菜;
并发:
- 1、多个事件,在同一个时间段内发生;(同一时刻可能只有一个)
- 2、Cpu快速切换处理;
- 3、一个大厨在炒三个菜;
线程:java.lang.Thread
- 1、创建:
- (1)继承thread类
- ①创建子类继承Thread,
- ②重写run方法
- ③创建对象
- ④调用start方法;
- 1)启动线程
- 2)调用当前线程的run方法;
- 3)结束前不能继续调start;不能让已经start的线程继续start否则报异常;
- ⑤多个线程间的执行没有冲突,同时执行;
- (1)继承thread类
例1
class F extends Thread {@Overridepublic void run() {System.out.println(1);}
}
例2
class H extends Thread {static Object obj = new Object();static int total = 100;@Overridepublic void run() {/* 同步代码块 *//* 使用static修饰后可以保证obj的唯一性 */synchronized (obj) {H.total--;System.out.println(H.total);}/*此处this不能保证唯一性/慎用*/synchronized (this) {H.total--;}synchronized (Window.class) {H.total--;}synchronized (H.class) {H.total--;}}
}
例3
public class ThreadTest {@Testpublic void test(){A a = new A();a.start(); //开始创建新线程;/* 还是主线程 */for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName() + ": " + i);}A a2 = new A();a2.start();/* 匿名对象 */new Thread(() -> {for (int i = 0; i < 10; i++) {if(i%2==0) {System.out.println(Thread.currentThread().getName() + ": " + i);}}}).start();}
}class A extends Thread {@Overridepublic void run() {for (int i = 0; i < 10; i++) {if(i%2==0) {System.out.println(Thread.currentThread().getName() + ": " + i);}}}
}
- (2)实现Runnable接口
- ①创建一个实现类
- ②实现接口内的抽象run方法
- ③创建对象
- ④将对象作为参数,传入Thread构造器;创建实例
- ⑤创建实例后,调用start方法;
class J implements Runnable{int i = 100;@Overridepublic void run() {
// synchronized (J.class){
// method();
// }i--;}public void method() {i--;}/* 同步方法 */public synchronized void method2() {i--;}
}
public class ThreadTest2 {public static void main(String[] args) {B b = new B();
// new Thread(b){}.start();Thread t = new Thread(b);t.start();for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread().getName()+ ": " + i);}Thread t2 = new Thread(b);t2.start();/* 有问题的写法? */new Thread(b){@Overridepublic void run() {for (int i = 6; i < 10; i++) {System.out.println(Thread.currentThread().getName()+ ": " + i);}}}.start();new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread().getName()+ ": " + i);}}}).start();}
}
class B implements Runnable {@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread().getName()+ ": " + i);}}
}
public class ThreadTest3 {public static void main(String[] args) throws InterruptedException {D d = new D();Thread t = new Thread(d);t.setName("子线程");t.setPriority(Thread.MIN_PRIORITY);t.start();Thread.currentThread().setName("主线程");for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName() + ": " + i);if(i == 3) {t.join();}System.out.println(Thread.currentThread().getPriority());}}
}class D implements Runnable {@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName() + "_" + Thread.currentThread().getPriority() + ": " + i);if(i % 3 == 0) {
// Thread.yield();}}}
}
建议:
- 1、建议使用Runnable 方式
- (1)避免了类的单继承性的局限性
- (2)更适合处理有共享数据的情况
联系:
- 1、Thread 类 本来也是实现的Runnable的方法;
常用方法:
- 1、start
- 2、Run:线程内主要需要的任务
- 3、currenctThread(): 获取当前线程
- 4、getName(): 获取线程的名称;
- 5、SetName: 设置线程名称
- 6、Sleep(ms): 静态方法:
- (1)当前线程休眠指定毫秒数;
- (2)会抛出异常;
- 7、Yield();静态方法
- (1)主动释放cpu的执行权
- (2)Cpu可能去执行其他的线程;
- (3)也可能很快继续分配回来;
- 8、Join(); 在线程A中通过线程B调用join方法;之后线程A被阻塞,直到B执行结束;
- 9、isAlive(); 线程是否存活
- 10、过时方法:stop,强行结束一个线程,使其死亡。不建议使用;
- 11、Suspend/resume; 操作失误可能造成死锁,不建议使用;
- (1)Suspend: 暂停
- (2)Resume:继续;
生命周期:
-
1、Jdk1.5之前
- (1)
-
2、Jdk 1.5 及之后
-
(1)
-
(2)阻塞分的更细了
-
(3)可以查看Thread的enum枚举类State;
-
优先级:
- 1、getPriority(); 获取优先级;
- (1)默认级别都是5;
- (2)最低的是1,最高的是10;
- 2、THread 内部常量:
- (1)MIN_PRIORITY: 1;
- (2)MAX_ : 10
- (3)NORMAL_ 5;
- 3、setPriority(): 设置优先级;
- (1)设置完之后不一定是哪个优先
- (2)内部有cpu等规则;
- 4、
线程安全
使用线程的同步机制
1、同步代码块
* (1)synchronized(同步监视器) {* ①需要被同步的代码* ②被操作的共享数据;
* };
- ******代码块被视为一个整体,使用syncxxx包裹后,一个线程结束后。另一个线 程才能执行;
- (2)同步监视器
- ①俗称锁;
- ②可以使用任何一个类的对象充当;
- ③多个线程,必须公用同一个同步监视器;
- ④这个对象必须是唯一的;可以尝试使用this;
- ⑤可以使用Window.class;
- ⑥可以使用当前类.class;
- (3)Ctrl+alt+t
2、同步方法;
- (1)S 如果操作共享数据的代码,在一个方法中
- (2)直接给方法添加修饰符:synchronzied;
- (3)非静态同步监视器默认就是this;(不可修改)
- ①此时考虑this是否唯一;
- (4)如果可以改成静态方法,可以考虑使用静态方法;
- ①默认的监视器是当前类.class;
- (5)如果this不唯一;
- ①类声明多个实例的情况会有不唯一的情况;
- (6)
public class WaitTest {public static void main(String[] args) {WaitTest wt = new WaitTest();wt.method();}public synchronized void method(){System.out.println(1);try {this.wait(1000);System.out.println(3);} catch (InterruptedException e) {e.printStackTrace();}this.notify();System.out.println(2);}//wait 中添加了时间之后打印顺序: 1-3-2;// wait 中不添加时间的话,只打印1;
}
推荐使用implements Runnable;方式;
5.0新特性:Lock
public class LockTest {private static final ReentrantLock lock = new ReentrantLock();public static void main(String[] args) {}public void method() {try {lock.lock();new Thread(){@Overridepublic void run() {System.out.println(1);}}.start();} finally {lock.unlock(); //为保证unlock被执行,放到finally中;}}
}
指令重排?是什么
- 1、volatile 修饰符避免
- 2、Volatile A a= new A();
死锁
- 1、互相抢占资源,等待对方释放;
- 2、每人一个监视器,等待对方释放监视器
- 3、拿不到监视器,对面不释放;
- 4、见代码:DeadLockTest.java; 加上sleep后可以高概率复现死锁;
public class DeadLockTest {public static void main(String[] args) {StringBuilder s1 = new StringBuilder();StringBuilder s2 = new StringBuilder();new Thread(){@Overridepublic void run() {synchronized (s1){s1.append("a");s2.append("1");/* 加上sleep之后1秒之内不会释放,另一个线程等着用s1;这个线程等着用s2 */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(){@Overridepublic void run() {synchronized (s2){s2.append("-3");s1.append("-c");synchronized (s1){s2.append("4");s1.append("d");}System.out.println(s1);System.out.println(s2);}}}.start();}
}
如何看待:
- 1、避免;
原因及解决避免:(破坏死锁的形成条件)
- 1、互斥条件:=>
- (1)无法处理;
- 2、占用且等待: =>
- (1)一次性拿到所有资源;
- 3、不可抢夺: =>
- (1)申请不到资源时,主动释放资源;
- 4、循环等待: =>
- (1)改为线性顺序;排号;
Lock锁:
- 1、解决线程安全;
- 2、JUC:=> java.util.concurrent;
- 3、Private static [final] ReentrantLock lock = new RenntrantLock();
- 4、lock.lock();
- (1)可能不安全的逻辑;
- 5、lock.unlock(); => 必须要保证一定会执行;
- Lock锁定对共享资源的调用;
- Lock好过synchronized;lock不需要包上正片代码
线程通信
一、两个线程,交替打印12345678.。。
- 1.See the code;
- Wait();
- 1、wait(); //进入等待并释放对同步监视器的调用;而sleep不会释放;
- 2、Notify(); //唤醒wait;
- (1)只能唤醒一个
- (2)唤醒被wait中优先级最高的
- (3)如果优先级相同,则随机唤醒;
- (4)然后被唤醒的,继续上次被wait的地方继续执行;
- 3、NotifyAll(): 唤醒所有wait的线程;
public class CommunicationTest {public static void main(String[] args) {Q q = new Q();Thread t1 = new Thread(q);Thread t2 = new Thread(q);t1.start();t2.start();}
}
class Q implements Runnable {static Object obj = new Object();private static int count = 100;@Overridepublic void run() {while (true) {synchronized (obj) { // 因为和notify和wait的调用者不一致了;// notify(); //唤起waitobj.notify(); //唤起waitif (count > 0) {try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + ": " + count);count--;try {obj.wait(); //进入等待并释放对同步监视器的调用;} catch (InterruptedException e) {e.printStackTrace();}} else {break;}}}}
}
/* 实现两个线程交替打印 */
//class Q implements Runnable {
// static Object obj = new Object();
// private static int count = 100;
//
// @Override
// public void run() {
// while (true) {
// synchronized (this) { //此处除了使用this,使用其他的都会报错;因为和notify和wait的调用者不一致了;
//
notify(); //唤起wait
// this.notify(); //唤起wait
//
// if (count > 0) {
// try {
// Thread.sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//
// System.out.println(Thread.currentThread().getName() + ": " + count);
// count--;
//
// try {
// wait(); //进入等待并释放对同步监视器的调用;
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//
// } else {
// break;
// }
// }
//
//
// }
// }
//}//class Q implements Runnable {
// private static int count = 100;
// @Override
// public void run() {
// while (count > 0) {
// try {
// Thread.sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// synchronized (Q.class){
// System.out.println(Thread.currentThread().getName() +": "+count);
// count--;
// }
// }
// }
//}
使用:
- 1、必须用在同步代码块,或者同步方法中;(synchronized)
- 2、不能和lock搭配使用;
- 3、Lock需要配合condition通信(更灵活);
- 4、监视器必须和wait和notify的调用者一致;
创建线程
- 1、Callable: 5.0新增;
/*** @Date 2023/8/3 14:51* @Discrition 非完全代码;callable 需要结合 futureTask 来使用;*/
public class ClassableTest {public static void main(String[] args) {W w = new W();w.call();
// try {
// w.call();
// } catch (Exception e) {
// throw new RuntimeException(e);
// }}
}class W implements Callable {@Overridepublic Object call() {synchronized (W.class) {System.out.println(1);}return null;}// public Object call() throws Exception {
// synchronized (W.class) {
// System.out.println(1);
// }
// return null;
// }
}
- 2、线程池:生产中使用;
线程池:
- 1、先创建好多个空线程,等待任务来了执行;可以复用;
public class ThreadPondTest {public static void main(String[] args) {
// ThreadPoolExecutor executor = new ThreadPoolExecutor();}
}class R {}
异常
Throwable两个子类:
1、Error
- (1)处理方式:只能改代码;(jvm资源耗尽,无法处理)
- (2)错误类型:
- ①Stack Overflow Error;栈溢出
- ②OutOfMemoryError;堆内存移除;==>OOM
2、Exception
- (1)可以处理,通过代码catch让程序继续执行;
- (2)区分:
- ①Java~运行时异常:只有一个(runtimeException=>扩展开还有很多)
- ②Javac~编译时异常
Exception:
- 1、将无线重复的ifelse=》改为。。。
- 2、将所有可能报错的放在一起处理下;
public class ThrowsTest {@Testpublic void test(){try {ThrowsTest.method1();}finally {System.out.println(1);}System.out.println(2);}public static void method1() throws OutOfMemoryError{int[] arr = new int[] {1,2};System.out.println(arr[2]);System.out.println(3);}@Testpublic void test2(){new B();}
}
class A {void method1() throws NullPointerException{}
}
class B extends A{void method1(){}
}
处理方式:
方式一 try-catch-finally(抓抛模型)**
1、自定义逻辑
- (1)抛出: 产生异常对象,抛出;
- (2)抓:捕获并处理,处理后代码可以继续执行;
- (3)Finally 无论什么情况,代码块内都会执行;
2、printStackTrace (最常用-推荐)打印堆栈信息
- (1)1111
- 3、
- 4、
- 5、
- 6、 运行时异常不做处理,主要处理编译时异常;
Catch结构顺序:
- 1、如果有多个catch结构,其中的异常类型无子父类关系则无顺序
- 2、如果有子父类关系,则父类要放到后面;
Finally:
- 1、在catch还存在异常的话,后面的代码不放到finally中的话不会被执行;
- 2、一定要被执行的语句,放到finally中;
- 3、Finally比catch先执行;
- 4、Finally比try中先执行;
System.exit(0); 虚拟机强行结束;
方式二
- 1、Throws
- 2、格式:
- (1)Throws 类型1, 类型2 。。。 ;
- (2)Public void test() throws NullxxxxException{}
- 3、是否解决了异常
- (1)向上抛出,不算真正解决;需要调用者来继续处理;
继承:
- 1、父类方法使用了throws,子类的方法如果也使用throws则必须为同类型异常,或子类异常,可以不是用throws;
- 2、RuntimeException 这个范围类型写了没用;
- 3、如果父类的方法没有抛出异常,则子类方法中不能抛出异常;
手动抛出异常:throw;
- 1、throw new Object(“description”);
public class ThrowsTest {@Testpublic void test(){try {ThrowsTest.method1();}finally {System.out.println(1);}System.out.println(2);}public static void method1() throws OutOfMemoryError{int[] arr = new int[] {1,2};System.out.println(arr[2]);System.out.println(3);}@Testpublic void test2(){new B();}
}
class A {void method1() throws NullPointerException{}
}
class B extends A{void method1(){}
}
自定义异常
- 1、创建类继承自某个异常类;
- 2、通常继承自 RuntimeException 或者Exception;
public class DIYException extends RuntimeException{
}
- 3、空参构造器
- 4、形参构造器
- 5、常量(序列版本号);
- 6、,即可抛出
public class ManuThrowTest {int i = 1;@Testpublic void test(){this.i = 0;this.method1();}public void method1(){if(this.i > 0) {}else {throw new NullPointerException("ghjg");}}public void method2() throws Exception {throw new Exception("123"); //必须搭配处理;(因为是编译时错误)}public void method3() {throw new DIYException(); //自定义异常类;}
}
为什么要自定义?
- 1、见名知意;
- 2、已有类型不足以见名知意;
~~~~运行时异常可以不处理,编译时异常必须要处理;或trycatch或throws~~~~