第六章-多线程

多线程

线程与进程的区别

​ 进程: 软件在内存中分配的空间 工厂的一个车间,只能单个车间运行

​ 存储在硬盘的文件由cpu加载到内存,形成一个进程

​ 线程:进程的一个执行路径 车间的多个工人

​ 多线程的程序真的是在同时执行吗?不是 底层是一个个在执行单核cpu多线程

​ 多线程提高效率是指提高了CPU的利用率

​ 多线程开启后,只是有了执行资格,但真正的执行要看cpu何时执行

实现多线程的两种方法
继承Thread

​ 继承Thread,重写run方法(线程任务),构建子类对象,调用start方法

实现runable接口*******************

​ 实现runnable接口,重写run方法(线程任务),创建子类对象c

​ 创建Thread对象,将c传入构造方法中,通过Thread对象调用start

​ 线程任务 vs 线程对象

​ run() Thread对象包括Thread及其子类

​ 两种方法的比较:

​ 有的类无法继承thread,可以通过实现runable接口的方式,解决单继承的问题;

​ 把线程任务和线程对象分开了,只用把线程任务创建一次,无需定义静态变量;

​ runable接口比较方便

​ start与run的区别

​ run知识普通方法调用 并没开启新线程

​ star开启新线程,自动调用run方法

​ run与main方法的区别

​ 主线程的任务定义在main方法中

​ 自定义线程的任务定义在run方法中

线程中常用方法

​ Thread.currentThread 获取当前线程 主线程的名字是main

​ getName() setName ()

​ Thread.currentThread.getName t1.getName()

线程阻塞

​ Thread.sleep(2000)毫秒值; 类名调用 sleep在哪个线程中,就让哪个暂停

​ join 对象调用 本线程先执行 阻塞其所在的线程 只控制两个线程

​ Thread.interrupt 叫醒一个被阻塞的线程 会抛出一个异常

public class ppppl {
	public static void main(String[] args) throws InterruptedException {
		mysx mysx = new mysx();//创建子类对象
		mysx.setName("子线程1");
		mysx  as = new mysx();
		as.setName("子线程2");
		as.start();
		as.join();//优先执行我,阻断其所在的线程
		mysx.start(); //开启多线程
		mysx.interrupt();  //如果去掉,会直接打断子线程的阻塞
		System.out.println(Thread.currentThread().getName()); //获取主线程名称  main
		for (int i = 0; i< 50; i++) {
			Thread.currentThread().setName("主线程"); //修改线程名称
			Thread.sleep(3); 
			System.out.println(Thread.currentThread().getName()+"____________________" + i + "次");//获取线程名称
		} 
	}
}
class mysx extends Thread {  //继承Thread
	@Override
	public void run() {  //重写run方法
		try {
			Thread.sleep(5000);//父类没有抛 ,所以子类也不能抛
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
		for (int i = 0; i < 50; i++) {
			try {
				Thread.sleep(2);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() 
					+ "=============================" + i + "次"); // 获取线程的名字
		}
	}
}

解救主线程

public class MapDemo {
	public static void main(String[] args){
		Person p = new Person(Thread.currentThread());
		p.start();
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("over");
	}
}
class Person extends Thread{
	Thread th;
	
	public Person(Thread th) {  //通过构造方法,将主线程传入,进而执行interrupt
		this.th = th;
	}

