多线程简单理解

多线程

一、创建线程的四种方式
创建线程的几种方式:
	1、通过继承Thread类来创建一个线程
	2、通过实现Runnable接口来创建Thread线程,
	3、与方法2类似,通过实现Callable接口来创建Thread线程
	4、使用Executor框架来创建线程池

编写多线程程序是为了实现多任务的并发执行,从而能够更好地与用户交互。一般有四种方法,Thread,Runnable,Callable,使用Executor框架来创建线程池。

Runnable和Callable的区别是,
(1)Callable规定的方法是call(),Runnable规定的方法是run()
(2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值得
(3)call方法可以抛出异常,run方法不可以
(4)运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。

四种方法如下:

①、通过继承Thread类来创建一个线程:

步骤1:定义一个继承Thread类的子类:

	class MyThead extends Thread{
    		public void run(){
     			for(int i = 1; i<=50; i++){
     				System.out.println("MyRunnable :"+ i);
     			}
    		}
	}

步骤2:构造子类的一个对象:

  	MyThread oneThread = new MyThread();

步骤3:启动线程:

  	oneThread.start();

至此,一个线程就创建完成了。

注释:这种创建线程的方法不够好,java只能单继承,现在继承了Thread类,就不能再继承其他类

②、通过实现Runnable接口来创建Thread线程:

步骤1:创建实现Runnable接口的类:

	class MyRunnable implements Runnable
	{
    		public void run()
    		{
      		//do something here
    		}
	}

步骤2:创建一个类对象:

   	Runnable mr = new MyRunnable();

步骤3:创建一个Thread对象:

   	Thread  t1 = new Thread(mr);

步骤4:启动线程:

  	t1.start();

至此,一个线程就创建完成了。

注释:线程的执行流程: 当执行代码t1.start();时,就会执行MyRunnable对象中的void run();方法,该方法执行完成后,线程就消亡了。

③、与方法1类似,通过实现Callable接口来创建Thread线程

其中Callable接口(也只有一个方法)定义如下:

public interface Callable<V>   
{   
	 V call() throws Exception;   
} 

步骤1:创建实现Callable接口的类MyCallable(略);

class MyCallable implements Callable
{
		public void call()
		{
  		//do something here
		}
}

不加泛型:
Callable mc = new MyCallable();
FutureTask oneTask = new FutureTask(mc);
Thread oneThread = new Thread(oneTask);
oneThread.start();

步骤2:创建一个类对象:

Callable<Integer> mc = new MyCallable<Integer>();

步骤3:由Callable创建一个FutureTask对象:

FutureTask<Integer> oneTask  = new FutureTask<Integer>(mc);

注释:FutureTask<Integer>是一个包装器,它通过接受Callable<Integer>来创建,它同时实现了Future和Runnable接口。

步骤4:由FutureTask创建一个Thread对象:

Thread oneThread = new Thread(oneTask);

步骤5:启动线程:

oneThread.start();

至此,一个线程就创建完成了。

④、使用Executor框架来创建线程池

为了更好的控制多线程,JDK提供了一套线程框架Executor,帮助开发人员有效地进行线程控制。它们都在java.util.concurrent包中,是JDK并发包的核心。其中有一个比较重要的类:Executors,它扮演着线程工厂的角色,我们通过Executors可以创建特定功能的线程池。
Executor框架包括:线程池,Executor,Executors,ExecutorService,CompletionService,Future,Callable等。

Executors创建线程池方法:

1、newFixedThreadPool()方法,该方法返回一个固定数量的线程池,该方法的线程数始终不变,当有一个任务提交时,若线程池中空闲,则立即执行,若没有,则会被暂时缓存在一个任务队列中,等待有空闲的线程去执行。

public static ExecutorService newFixedThreadPool(int threads){
	return new ThreadPoolExecutor(threads, threads,
								  OL,TimeUnit.MILLISECONDS,
								  new LinkdedBlockingQueue<Runnable>());
}	

2、newSingleThreadExecutor()方法,创建一个线程的线程池,若空闲则执行,若没有空闲线程则暂缓在任务队列中

public static ExecutorService newSingleThreadExecutor(int threads){
	return new FinalizableDelegatedExecutorService
		(new ThreadPoolExecutor (1, 1,
								  OL,TimeUnit.MILLISECONDS,
								  new LinkdedBlockingQueue<Runnable>()));
}

3、newCachedThreadPool()方法,返回一个可以根据实际情况调整线程个数的线程池,不限制最大线程数量。只要有一个任务来了,就创建一个线程去执行任务。若无任务则不创建线程。并且每一个空闲线程会在60秒后自动回收。

public static ExecutorService newCachedThreadPool(){
	return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
								  60L,TimeUnit.SECONDS,
								  new SynchronousQueue<Runnable>());
}

