1、多线程的方式1-实现Runnable接口(推荐使用)

优点:,可以多实现不可以多继承

/*
 * 使用Thread类实现多线程的程序(多开启执行路径)
 * 
 * 实现步骤:
 *   创建Thread类对象,构造方法中,传递Runnable接口的实现类对象
 *   Thread类调用方法start()开启线程
 */
public class Demo_01Thread {
	public static void main(String[] args) {
		//创建Runnable接口的实现类对象
		Demo_02Runnable runnable = new Demo_02Runnable();
		//创建Thread类对象,传递接口的实现类对象
		Thread thread = new Thread(runnable);
		//thread类方法start()开启线程,start方法不能执行2次。
		thread.start();
		//这样main中和Demo_02Runnable中run方法就同时开始执行了
		for(int x = 0 ; x< 100;x++){
			System.out.println("main..."+x);
		}
	}
}
/*
 * 创建的类,实现Runnable接口
 * 重写接口抽象方法,全部
 * 
 * run()方法中,就是线程要执行的目标方法
 */
public class Demo_02Runnable implements Runnable{
	public void run()  {
	    //调用Thread类静态方法currentThread()获取,正在运行run的线程对象
		//Thread t = Thread.currentThread();
		//调用Thread类方法getName()获取线程名字
		//System.out.println(t.getName());Thread-0
	  for(int x = 0 ; x< 100;x++){
	        //调用Thread类静态方法sleep(毫秒),休眠多少毫秒再往下执行
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("run..."+x);
		}
	}
}

2、多线程的方式2-继承Thread

public class Demo_01Thread {
	public static void main(String[] args) {
		//创建Thread子类对象
		Demo_02SubThread sub = new Demo_02SubThread();
		
		//子类对象,调用父类方法start
		sub.start();
	}
}
public class Demo_02SubThread extends Thread{
	//	private int tickets = 100;
	public void run(){
		//Thread-0继承方式,实现线程	
		System.out.println(getName()+"继承方式,实现线程");
	}
}

3、多线程的方式3-匿名内部类,匿名对象,创建线程程序

/*
 * 匿名内部类,匿名对象,创建线程程序
 * 匿名内部类: 继承或者实现接口
 *  new 父类或者接口(){
 *    重写抽象方法(){}
 *  }.方法();
 */
public class Demo_03Thread {
	public static void main(String[] args) {
		/*Runnable r = new Runnable(){
			public void run(){
				System.out.println("线程实现了");
			}
		};
		new Thread(r).start();*/
		//第一种方式
		new Thread(new Runnable(){
			public void run(){
				System.out.println("线程实现了");
			}
		}).start();
		//第二种方式匿名对象
		new Thread(){
			public void run(){
				System.out.println("线程实现了2222");
			}
		}.start();
	}
}

4、Thread类方法介绍

/*
 * Thread类方法:静态方法(类名直接调用)
 * static Thread currentThread()获取当前正在执行的线程对象
 * 在那个线程中执行,获取的就是那个线程的对象
 * Thread类非静态方法 String getName()获取线程名字,main中线程名为main
 * Thread类静态方法 sleep(1000) 线程暂时休眠
 */
String name = Thread.currentThread().getName();
Thread.sleep(500);

5、线程安全问题,同步代码块、Lock的使用
synchronized补充:与同步代码块相同,java还提供了同步方法来解决同步问题。
同步方法的格式:在方法的返回值前加synchronized关键字,该方法的锁默认使用this。
如果方法为静态方法,则锁默认使用该类的字节码文件对象:类名.class

/*
 * 开启3个线程,每个线程都去调用方法run
 * 但是,run方法数据,是共享
 */
public class Demo_01Thread {
	public static void main(String[] args) {
		Demo_02Ticket ticket = new Demo_02Ticket();
		Thread t0 = new Thread(ticket);
		Thread t1 = new Thread(ticket);
		Thread t2 = new Thread(ticket);
		
		t0.start();
		t1.start();
		t2.start();
	}
}
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/*
 * 模拟售票的案例
 * 解决安全问题: 保证一个线程没有执行完毕 ,其他的线程不运行执行
 * 同步技术: 关键字
 *   synchronized
 * 同步代码块格式
 *  synchronized(任意对象){
 *     线程操作的所有共享数据
 *  }
 *  
 *  作用: 保证没有锁的线程,永久都会被阻挡在同步之外
 *  
 *  JDK5后出现的新特性 Lock接口,实现类ReentrantLock
 *  Lock lock = new ReentrantLock();
 */
public class Demo_02Ticket implements Runnable {
	// 成员位置,定义100张票
	private int tickets = 100;
	Lock lock = new ReentrantLock();
	private Object obj = new Object();

	@Override
	public void run() {
		while (true) {
			//这里用lock锁也是可以的更灵活一点
			//lock.lock();
			// 同步代码块
			synchronized (obj) {
				// 进行售票操作,减法
				if (tickets > 0) {
					try {
						Thread.sleep(10);
					} catch (Exception ex) {
						ex.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() + " 出售第 " + tickets--);
				}
			}
			//lock.unlock();
		}
	}
}

6、死锁的原理和实践

原理:如图所示,2个人分别拥有A\B锁,当2个人分别拿着A\B锁进入第一个同步程序后,由于2人都没有释放锁,所以都进入不了第二个同步程序,就一直等着造成死锁。

多线程调用grpc接口_多线程调用grpc接口


代码实现
1、创建2个锁

public class LockA {
	private LockA(){}
	public static final LockA locka = new LockA();
}
public class LockB {
	private LockB(){}
	public static final LockB lockb = new LockB();
}