	public void run(){
		try {
			sleep(40); //保证主线程已经进入sleep状态
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		th.interrupt();
	}
}
线程的生命周期图

线程的整个状态

线程安全问题

​ 锁对象 synchronized :多个线程必须是同一个锁对象 通常用this.getClass

同步代码块 *********

​ 可能会出现死锁,最好不要锁嵌套锁

​ synchronized(锁对象){代码块}

public class MapDemo {
	public static void main(String[] args) {
		ticket tt = new ticket();
		Thread t1 = new Thread(tt);
		Thread t2 = new Thread(tt);
		Thread t3 = new Thread(tt);
		t1.start();
		t2.start();
		t3.start();
	}
}

class ticket implements Runnable {
	static int t = 100;
	public void run() {
		while (true) {
			synchronized (this.getClass()) {
				if (t > 0) {
					try {
						Thread.sleep(30);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() + "正在出售" + t-- + "张票");
				} else {
					break;
				}
			}
		}
	}
}


public class MapDemo {
	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.start();
		t2.start();
		t3.start();
	}
}

class ticket implements Runnable {
	static int t = 100;
	ReentrantLock lock = new ReentrantLock();
	public void run() {
		while (true) {
			lock.lock();
			try {
				if (t > 0) {
					Thread.sleep(30);
					System.out.println(Thread.currentThread().getName() + "正在出售" + t-- + "张票");
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}finally{
				lock.unlock();
			}
		}
	}
}
同步方法 变为单线程

​ 用在单例设计模式中,防止出现线程安全问题

​ 将synchronized放在run方法的修饰符中 public synchronized void 锁的是整个方法,等同于单线程

​ 如果修饰的是成员方法 锁对象默认是this对象 如果使用继承thread就锁不住

加锁 --一般不会出现死锁

​ lock unlock

​ 创建锁对象 Lock lock = new ReentrantLock();

​ 特别注意:先把线程安全问题的代码用try包起来,把lock.unlock()放到finally里中,防止不解锁

wait notify notifyAll

必须写在同步代码块,保证锁对象,其是由锁对象调用,且wait和notify需要用同一锁

public class Patrenaza {
	public static void main(String[] args) {
		Object o = new Object();
		Demo6 demo6 = new Demo6(o);
		demo6.start();
		Demo7 demo7 = new Demo7(o);
		demo7.start();
	}
}

class Demo6 extends Thread {
	Object o;

	public Demo6(Object o) {
		this.o = o;
	}

	public void run() {
		synchronized (o) { // 加锁对象
			System.out.println("wait------start");
			try {
				o.wait(); // 用锁对象调用
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("wait--------end");
		}
	}
}

class Demo7 extends Thread {
	Object o;

	public Demo7(Object o) {
		this.o = o;
	}

	public void run() {
		synchronized (o) { // 用同一把锁
			System.out.println("notiy------start");
			o.notify();
			System.out.println("notify--------end");
		}
	}
}
public class Test4Demo {
	public static void main(String[] args) {
		per p = new per();
		new Producer(p).start();
		new Consumer(p).start();
	}
}
class Producer extends Thread {
	per p;
	public Producer(per p) {
		this.p = p;
	}
	@Override
	public void run() {
		for (int i = 0; i < 100; i++) {
			if (i % 2 == 0) {
				p.set("三", 99);
			} else {
				p.set("111111111111", 99);
			}
		}
	}
}

class Consumer extends Thread {
	per p;

	public Consumer(per p) {
		this.p = p;
	}

