目录
1.线程控制
1.Join
1.2 Yield
2.线程的同步
2.1问题的提出
2.2 例题:
模拟火车站售票程序,开启三个窗口售票
2.3注意
2.4 同步方法
3. 同步机制中的锁
4.Lock(显式锁)
4.1是什么
4.2 synchronized 与 Lock 的对比
5. 定时器
6. 死锁
6.1是什么
7.线程通信
7.1概述
7.2使用方式
1.线程控制 1.Join
join : 合并线程 ,多干线程合并为一个线程
package com; public class Thread_01_Join { public static void main(String[] args) { //创建线程对象 Thread t1 =new Thread(new Processer_01()); Thread t2 =new Thread(new Processer_01()); //线程设置名字 t1.setName("我是t1"); t2.setName("是我t2"); //启动线程 t1.start(); t2.start(); //执行到join的时候 ,因为时t1调用的,所以main之后的代码,必须等t1执行完之后才能执行 try { t1.join(); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0;i<5 ;i++){ System.out.println(Thread.currentThread().getName()+" : " + i); } } } //接口线程 class Processer_01 implements Runnable{ //覆写run方法 @Override public void run(){ for(int i = 0 ; i<5 ; i++){ //currentThread : 获取当前线程队象 getName : 获取当前线程的名字 System.out.println(Thread.currentThread().getName()+" : "+ i); } } }1.2 Yield
yield : 暂停当前正在执行的线程,并让其他线程执行
1. yield是静态方法, 写在那个线程里,就让那个线程让位
2. 给同等级的优先级让位 , 不同优先级不让位
3. 只让出当前执行的时间片, 下次让不让不一定(也就是说只能保证让一次位)
yield() 应该做的是让当前运行线程回到可运行状态 , 以允许有相同优先级的其他线程获得运行机会
因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行.
但是,实际中无法保证yield()达到让步目的, 因为让步的线程还有可能呗线程调度程序再次选中.
结论 : yield() 从未导致下属昵称转到等待/睡眠/阻塞状态.
在大多数情况下,yield() 将导致线程从运行状态转到可运行状态,但有可能没有效果
public class Thread_02_Yield { public static void main(String[] args) { Thread t1 = new Processor_02(); t1.start(); for(int i=0;i<10;i++){ System.out.println(Thread.currentThread().getName()+" : "+i); } } } class Processor_02 extends Thread{ @Override public void run() { for (int i = 0; i < 10; i++) { if (i % 2 == 0) { Thread.yield(); } System.out.println(getName()+" : "+i); } } }2.线程的同步 2.1问题的提出
多个线程执行的不确定性引起执行结果的不稳定
多个线程对账本的共享,会造成 *** 作的不完整性,会破坏数据
线程同步 :
当多个线程同时 *** 作同一个数据的时候,尤其是更改 *** 作,为了保证数据的一致性和正确性,需要进行一定的保护
所以线程同步是一种数据安全机制
同步编译和异步编译
同步编译 : 线程之间不是独立的,相互有影响,必须一个个执行
异步编译 : 线程之间是独立的,相互吗,没有影响
以下程序 因为同时 *** 作了某个数据,导致结果不正确
1. 可以使用 synchronized修饰符解决
使用方式 : public synchronized void m1(){} 使用synchronized的方法,
不能被多个线程同时执行
比如 访问该对象中的某一个加锁的成员方法,那么该队昂中所有加锁的成员方法全部锁定,
其他线程都无法访问,只能拍到等待,等待该线程执行结束,交出锁
比如访问一个类的加锁的静态方法,那么该类中所有加锁的静态方法 全部锁定
2. synchronized{} 语句块解决
synchronized(对象){ //这种方式是把该对象中所有加锁的
成员方法和代码块锁全部锁定
同步代码;
}
synchronized(类名.class){ //这种方式是把该类中所有加锁的
静态方法和代码块锁全部锁定
同步代码;
}
package com; public class Thread_03_Synchronization { public static void main(String[] args) { //创建A对象传入参数10 ,也就是i=10 A a = new A(10); A a1 = new A(11); //创建线程对象 因为是接口类型所以用这样的创建对象的方法 Thread t1 = new Thread(new Processor_03(a)); Thread t2 = new Thread(new Processor_03(a)); t1.setName("t1"); t2.setName("t2"); t1.start(); t2.start(); } } class Processor_03 implements Runnable { A a; public Processor_03(A a) { super(); this.a = a; } @Override public void run() { a.m1(); } } class A{ int i ; //有参构造 public A(int i) { super(); this.i = i; } //方法锁 // public synchronized void m1(){ public synchronized void m1(){ System.out.println("1--------------------1"); try { //睡眠 会出现异常 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } i++; System.out.println(Thread.currentThread().getName()+" : "+i); System.out.println("2@@@@@@@@@@@@2"); } }2.2 例题: 模拟火车站售票程序,开启三个窗口售票
class Ticket implements Runnable { private int tick = 100; public void run() { while (true) { if (tick > 0) { System.out.println(Thread.currentThread ().getName() + "售出车票,tick号为:" + tick--); } else{ break; } } }
class TicketDemo { public static void main(String[] args) { Ticket t = new Ticket(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); Thread t3 = new Thread(t); t1.setName("t1窗口"); t2.setName("t2窗口"); t3.setName("t3窗口"); t1.start(); t2.start(); t3.start(); } }
理想状态:
极端状态 :
private int tick = 100; public void run(){ while(true){ if(tick>0){ try{ Thread.sleep(10); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+“售出车票,tick号为:"+tick--); } } }2.3注意
- 多线程出现了安全问题
- 问题的原因:
当多条语句在 *** 作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有 执行完,另一个线程参与进来执行。导致共享数据的错误。
3.解决办法:
对多条 *** 作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以 参与执行。
2.4 同步方法Synchronized的使用方法
- Java对于多线程的安全问题提供了专业的解决方式:同步机制
- 同步代码块:
synchronized (对象){
// 需要被同步的代码;
}
- synchronized还可以放在方法声明中,表示整个方法为同步方法。
例如:
public synchronized void show (String name){
….
}
3. 同步机制中的锁同步锁机制:
在《Thinking in Java》中,是这么说的:对于并发工作,你需要某种方式来防 止两个任务访问相同的资源(其实就是共享资源竞争)。 防止这种冲突的方法 就是当资源被一个任务使用时,在其上加锁。第一个访问某项资源的任务必须 锁定这项资源,使其他任务在其被解锁之前,就无法访问它了,而在其被解锁 之时,另一个任务就可以锁定并使用它了。
- synchronized的锁是什么?
- 任意对象都可以作为同步锁。所有对象都自动含有单一的锁(监视器)。
- 同步方法的锁:静态方法(类名.class)、非静态方法(this)
- 同步代码块:自己指定,很多时候也是指定为this或类名.class
- 注意:
- 必须确保使用同一个资源的多个线程共用一把锁,这个非常重要,否则就
无法保证共享资源的安全
- 一个线程类中的所有静态方法共用同一把锁(类名.class),所有非静态方 法共用同一把锁(this),同步代码块(指定需谨慎)
释放锁的 *** 作
- 当前线程的同步方法、同步代码块执行结束。
- 当前线程在同步代码块、同步方法中遇到break、return终止了该代码块、 该方法的继续执行。
- 当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束。
- 当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线 程暂停,并释放锁。
不会释放锁的 *** 作
- 线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行
- 线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁(同步监视器)。
应尽量避免使用suspend()和resume()来控制线程
4.Lock(显式锁) 4.1是什么1. 从JDK 5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当。
2. java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的 工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象 加锁,线程开始访问共享资源之前应先获得Lock对象。
3. ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和 内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以 显式加锁、释放锁。
4.2 synchronized 与 Lock 的对比- Lock是显式锁(手动开启和关闭锁,别忘记关闭锁),synchronized是 隐式锁,出了作用域自动释放
- Lock只有代码块锁,synchronized有代码块锁和方法锁
- 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
优先使用顺序:
Lock 同步代码块(已经进入了方法体,分配了相应资源)同步方法(在方法体之外)
class A{ private final ReentrantLock lock = new ReenTrantLock(); public void m(){ lock.lock(); try{ //保证线程安全的代码; } finally{ lock.unlock(); } } } 注意:如果同步代码有异常,要将unlock()写入finally语句块
package com; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Thread_04_Lock { public static void main(String[] args) { A1 a = new A1(10); Thread t1 = new Thread(new Processor_04(a)); Thread t2 = new Thread(new Processor_04(a)); t1.setName("t1"); t2.setName("t2"); t1.start(); t2.start(); } } class Processor_04 implements Runnable{ A1 a ; public Processor_04(A1 a) { super(); this.a = a; } @Override public void run() { a.m1(); } } class A1{ int i ; public A1(int i) { super(); this.i = i; } //创建锁对象 Lock显式锁 Lock lock = new ReentrantLock(); public void m1(){ System.out.println("@@@@@@"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //开始加锁 lock.lock(); i++; System.out.println(Thread.currentThread().getName()+" : "+i); //解锁 lock.unlock(); System.out.println("=========="); } }5. 定时器
package com; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; import java.util.TimerTask; public class Thread_05_Timer { public static void main(String[] args) throws ParseException { //创建定时器 Timer timer = new Timer(); //1. 做什么事 //2. 开始时间 ,可以是时间(到了指定时间开始u执行), 也可以是毫秒数(当前时间开始,多长时间之后开始执行) //3. 执行的间隔时间 //两秒之后开始执行,并且每隔1秒执行一次 // timer.schedule(new LogTimerTask(), 2000, 1000); long m =System.currentTimeMillis(); m+=1000*60; String string ="2021-10-29 19:30:00 000"; Date d =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").parse(string); timer.schedule(new LogTimerTask(), d, 1000); } } //任务类 class LogTimerTask extends TimerTask{ @Override public void run() { System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(new Date())); } }6. 死锁 6.1是什么
死锁 : 在执行过程中,都遇到了加锁的功能,从而进入等待状体,导致大家都访问不了
1. 某个线程执行完成,需要 先后 嵌套 锁定 两个对象
2. A线程 先进入第一个对象,并锁定第一个对象,在第一个对象中去嵌套访问并锁定第二个对象
3. B线程,先进入第二个对象,并锁定第二个对象,在第二个对象中去嵌套访问并锁定第一个对象
4. 当A线程把第一个对象锁定之后,要去访问第二个对象的时候
发现已经被B线程锁住了,只能等待B线程交出第二个对象的锁才才能执行
5. 当B线程把第二个对象锁定之后,要去访问第一个对象的时候,
发现已经被A线程锁住了,只能等待A线程交出第一个对象的锁才能执行
6. 因此 导致A和B都进入等待状态
public class Thread_06_DeadLock { public static void main(String[] args) { Object o1 = new Object(); Object o2 = new Object(); Thread t1 = new T1(o1,o2); Thread t2 = new T2(o1, o2); t1.start(); t2.start(); } } class T1 extends Thread{ Object o1 ; Object o2 ; public T1(Object o1, Object o2) { super(); this.o1 = o1; this.o2 = o2; } @Override public void run() { super.run(); synchronized(o1){ System.out.println("t1已经进入o1 准备进入o2"); synchronized(o2){ System.out.println("t1执行完成"); } } } } class T2 extends Thread{ Object o1 ; Object o2 ; public T2(Object o1, Object o2) { super(); this.o1 = o1; this.o2 = o2; } @Override public void run() { super.run(); System.out.println("@@@@@@@"); synchronized(o2){ System.out.println(" t2已经进入o2 准备进入o1"); synchronized(o1){ System.out.println("t2执行完成"); } } } }7.线程通信 7.1概述
包含:
7.2使用方式
package com; public class Thread_07_Wait { public static void main(String[] args) { //创建num对象 Num num = new Num(); //创建线程对象 Thread t1 = new PrintOdd(num); Thread t2 = new PrintEven(num); //命名 t1.setName("t1"); t2.setName("t2"); //启动 t1.start(); t2.start(); } } class PrintOdd extends Thread{ Num num; public PrintOdd(Num num) { super(); this.num = num; } @Override public void run(){ while(true){ num.printOdd(); } } } class PrintEven extends Thread{ Num num; public PrintEven(Num num) { super(); this.num = num; } @Override public void run(){ while(true){ num.printEven(); } } } //业务类 class Num{ int count = 1; public synchronized void printOdd(){ System.out.println(Thread.currentThread().getName()+" : "+count); count++; this.notifyAll(); try { Thread.sleep(500); //挂起 交出该对象持有的锁 , 让其他线程可以执行 this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } public synchronized void printEven(){ System.out.println(Thread.currentThread().getName()+" : "+count); count++; this.notifyAll(); try { Thread.sleep(500); //挂起 交出该对象持有的锁 , 让其他线程可以执行 this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)