线程学习笔记

进程与线程

进程

  • 是指一个内存中运行的应用程序,每个进程都一个独立的内存空间

线程

  • 是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行,一个进程最少有一个线程
  • 线程实际上是在进程基础上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分为若干个线程

线程又分为守护线程和用户线程

  • 用户线程:当一个进程不包含任何存活的用户线程时,进程结束
  • 守护线程:守护用户线程的,当最后一个用户线程死亡时,所有守护线程自动死亡,创建线程时调用setDaemon(true)

线程的创建

  • 继承Thread

    public static void main(String[] args){
    	MyThread m=new MyThread();
    	m.start();
    }
    
    public class MyThread extends Thread{
    	/**
    	*run方法就是线程要执行的任务方法
    	*/
    	@Override
    	public void run(){
    		//这里的代码就是一条新的执行路径
    		//这个执行路径的触发方式,不是调用run方法,而是通过thread对象的start()来启动任务
    	}
    }
    
  • 实现Runnable

    public static void main(String[] args){
    	//实现Runnable
    	//1.创建一个任务对象
    	MyRunnable mr=new MyRunnable();
    	//2.创建一个线程,并为其分配一个任务
    	Thread t=new Thread(mr);
    	//3.执行这个线程
    	t.start();
    }
    
    public class MyRunnable implements Runnable{
    	@Override
    	public void run(){
    		//线程的任务
    	}
    }
    
  • 实现Runnable与继承Thread相比有如下优势:

    • 1.通过创建任务,然后给线程分配的方式来实现的多线程,更适合多个线程同时执行相同任务的情况
    • 2.可以避免单继承带来的局限性
    • 3.任务与线程本身是分离的,提高了程序的健壮性
  • 带返回值的Callable

    不常用,了解即可,面试可能会问

    1. 编写类实现Callable接口 , 实现call方法
    class XXX implements Callable<T> {
    	@Override
    	public <T> call() throws Exception {
    		return T;
    	}
    }
    
    2. 创建FutureTask对象 , 并传入第一步编写的Callable类对象
    FutureTask<Integer> future = new FutureTask<>(callable);
    
    3. 通过Thread,启动线程
    new Thread(future).start();
    

    获取返回值

    • Callalble接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。
  • Runnable与Callable比较

    Runnable 与 Callable的相同点

    • 都是接口
    • 都可以编写多线程程序
    • 都采用Thread.start()启动线程

    Runnable 与 Callable的不同点

    • Runnable没有返回值;Callable可以返回执行结果
    • Callable接口的call()允许抛出异常;Runnable的run()不能抛出

保证多线程安全的方式

  • 同步代码块

    synchronized(obj)
    {
        //需要被同步的代码块
    }
    
    public static void main(String[] args)throws InterruptedException{
    	//同步代码块
    	//格式:synchronized(锁对象)
    	Runnablerun=newTicket();
    	newThread(run).start();
    	newThread(run).start();
    	newThread(run).start();
    	newThread(run).start();
    }
    
    static class Ticket implements Runnable{
    	private int count=10;
    	private Object o=new Object();
    	@Override
    	public void run(){
    		while(true){
    			synchronized(o){
    				if(count>0){
    					System.out.println("卖票");
    					try{
    						Thread.sleep(1000);
    					}catch(InterruptedException e){
    						e.printStackTrace();
    					}
    					count--;
    					System.out.println(Thread.currentThread().getName()+"余票:"+count);
    				}else{
    					break;
    				}
    			}
    		}
    	}
    }
    
    
  • 同步方法

    public static void main(String[] args)throws InterruptedException{
    	//同步方法
    	Runnablerun=newTicket();
    	newThread(run).start();
    	newThread(run).start();
    	newThread(run).start();
    	newThread(run).start();
    }
    
    static class Ticket implements Runnable{
    	private int count=10;
    	private Object o=new Object();
    	@Override
    	public void run(){
    		while(true){
    			boolean flag=sale();
    			if(!flag)
    				break;
    		}
    	}
    
    	public synchronized boolean sale(){
    		if(count>0){
    			System.out.println("卖票");
    			try{
    				Thread.sleep(1000);
    			}catch(InterruptedExceptione){
    				e.printStackTrace();
    			}
    			count--;
    			System.out.println(Thread.currentThread().getName()+"余票:"+count);
    			
    			return true;
    		}else{
    			return false;
    		}
    	}
    }
    
  • 显式锁Lock

    创建一个锁对象:Lock l=new ReentrantLock();
    在要想上锁的代码块前一行加:l.lock();
    释放锁时结束的后一行加:l.unlock();

  • 公平锁和非公平锁

    定义:

    • 公平锁是指多个线程按照申请锁的顺序来获取锁
    • 非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,后申请锁的线程有可能先获取锁

    创建一个锁对象时添加参数fair为true,直接修改为公平锁:Lock l=new ReentrantLock(true);

  • 线程死锁

    线程死锁是指两个或两个以上的线程互相持有对方所需要的资源,由于synchronized的特性,一个线程持有一个资源,或者说获得一个锁,在该线程释放这个锁之前,其它线程是获取不到这个锁的,而且会一直死等下去,因此这便造成了死锁