4、newScheduledThreadPool()方法,该方法返回一个ScheduledExecutorService对象。但该线程池可以指定线程的数量,其中的每一个线程可以实现定时器的作用。

public ScheduledThreadPoolExecutor(int corePoolSize){
	super(corePoolSize, Integer.MAX_VALUE,0, MANOSECONDS,
				new DelayedWorkQueue());
}

上面这些线程池底层都调用了ThreadPoolExecutor,不同类的线程池传递给ThreadPoolExecutor的参数不同

public ThreadPoolExecutor(int corePoolSize,
							int maximumPoolSize,
							long keepAliveTime,
							TimeUnit unit,
							BlockingQueue<Runnable> workQueue){
					this(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,
						Executors.defaultThreadFactory(),defaultHandler);
}
二、线程的五种状态:
1、初始状态(new)(新生状态):Thread T = new Thread();
2、就绪状态(Runnable):T.start();	
3、运行状态(Running):T线程由*就绪状态*(仅这一种)获取到CPU为其分配的时间片,此时才真正执行线程体的代码块
4、死亡状态(Dead):run(),main()结束
5、阻塞状态(Blocked):调用sleep()、wait()或同步锁定时,线程进入阻塞状态,阻塞结束后重新进入*就绪状态*,等待CPU调度

进入就绪状态的四种原因:

1、T.start();
2、阻塞状态结束进入就绪状态
3、T.yield();	自己中断运行状态进入就绪状态,让出CPU时间片等待重新调度,仍然有可能立即被调度,公平竞争,主要用来避免一个线程占用太多CPU时间片
4、JVM将本机线程切换到其他线程

进入阻塞状态的四种原因:

1、T.sleep();	运行状态变成阻塞,自身占有资源不用,进行休眠,每个对象都有一个锁对象,sleep时不释放锁对象		静态方法,直接写在方法体中
2、T.wait();	运行状态进入等待队列,自身不占资源,等待状态							静态方法,直接写在方法体中
3、同步锁定时:	当需要锁对象而没有拿到时
4、T.join(); 	插队,直接进入运行状态,执行完之后再执行其他线程,其他线程会因此阻塞,该方法写在哪个线程中,哪个线程被阻塞

进入死亡状态:

1、线程正常结束运行	
2、T.stop()/destroy();	强行停止(不推荐使用,本身被JDK废弃了)
3、自己控制线程结束:	比如加一个boolean类型的变量,当该变量为false时,终止线程的运行	
三、线程的同步(并发):

处理多线程问题时,多个线程访问同一个对象,并且某些线程还会修改该对象,这时就会用到“线程同步(并发)”。
由于同一个进程的多个线程共享同一资源,因此会产生访问冲突问题,为了保证访问信息数据资源时的正确性,需要加入“锁机制(synchronized)”。
线程同步其实就是一种等待机制+锁机制,多个同时访问此对象的线程进入这个对象的等待池形成“队列”并加上“锁机制”,获得锁的线程使用该对象资源,用完后释放锁,下一个竞争到该锁的线程拥有使用该对象资源的权利

synchronized关键字的两种用法:

1、synchronized方法(同步方法):将一个方法声明为synchronized,对一个方法加锁,如果方法太大,会大大影响效率

	synchronized 返回值类型 方法名称(形参列表0){//对当前对象加锁
		//代码(原子操作)
	}
2、synchronized块(同步代码块):对代码块(原子操作)这个临界资源对象加锁。每个对象都有一个互斥锁标记用来分配给线程		多用6

	synchronized(临界资源对象,比如account){//对临界资源对象加锁,一般该对象就是要修改的对象
		//代码(原子操作)
	}

Lock接口:
比synchronized有更多实用型方法,功能更强大,性能更优越,

void lock(); //获取锁 
boolean tryLock(); //尝试获取锁 
 void unLock();//释放锁

实现类:

	ReentrantLock(可重入锁):如果一个线程试图获取一个已经由他自己持有的锁时,那么这个请求会立刻成功,并将这个锁的计数+1,当线程退出同步代码块时,计数-1,知道为0为止,这时锁会彻底释放,没有可重入锁的话,就会造成死锁,	
	ReentrantLockReadWriteLock(读写锁)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值