	public void run() {
		for (int i = 0; i < 100; i++) {
			p.show();
		}
	}
}

class per {
	private String name;
	private int age;
	boolean flag;
	public per() {
	};
	public synchronized void set(String name, int age) {
		if (!flag) {
			this.name = name;
			this.age = age;
		}
		this.flag = true;
		this.notify();
		try {
			this.wait();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	public synchronized void show() {
		if (flag) {
			System.out.println(this.name + "==" + this.age);
		}
		this.flag = false;
		this.notify();
		try {
			this.wait();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Test3Demo {
	public static void main(String[] args) {
		List<cat> list = new ArrayList<>();
		list.add(new cat("万物", 11));
		list.add(new cat("万", 21));
		list.add(new cat("物", 51));
		Object o = new Object();
		new Thread(new prod(o, list)).start();
		new Thread(new Consu(o, list)).start();
	}
}

class prod implements Runnable {
	private Object o;
	private List<cat> list;

	public prod(Object o, List<cat> list) {
		this.o = o;
		this.list = list;
	}

	@Override
	public void run() {
		while (true) {
			synchronized (o) {
				if (list.size() == 0) {
					Scanner sc = new Scanner(System.in);
					System.out.println("请输入名字");
					String name = sc.next();
					System.out.println("请输入年龄");
					int age = sc.nextInt();
					list.add(new cat(name, age));
				}
				o.notify();
				try {
					o.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
}
class Consu implements Runnable {
	Object o;
	List<cat> list;
	public Consu(Object o, List<cat> list) {
		this.o = o;
		this.list = list;
	}
	@Override
	public void run() {
		while (true) {
			synchronized (o) {
				if (list.size() != 0) {
					System.out.println(list);
					list.remove(0);
				}
				o.notify();
				try {
					o.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
}
class cat {
	String name;
	int age;
	@Override
	public String toString() {
		return "cat [name=" + name + ", age=" + age + "]";
	}
	public cat(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
}

多线程的类,要不就继承Thread 要不就实现Runable接口,都要重写run方法(线程任务) Thread及其子类构建的对象为线程对象

匿名内部类
​ 本质: 实现了接口或者继承了类的子类对象

匿名内部类生成多线程

//开启两个线程,一个打印大写字母表,一个打印小写字母表
public class Jih {
	public static void main(String[] args) {
		new Thread(){
			public void run(){
				for(int i = 'a'; i < 'z'; i++){
					System.out.println(Thread.currentThread().getName()+(char)i);
				}
			}
		}.start();
		new Thread(new Runnable() {
			@Override
			public void run() {
				for(int i = 'A';i<='Z';i++){
					System.out.println(Thread.currentThread().getName()+(char)i);
				}
			}
		}).start();
	}
}
线程池(Executors类)*************

​ 程序每启动一个线程(start),成本是非常高的(与CPU进行交互);

​ 线程池里的线程结束后,不会马上消失,而是进入空闲状态,等待下一个对象使用

​ 后面再用.start时,考虑能否用pool.execute代替 Executors.new

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Patrenaza {
	public static void main(String[] args) {
		ExecutorService pool = Executors.newCachedThreadPool(); // 具有缓存功能的线程池:如果没有线程就开启,有了就用;用完先等待,没人用再删
		pool.execute(new Runnable() { // 实现多线程的第二种方式的匿名内部类 线程对象
			@Override
			public void run() { // 线程方法
				System.out.println("启动一个线程"); // 线程安全问题不可避免,为防止线程安全,需要同步代码块
												// synchronized
			}
		});
		pool.execute(new Thread(new Runnable() { // 匿名内部类
			@Override
			public void run() { // 线程方法
				System.out.println("多线程的第二种方式");
			}
		}));
		pool = Executors.newFixedThreadPool(20); //具有固定线程数的线程池
		pool = Executors.newSingleThreadExecutor();//单线程
	}
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test2Demo {
	public static void main(String[] args) {
		ExecutorService pool = Executors.newCachedThreadPool();
        //线程池后面的参数可以时实现runable的具体对象,thread也行 runable接口对象也行
		pool.execute(new tk()); //两个tk对象,int要加static
		pool.execute(new tk());   
	}
}

class tk implements Runnable {
	static int ticket = 100;//必须要加static  因为用线程池匿名内部类
	public void run() {
		while (true) {
			synchronized (this.getClass()) {
				if (ticket > 0) {
					try {
						Thread.sleep(20);
						System.out.println(Thread.currentThread().getName() + "========" + ticket--);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
}
定时器

​ 使用多线程实现的 Thread.sleep();

​ Timer类 用来执行任务,执行完不会自动关闭

new Timer().schedule (TimerTask task, long delay) TimerTask是个抽象类 重写run方法定义线程任务
new Timer().schedule (TimerTask task, Date time) 如果时间已经超过 就立即执行

new Timer().schedule(TimerTask task, long delay,long period)

new Timer().cancel 放在任务执行语句后面,用于执行完关闭

Date da = new SimpleDateFormat(“yyyy-M-d HH:mm:ss”).parse(“2020-1-8 20:24:20”);

public class Jih {
	public static void main(String[] args) throws ParseException  {
		new Timer().schedule(new TimerTask() {
			@Override
			public void run() {
				System.out.println(new SimpleDateFormat("yyyy-M-d HH:mm:ss").format(new Date()));
			}
		}, new SimpleDateFormat("yyyy-M-d HH:mm:ss").parse("2020-1-8 20:31:30"),1000);
	}
}

用多线程写一个计时器

import java.io.ObjectInputStream.GetField;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimerTask;
import java.util.concurrent.Delayed;

public class Jih {
	static boolean flag = true;
	public static void main(String[] args)   {
			get(new TimerTask() {
				@Override
				public void run() {
					System.out.println(new SimpleDateFormat("yyyy/MM/dd  HH-mm-ss").format(new Date()));
				}
			}, 5000, 2000);
			try {
				Thread.sleep(10000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			cancel();
	}
	public static void cancel(){
		flag = false;
	}
	
	public static void get(TimerTask task ,long delay,long period){
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					Thread.sleep(delay);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				while(flag){
					task.run();
					try {
						Thread.sleep(period);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}).start();
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值