Java多线程并发(1)

先浅显的写一下Java多线程的知识,之后再添加
#多线程

一、前置知识

1、并发和并行
  并发:两个或多个事件在同一时间段前后发生或交替发生
  并行:两个或多个事件在同一时间点发生

2、进程和线程
  进程:进入内存执行的应用程序
  线程:是进程的一个基本单元,是操作系统运算调度的最小单位

思考一下:单核CPU系统中,多线程的并发还有没有用?
解答:理所当然,在多核CPU的系统中,多线程可以大大提高速度。
但是,在单核系统中,多线程并发的好坏得看情况而言:
*好:
1、在一些情形下,我们有多个程序或功能看起来是同时运行的需求;
2、或者单线程需要运行一会休息一会,而我们的多线程并发可以在本该休息的时候工作,提高了cpu的利用率;
3、在有频繁的IO操作时使用多线程会大大提高程序的执行效率。
*坏:只是提高了CPU的利用率,并没有提高速度

二、多线程并发

1、多线程的内存图示(和单线程相比)

 *假设main方法中有两个线程创建了(其中一个主线程)
 *拿线程调用run()方法和start()方法来进行比较
  1、直接调用run方法,这俩压入一个栈执行,是单线程
  2、调用start方法,操作系统会给线程重新开辟一个栈内存空间,cpu可以选择执行,所以这俩线程就
    是并发执行

2、线程的创建(2种方法)

  1、写Thread的子类,并重写run()方法;创建子类对象,调用start()方法开启线程;
  2、写实现类实现Runnable接口,重写run()方法;创建实现类对象;创建Thraed对象,传入实现类对象,调用start()方法开启线程。
  3、还可以使用这俩的内部类写法
    (1)new Thraed(){重写run()方法}.start();
    (2)new Thread(new Runnable(重写run()方法)).start();
  4、两种方法的优劣:
    java是单继承的关系,子类的方法让该类无法再继承其它类;
    接口的方法还有利于解耦;
    结论: 实现runnable接口的方法好一点

3、线程的六大状态:

  1、new 新建状态:没有启动,还不能被CPU选择运行;
  2、Runnable 运行状态:start启动了,被CPU选择运行
  3、Blocked 阻塞状态:start启动了,可以被CPU选择,但是没有被选中运行;
  4、TIMED_WAITING 计时睡眠状态:调用sleep()方法,进入睡眠状态,睡一段时间后会自己醒来继续
    参与CPU的竞争。 wait(时间参数),等待唤醒,若没被唤醒,时间一到,自己醒来。
  5、WAITING 无限等待状态:调用wait()方法进入无限等待状态,除非notify()唤醒,不然会一直等下

  6、TERMINATED 死亡状态:run()方法执行结束;stop()方法强行结束;或出现异常导致结束;线程进入死亡状态,不会再执行。

4、线程的常用方法:

  1、getName():获取线程名称
  2、setName():设置线程名称
  3、Thread.currentThread().getName(); 获取当前执行线程的名称
  4、sleep():暂停执行,到时间后自动醒来,线程.sleep();
  5、wait():调用方式,锁对象.wait(); 还可以加时间参数,还未被唤醒,时间一到也会自己醒来
  6、notify():唤醒其所在锁所阻塞的单个线程,锁对象.notify();
  7、notifyAll():唤醒所在监视器上所有等待的线程

