java中的多线程详解及代码实现

一. 基本概念及实现方式

基本概念:

1. 进程 进程是独立的应用程序,占用cpu资源和物理内存
2. 线程 线程是进程中虚拟的时间片,所谓的多线程并发实际上就是时间片的轮转或者抢占

第一种实现方式:继承Thread类

// 创建当前类的对象,并且调用run()方法
public class ExtendsThread extends Thread {
	public static void main(String[] args) {
		ExtendsThread t = new ExtendsThread();
		t.start();

	}

	@Override
	public void run() {
		for (int i = 0; i < 1000; i++) {
			System.out.println("你是最棒的你知道么?");
		}
	}

}

第二种实现方式:实现Runnable接口

/**
 * 创建一个该类的对象,创建一个Thread类的对象,并且将当前类的对象作为参数传入
 * 调用start()方法开启线程
 * @author Administrator
 *
 */      
public class ImplRunnable implements Runnable {
	public static void main(String[] args) {
		ImplRunnable ir = new ImplRunnable();
	    Thread t =new Thread(ir);
	    t.start();
	}
   
	@Override
	public void run() {
	    System.out.println("你是最棒的你知道么?");
	}

}

二.线程的锁

第一种分类方式:

1. 乐观锁

乐观锁是一个代码块锁,
乐观锁会给一个版本号,在其余线程想要访问或者修改变量时
会读取版本和原来版本号做对比,一致,就开始修改变量,
否则,就重新读取版本号

2. 悲观锁

悲观锁是一个方法锁,锁住方法入口不让其他线程操作

第二种分类方式:

1. synchronized关键字

//一个简单的synchronized锁
public class SynchronizedDemo {
    public int age () {
    	int myAge = 23;
    	synchronized (this) {
    		return myAge;
		}
    	
    }
}

2. lock接口锁ReentrantLock实现类锁

// 一个简单的lock锁
public class LocDemo {
	ReentrantLock  lock = new ReentrantLock ();
    public int age() {
    	int myAge = 23;
    	lock.lock();
    	myAge++;
    	lock.unlock();
    	return myAge;
    }
}

3.两种锁的对比

1. 最大的区别就是 Synchornized当线程执行完,锁会自动释放掉
   lock锁必须要手动释放,否则就会造成死锁
2. 当有大量线程存在时,lock的效率远远高于synchornized

三. 线程的状态

以下几种:

1. 新建 : 重写run()方法创建线程
2. 就绪 : 调用了start()方法,线程具备了抢占cpu资源的资格
3. 执行 : 线程获得cpu资源,开始执行
4. 阻塞 : 调用了sleep方法,或者wait方法
        其中: sleep状态线程不会释放锁,等待sleep结束线程会继续执行
              wait 状态线程可以释放锁,wait状态必须手动结束
5. 死亡 : 线程执行结束

四.线程池

如果并发线程的数量很多,并且每一个线程都有比较短的生命周期
那么频繁的创建线程和销毁线程就会占用大量的资源,降低系统的效率,
这个时候,就需要用到线程池

1. 首先介绍一下线程池的组成部分:
1.线程池管理器: 用于创建任务并管理线程,并且向线程里面添加任务
2.工作线程    : 在没有任务的时候进行等待,有任务的时候执行任务
3.任务接口    : 任务接口负责把任务交付给线程
4.阻塞队列    : 当线程池里面的任务执行满了,其余的任务放入阻塞队列里进行等待
2. 线程池的核心类 ThreadPoolExecutor

类的实现机制

ThreadPoolExecutor extends AbstractExecutorService implements ExecutorService
implements  Executor
//核心方法
// 顶级接口Executor里面声明的方法,通过该方法向线程池提交任务,交由线程池去执行
1. execute()  
// 在ExecutorService中声明的方法,向线程池提交任务,并且能够返回任务执行的结果
2. submit()
// 后面的两个方法是用来关闭线程池的
3. shutdown()
4. shutdownNow()
3. 线程池与数据库连接池的关系
常用的数据库连接池包括 : dbcp 和 c3p0
// 两者的主要区别
1. c3p0如果因为网络问题断开连接,网络接通会自动重连,但dbcp不会
2. 两者对数据连接的处理方式不同!
   c3p0提供最大空闲时间,而dbcp提供最大连接数
   c3p0超过最大空闲连接时间时,当前线程会被断掉
   而dbcp当连接数超过最大连接数时,所有的线程都会被断掉
用线程池实现连接池
1. 开启线程池,一个线程代表一个数据库连接
2. 当这个连接所有操作均完成或者人为关闭,释放这个线程转为空闲状态
3. 超过数据库最大承载的用户数时,把新的连接放入阻塞队列中
java中几种常用的线程池
1. 有固定数量线程的线程池FixedThreadPool
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(100);
2. 带有缓冲区的线程池 CachedThreadPool
    ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
3. 带有定时调度功能的线程池
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
4. 只有一个线程的线程池
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
定时调度线程池的使用实例
1. 首先创建一个定时调度任务的线程池
// 这里设置线程池线程数量为1个的目的是:
// 在下次定时调度执行时,如果上次的任务还没有执行完,就阻塞,直到上次执行完毕!
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
// 执行定时调度任务,传入参数为需要定时调度的类的对象,多长时间之后开始执行,
// 每隔多久执行一次, 最后的参数指的是按照秒来衡量
executor.scheduleWithFixedDelay(hbaseScheduled, 60, 300, TimeUnit.SECONDS);

对java线程的理解和总结,就先介绍到这里,还属于很基础的层面
等到以后我的理解真正加深了之后再对这部分的内容进行深入和补充!