Java 多线程

什么是线程?线程与进程及程序是什么关系

程序: 一组指令的有序集合,它本身没有任何运行的含义,它只是一个静态的实体。为了实现某个功能,使用某种语言所写的静态指令

进程:指在系统中能独立运行并作为资源分配的基本单位,它是由一组机器指令、数据和堆栈等组成的,是一个能独立运行的活动实体。程序加载至内存中
线程:进程内的一个相对独立的可执行的单元。若把进程称为任务的话,那么线程则是应用中的一个子任务的执行。

经典面试题:
线程和进程的区别是什么?

  1. 进程是操作系统分配资源的最小单元;
  2. 线程是操作系统调度的最小单元;
  3. 一个程序至少有一个进程;一个进程至少有一个线程
  4. 每个进程对应一个JVM实例,多个线程共享JVM里的堆;
  5. 线程不能看做独立应用,而进程可以;
  6. 进程有独立的地址空间,互不影响,而线程只是进程不同的执行路径;
  7. 进程的切换比线程的切换开销大;

如何创建并启动线程?

  1. 继承Thread类重写run()方法,调用start()方法即可启动一个线程
class Thread1 extends Thread{
   @Override
   public void run() {
   	// 书写线程所需要执行的逻辑代码
   }
}

启动代码:

public class ThreadTest {
   public static void main(String[] args) {
   	Thread1 thread1 = new Thread1();
   	thread1.start();
   }
}
  1. 实现Runnable接口
public class RunableTest implements Runnable{
	@Override
	public void run() {
		// 书写线程中所需要执行的逻辑代码
	}
	public static void main(String[] args) {
		new Thread(new RunableTest()).start();
	}
}
  1. 实现Callable接口通过FutureTask包装器来创建Thread线程
public class RunableTest implements Callable<Integer>{

	
	public static void main(String[] args) {
		RunableTest runableTest = new RunableTest();
		FutureTask<Integer> futureTask = new FutureTask<>(runableTest);
		new Thread(futureTask).start();
	}

	@Override
	public Integer call() throws Exception {
		// TODO 书写所需要执行的逻辑代码
		return null;
	}
}
  1. 使用线程池的方式创建
public static void main(String[] args) {
		ExecutorService pool = Executors.newFixedThreadPool(10);
		ThreadPoolExecutor executor = (ThreadPoolExecutor) pool;
		executor.setCorePoolSize(15);
		executor.execute(new Runnable() {
			
			@Override
			public void run() {
				// TODO 书写所需要执行的逻辑代码
				
			}
		});
	}

经典面试题:
实现多线程的方式有几种,他们有什么优劣?
答:在JDK5.0之后拥有四种实现方法:
1. 继承Thread类
- 代码简便易懂
- 但Java当中不支持多继承,继承Thread类意味将抛弃其他类特征的功能与扩展
2. 实现Runable接口
- Java当中支持多实现,实现与继承之间互不影响,可以更好的进行功能扩展
- 代码逻辑略复杂
3. 实现Callable接口通过FutureTask包装器来创建Thread线程
- 可以获得由线程执行任务过后的返回值。
4. 实现使用线程池创建
- 易于管理线程
- 便于对线程资源的合理分配及利用
- 线程执行完成之后不会立即消亡,减少对系统资源的消耗

同步锁
什么情况下需要?
多个线程拥有一个共享资源(就会存在数据安全问题)

实现同步锁的方式

  1. synchronized
    译为:同步
    将多个线程所共享的数据或资源进行同步处理,即线程一进入,在线程一未处理完成之前不允许任何线程进入。
    synchronized使用方法
    synchronized (使用者对象){
    	//	需要同步进行的代码
    }
    // 实例代码
    synchronized (this) {
    }
    
    同时synchronized除了可以使用代码快的方式也可以作用在方法中
    public synchronized test () {
    	// 需要同步进行的代码
    }
    
    注意事项:
    - synchronized 锁住共享资源,要求所有线程使用的为共享资源,而非单个资源
    - 当synchronized 修饰代码快时,它锁住的是this,即当前对象
    - 当synchronized 修饰静态方法时,它锁住的是类,非静态方法时为this,即当前类对象
    - synchronzied使用不当时,会产生死锁情况。
    例如:A线程先使用a资源并上锁,再使用b资源并上锁,结束后释放。B线程先使用b资源并上锁,再使用b资源并上锁,结束后释放。
    当A线程锁住a对象,并未处理完成时,B线程也开始工作,锁住了b资源。
    由于B线程锁住了b资源导致,A无法访问b资源,因此一直在等待B线程释放。
    而A线程锁住了B所需要的a资源,B无法反问a资源,因为一直在等待A线程释放。
    实例代码:
public class RunableTest {
	private static String a = "10";
	private static String b = "20";

	public static void main(String[] args) {

		ExecutorService pool = Executors.newFixedThreadPool(10);
		ThreadPoolExecutor executor = (ThreadPoolExecutor) pool;
		executor.setCorePoolSize(15);
		executor.execute(new Runnable() {

			@Override
			public void run() {
				// TODO Auto-generated method stub
				synchronized (RunableTest.a) {
					a = "lock1";
					System.out.println(a);
					try {
						Thread.sleep(3000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					synchronized (RunableTest.b) {
						System.out.println(b);
					}
				}

			}
		});

		executor.execute(new Runnable() {

			@Override
			public void run() {
				// TODO Auto-generated method stub
				synchronized (RunableTest.b) {
					b = "lock2";
					System.out.println(b);
					try {
						Thread.sleep(3000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					synchronized (RunableTest.a) {
						System.out.println(a);
					}
				}

			}
		});

	}
}

为了避免死锁情况的产生,在使用时候,要考虑清楚
2. Lock
译为:锁
所在包:java.util.concurrent.locks
Lock为接口,即我们要使用它的实现类ReentrantLock,代码如下:
在ReentrantLock当中提供了两个构造器,一个空参构造器,一个有参构造器。
有参构造器传入的值为是否采用公平模式,即先进先出。

		 public ReentrantLock() {
        		sync = new NonfairSync();
  		  }
     	  public ReentrantLock(boolean fair) {
        	sync = fair ? new FairSync() : new NonfairSync();
   		 }

使用Lock代码:

				Lock lock = new ReentrantLock(); 
				lock.lock();// 上锁
				// 需要锁住的代码
				System.out.println("HelloWorld");
				lock.unlock();// 解锁

经典面试题
synchronized与Lock的不同?
不同:

  • Lock需要手动释放锁,而synchronized自动释放锁
  • synchronized是Java当中的关键字,而Lock为接口
  • synchronized在遇到异常时会自动退出并释放锁,而Lock出现异常不会自动释放锁,因此Lock.unlock()解锁操作一般写在finally块当中
    附线程生命周期图及相关方法说明:
    线程生命周期

相关方法说明:
sleep():让线程休息指定毫秒数
start():启动一个线程(注意:启动一个线程并非直接运行,而是等待CPU分配)
join():让XX线程先运行完成之后再执行此线程,可理解为现实生活的插队
yield():让线程暂停
Object.wait(): 让当前线程进入等待状态,注意此方法在Object类中,而非Thread类中
notity()/notityAll():唤醒一个线程或多个线程

以上线程章节学习完结,如有不对之处,敬请指出~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值