5、线程的安全问题

  1、产生原因:cpu执行多线程时,执行的过程中随时有可能切换到其它线程执行。

  2、解决办法:将使用共同数据(可能造成安全问题的代码锁起来)
    (1)同步代码块
    (2)同步方法(静态同步方法也可以)
    (3)lock锁机制

	例题:两个窗口同时售卖十张票。
		主方法:
		`	int num = 10;
    		RunnableImpl r1 = new RunnableImpl(num);
    		Thread t1 = new Thread(r1);
    		Thread t2 = new Thread(r1);
    		t1.start();
    		t2.start();`
		
		Runnable接口的实现类:
		`	public class RunnableImpl implements Runnable {

		    private static int num = 0;
		
		    Object o = new Object();
		
		    Lock l = new ReentrantLock();
		
		    @Override
		    public void run() {
		        while (true) {
		            //1、同步代码块
		            synchronized (o) {
		                if (num > 0) {
		                    System.out.println(Thread.currentThread().getName() + "窗口顾客正在支付第" + num + "张票");
		                    try {
		                        Thread.sleep(2000);//触发操作系统立刻重新进行一次CPU竞争
		                    } catch (InterruptedException e) {
		                        e.printStackTrace();
		                    }
		                    num--;
		                    System.out.println(Thread.currentThread().getName() + "窗口顾客支付第" + (num + 1) + "张票成功");
		
		                }
		            }
		
		            pay();
		            pay2();
		            pay3();
		        }
		
		    }
		    public RunnableImpl(int num) {
		        this.num = num;
		    }
		
		    //2、同步方法:有个默认的锁对象,就是我们的实现类对象,也就是this
		    public synchronized void pay(){
		        if (num > 0) {
		            System.out.println(Thread.currentThread().getName() + "窗口顾客正在支付第" + num + "张票");
		            try {
		
		                Thread.sleep(2000);//触发操作系统立刻重新进行一次CPU竞争
		             } catch (InterruptedException e) {
		                e.printStackTrace();
		            }
		            num--;
		            System.out.println(Thread.currentThread().getName() + "窗口顾客支付第" + (num + 1) + "张票成功");
		    }
		    }
		
		    //3、静态同步方法也可以,因为静态修饰的东西优先进入内存,所以不能在用new出来的实现类对象当做锁对象。
		    // 这里使用实现类的class对象当做锁对象,比如本例:RunnableImpl.class 对象
		    public static synchronized void pay2(){
		        if (num > 0) {
		            System.out.println(Thread.currentThread().getName() + "窗口顾客正在支付第" + num + "张票");
		            try {
		                Thread.sleep(2000);//触发操作系统立刻重新进行一次CPU竞争
		            } catch (InterruptedException e) {
		                e.printStackTrace();
		            }
		            num--;
		            System.out.println(Thread.currentThread().getName() + "窗口顾客支付第" + (num + 1) + "张票成功");
		        }
		    }
		
		    //4、lock锁
		    public void pay3(){
		        l.lock();
		        if (num > 0) {
		
		            try {
		                System.out.println(Thread.currentThread().getName() + "窗口顾客正在支付第" + num + "张票");
		                Thread.sleep(2000);//触发操作系统立刻重新进行一次CPU竞争
		                num--;
		                System.out.println(Thread.currentThread().getName() + "窗口顾客支付第" + (num + 1) + "张票成功");
		            } catch (InterruptedException e) {
		                e.printStackTrace();
		            }finally { //不论异常是否发生,总是释放锁资源
		                l.unlock();
		            }
		
		        }
		
		    }
		}`

6、简单的等待唤醒案例

 *描述:
	客户线程:客户给老板说要买包子,然后进入无限等待状态
	老板线程:老板知道客户要买包子,做包子,唤醒客户线程,让客户吃包子

	`
	Object o = new Object(); //共同使用的锁对象

	//客户线程
    new Thread(){
        @Override
        public void run() {
            synchronized (o){
                System.out.println("老板,来2个包子");
                try {
                    o.wait(); //进入无限等待状态
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("吃完了");
            }
        }
    }.start();

	//老板线程
    new Thread(){
        @Override
        public void run() {
            synchronized (o){
                System.out.println("请稍等");
                try {
                    Thread.sleep(2000); //睡眠状态做包子
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("做好了");
                o.notify(); //唤醒锁对象o正在阻塞的线程
            }
        }
    }.start();
	'

线程池

  池子就是容器,可以用集合来存放
  1、当程序第一次启动时,创建多个线程,保存到一个集合中。方便取用,省去了频繁创建的过程
  2、使用list集合存放。remove()方法返回线程名字,且保证了一个线程只能被一个任务使用
  3、使用完毕后,归还线程给线程池,add()方法归还。
  4、jdk1.5之后,有内置线程池

内置线程池 
1、java.util.concurrent.Executors类
		static ExecutorService newCachedThreadPool() 
      	创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。 
2、java.util.concurrent 接口 ExecutorService
		(1)Future<?> submit(Runnable task) 
    		提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。 
		(2)void shutdown()  销毁线程池

3、步骤:
	(1)创建线程池对象,参数为线程数量(返回值是ExecutorService接口对象)
		ExecutorService es = Executors.newFixedThreadPool(5);

	(2)创建Runnable接口的实现类,重写run()方法
		public class RunnableImpl implements Runnable{
		    @Override
		    public void run() {
		        System.out.println(Thread.currentThread().getName());
		    }
		}

	(3)调用ExecutorService接口的submit()方法,拿出线程,传递实现类对象。
		es.submit(new RunnableImpl());

	(4)线程使用完毕,会被自动归还给线程池
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值