线程池

如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低 系统的效率,因为频繁创建线程和销毁线程需要时间. 线程池就是一个容纳多个线程的容器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。
  • 缓存线程池

    /**
    *缓存线程池:
    *长度无限制
    *任务加入后的执行流程:
    *1.判断线程池是否存在空闲线程
    *2.存在则使用
    *3.不存在,则创建线程并放入线程池,然后使用
    */
    public static void main(String[] args){
    	ExecutorService service=Executors.newCachedThreadPool();
    	//指挥线程池中执行新的任务
    	service.execute(new Runnable(){
    		@Override
    		public void run(){
    			
    		}
    	});
    	service.execute(new Runnable(){
    		@Override
    		public void run(){
    		
    		}
    	});
    	service.execute(new Runnable(){
    		@Override
    		public void run(){
    		
    		}
    	});
    	try{
    		Thread.sleep(1000);
    	}catch(InterruptedException e){
    		e.printStackTrace();
    	}
    	service.execute(new Runnable(){
    		@Override
    		public void run(){
    		
    		}
    	});
    }
    
    
  • 定长线程池

    /**
    *定长线程池:
    *长度是指定的数值
    *任务加入后的执行流程:
    *1.判断线程池是否存在空闲线程
    *2.存在则使用
    *3.不存在空闲线程,且线程池未满的情况下,则创建线程并放入线程池,然后使用
    *4.不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
    */
    public static void main(String[] args){
    	ExecutorService service=Executors.newFixedThreadPool(2);
    	service.execute(new Runnable(){
    		@Override
    		public void run(){
    			System.out.println(Thread.currentThread().getName());
    			try{
    				Thread.sleep(3000);
    			}catch(InterruptedException e){
    				e.printStackTrace();
    			}
    		}
    	});
    	service.execute(newRunnable(){
    		@Override
    		public void run(){
    			System.out.println(Thread.currentThread().getName());
    			try{
    				Thread.sleep(3000);
    			}catch(InterruptedExceptione){
    				e.printStackTrace();
    			}
    		}
    	});
    	service.execute(newRunnable(){
    		@Override
    		public void run(){
    			System.out.println(Thread.currentThread().getName()+"锄禾日当午");
    		}
    	});
    }
    
    
  • 单线程线程池

    /**
    *单线程线程池:
    *执行流程:
    *1.判断线程池的那个线程是否空闲
    *2.空闲则使用
    *3.不空闲,则等待池中的单个线程空闲后使用
    */
    public static void main(String[] args){
    	ExecutorService service=Executors.newSingleThreadExecutor();
    	service.execute(newRunnable(){
    		@Override
    		public void run(){
    			System.out.println(Thread.currentThread().getName());
    		}
    	});
    	
    	service.execute(newRunnable(){
    		@Override
    		public void run(){
    			System.out.println(Thread.currentThread().getName());
    		}
    	});
    }
    
    
  • 周期定长线程池

/**
*周期任务定长线程池:
*执行流程:
*1.判断线程池中是否存在空闲线程
*2.存在则使用
*3.不存在空闲线程,且线程池未满的情况下,则创建线程,并放入线程池,然后使用
*4.不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
*
*周期性任务执行时:
*定时执行,当某个时机触发时,自动执行某任务
*/
public static void main(String[] args){
	ScheduledExecutorService service=Executors.newScheduledThreadPool(2);
	
	/**
	*1.定时执行一次
	*参数1:定时执行的任务
	*参数2:时长数字
	*参数3:时长数字的时间单位,TimeUnit的常量指定
	*/
	/*service.schedule(new Runnable(){
		@Override
		public void run(){
			System.out.println("");
		}
	},5,TimeUnit.SECONDS);*/
	/**
	*周期性执行任务
	*参数1:任务
	*参数2:延迟时长数字(第一次执行在什么时间以后)
	*参数3:周期时长数字(每隔多久执行一次)
	*参数4:时长数字的单位
	*/
	service.scheduleAtFixedRate(new Runnable(){
		@Override
		public void run(){
			System.out.println();
		}
	},5,1,TimeUnit.SECONDS);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值