2、实现Runnable接口,编写run方法,同步代码块套同步代码块

public class Demo_02DeadRunnable implements Runnable {
	private boolean flag ;
	public Demo_02DeadRunnable(boolean flag){
		this.flag = flag;
	}
	public void run(){
		while(true){
			//对成员变量flag判断,值是true,线程先进入A同步,进去B同步
			if(flag){
				//A同步代码块
				synchronized(LockA.locka){
					System.out.println("if...locka");
					//进去B同步代码块
					synchronized(LockB.lockb){
						System.out.println("if...lockb");
					}
				}
			}else{
				//进去B同步代码块
				synchronized (LockB.lockb) {
					System.out.println("else...lockb");
					//进去A同步代码块
					synchronized (LockA.locka) {
						System.out.println("else...locka");
					}
				}
			}
		}
	}
}

3、测试类,进行测试
可以多执行几次,每次都不同,因为线程是随机分配cup执行权的,可以看到2个线程进入if else第一个同步代码块就造成了死锁,一直等待

public class Demo_01DeadThread {
	public static void main(String[] args) {
		Demo_02DeadRunnable r0 = new Demo_02DeadRunnable(true);
		Demo_02DeadRunnable r1 = new Demo_02DeadRunnable(false);
		
		Thread t0 = new Thread(r0);
		Thread t1 = new Thread(r1);
		
		t0.start();
		t1.start();
	}
}

7、线程池的使用
第一种: submit(Runnable r)方式

/*
 * java.util.concurrent 提供实现线程池的类和接口
 * Executors类,静态方法
 * public static ExecutorService newFixedThreadPool(int nThreads)
 * 创建一个固定个数的线程池对象
 * 返回值,返回的是ExecutorService接口的实现类对象,作用,执行
 * 
 * ExecutorService方法,submit()提交线程任务
 * submit(Runnable r)
 * shutdown 关闭线程池
 */
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo_01ThreadPool {
	public static void main(String[] args) {
		//使用类Executors类,静态方法 newFixedThreadPool创建固定个数的线程池对象
		//返回ExecutorService接口的实现类对象,执行线程
		ExecutorService service = Executors.newFixedThreadPool(2);
		//接口的引用service,方法submit提交线程任务
		service.submit(new Demo_02Runnable());
		service.submit(new Demo_02Runnable());
		service.submit(new Demo_02Runnable());
		service.shutdown();
	}
}
public class Demo_02Runnable implements Runnable{
	public void run(){
		System.out.println(Thread.currentThread().getName()+"  线程池");
	}
}

第二种:submit(Callable task) 方式
好处: 线程执行后,可以具有返回值,可以抛出异常
弊端:Callable接口适用于线程池

/*
 * 实现线程程序的第三种方式,JDK5后,出现的新的特性
 * java.util.concurrent.Callable接口
 * 
 * 好处: 线程执行后,可以具有返回值,可以跑出异常
 * 弊端:Callable接口适用于线程池
 * ExecutorService接口方法
 *  <T> Future<T>  submit(Callable<T> task)  
 *  submit提交线程任务,Callable接口的实现类的对象
 *  获取线程运行后的返回值
 *  
 *  Future 返回Future接口的实现类对象
 *  接口中的方法 get
 */
public class Demo_03Thread {
	public static void main(String[] args)throws Exception {
		ExecutorService service = Executors.newFixedThreadPool(2);
		//提交线程,实现Callable接口实现类对象, submit返回 Future接口的实现类对象
		Future<String> f1 = service.submit(new Demo_04ThreadCallable());
		//调用Future接口的实现类对象方法 get
		String s1 = f1.get();
		System.out.println(s1);
		
		//submit可以提交Runnable接口的实现类,获取返回值,但是, run方法,void
		Future f2 = service.submit(new Runnable(){
			public void run(){}
		});
		
		System.out.println(f2.get());
		service.shutdown();
	}
}
import java.util.concurrent.Callable;
/*
 * 实现java.util.concurrent.Callable接口的类,可以被线程执行,并且还可以返回值
 * 返回String
 */
public class Demo_04ThreadCallable implements Callable<String>{
	public String call(){
		System.out.println(Thread.currentThread().getName()+"实现Callable接口的线程实现");
		return "abc";
	}
}

多线程 的生命周期

多线程调用grpc接口_同步代码块_02


补充:-------------------------------------------------------------------------------------------------------

如何解决多线程数据安全:

1、明确哪些代码是多线程运行代码。
2、明确共享数据。
3、明确多线程运行代码中哪些语句是操作共享数据的。
------------------------------------重要---------------------------------------------

---------------------------等待唤醒机制的使用---------------------------------

线程间通信安全示例:

保证input和output中同步代码块用的是同一个锁,input和output交替执行,要保证input完了开始等待,此时唤醒output,相反output完了开始等待,此时唤醒input执行。

1、创建一个类

定义了同步方法,并利用等待、唤醒机制给成员变量赋值和输出值。

多线程调用grpc接口_同步代码块_03

input部分代码:由于类中定义的是同步方法,所以这里不用同步处理,直接赋值。

多线程调用grpc接口_类对象_04


output代码:由于类中定义的是同步方法,所以这里不用同步处理,直接out方法

多线程调用grpc接口_多线程调用grpc接口_05


main代码

多线程调用grpc接口_System_06


wait()、notify()方法介绍:

多线程调用grpc接口_类对象_07

------------------------------同步方法的使用------------------------------

默认锁使用的是this当前对象,如果是静态方法那么就是当前类.class

多线程调用grpc接口_System_08