Java 多线程

Java 多线程,第1张

Java 多线程

目录

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注意
  1. 多线程出现了安全问题
  2. 问题的原因:

当多条语句在 *** 作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有 执行完,另一个线程参与进来执行。导致共享数据的错误。

     3.解决办法:

对多条 *** 作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以 参与执行。

2.4 同步方法

Synchronized的使用方法

  1. Java对于多线程的安全问题提供了专业的解决方式:同步机制
  1. 同步代码块:

synchronized (对象){

// 需要被同步的代码;

}

  1. synchronized还可以放在方法声明中,表示整个方法为同步方法。

例如:

public synchronized void show (String name){

….

}

3.  同步机制中的锁

同步锁机制:

在《Thinking in Java》中,是这么说的:对于并发工作,你需要某种方式来防 止两个任务访问相同的资源(其实就是共享资源竞争)。 防止这种冲突的方法 就是当资源被一个任务使用时,在其上加锁。第一个访问某项资源的任务必须 锁定这项资源,使其他任务在其被解锁之前,就无法访问它了,而在其被解锁 之时,另一个任务就可以锁定并使用它了。

  1. synchronized的锁是什么?
  • 任意对象都可以作为同步锁。所有对象都自动含有单一的锁(监视器)。
  • 同步方法的锁:静态方法(类名.class)、非静态方法(this)
  • 同步代码块:自己指定,很多时候也是指定为this或类名.class
  1. 注意:
  • 必须确保使用同一个资源的多个线程共用一把锁,这个非常重要,否则就

无法保证共享资源的安全

  • 一个线程类中的所有静态方法共用同一把锁(类名.class),所有非静态方 法共用同一把锁(this),同步代码块(指定需谨慎)

释放锁的 *** 作

  1. 当前线程的同步方法、同步代码块执行结束。
  2. 当前线程在同步代码块、同步方法中遇到break、return终止了该代码块、 该方法的继续执行。
  3. 当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束。
  4. 当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线 程暂停,并释放锁。

不会释放锁的 *** 作

  1. 线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行
  2. 线程执行同步代码块时,其他线程调用了该线程的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 的对比
  1. Lock是显式锁(手动开启和关闭锁,别忘记关闭锁),synchronized是 隐式锁,出了作用域自动释放
  2. Lock只有代码块锁,synchronized有代码块锁和方法锁
  3. 使用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();
		}
	}
}

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/4828190.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-11-10
下一篇 2022-11-10

发表评论

登录后才能评论

评论列表(0条)

保存