1 . 线程的创建方式
常见创建线程的4四种方式:
- 继承Thread类,重写run()方法,java单一继承原则,类继承Thread后无法再继承其它类,使用时需注意;
public static class Ta extends Thread{
@Override
public void run() {
}
}
- 实现Runable接口,实现run()方法,可以实现多个接口;
public static class Tb implements Runnable {
@Override
public void run() {
}
}
- 实现Callable接口,重写call()方法,实现Callable接口来创建的线程,可以通过FutureTask类接收线程的处理结果;
public static class Tc implements Callable<Integer> {
@Override
public Integer call() throws Exception {
}
}
- 线程池(实际项目使用推荐使用该方式);
常用线程池(4种):
1 . newCachedThreadPool:可缓存线程池,线程池最大容量为Interger.MAX_VALUE,可线程空闲超时可自动回收,使用时要注意控制任务的数量,否则,由于大量线程同时运行会造成系统瘫痪。
2 . newFixedThreadPool:指定工作线程数量的线程池,每当任务一提交就会创建一个工作线程,知道线程数达到线程池初始最大数。创建的线程不会自动销毁,但是可以提高效率和节省线程创建所耗开销,线程空闲会占用一定的系统资源。
3 . newSingleThreadExecutor:创建一个单线程化的Executor,只创建唯一的工作者线程来执行任务,可保证任务按顺序执行。
4 . newScheduleThreadPool:创建一个定长的线程池,而且支持定时的以及周期的任务执行。
注意:创建线程池不要使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险.。因为Executors 为默认创建
1)创建FixedThreadPool 和 SingleThreadPool的允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致内存资源不足;
2)创建CachedThreadPool 和 ScheduledThreadPool的允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致内存资源不足;
2. 线程的使用
当web层接收到一个请求时,就会去线程池拿一个空闲线程使用;
这个线程就成为了就绪状态,等待cpu去调用,CPU执行线程是可以在任何时候让
改线程记住当状态停止任务回到就绪状态,等待cpu再次调用,以此往复直到这个
线程的任务完成。
1 .线程加锁问题
转账业务,账户A,一个线程向A中存钱,一个线程取钱;
如果在没有对用户A加锁时,存钱取钱都是对同一个余额操作,导致结果时存完余额为
1000+300,取钱操作完后更新余额为1000-300,最后结果为700;
加锁处理流程为,存钱线程执行时,取钱线程无法获取用户A,只有等待存钱完后才能
获取用户A,因此取钱执行结果是1300-300=1000。
2 .多个资源加锁导致死锁问题
现象:有A和B两个需要加锁资源,1号线程执行A给B转账,2号线程执行B给A转账;
1线程获取A的锁,2线程获取B的锁,cpu再次执行1号线程时会再去获取B的锁这是发现
锁被使用就会等带锁B释放,同样2号线程获取A线程也会一直等待,这个时候就出现了死锁,
导致cpu利用率飙升;
解决方法:
1.顺序加锁,在1号,2号线程获取A和B锁的顺序要指定一个算法顺序,例如每次获取都是
获取资源大的锁(if A>B return A);
2.加锁时限,当线程尝试获取锁时加上一个超时时限,超过时限就放弃该锁,并释放已经
获取的锁,然后等待一段时间在重试;
3.死锁检测机制,主要针对那些不可能按顺序加锁且加锁超时不可行的场景。
3.线程并发问题解决:两个线程分别打印出二十六个字母中的元音字母和其它字母,要求按字母顺序打印;
问题场景描述:多个线程对统一个数据集做不同的任务处理,设计数据集资源的并发控制。
public class ThreadDemo{
//共计26字母
public static String[] words = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"};
//记录当前检索的字段下标 (用volatile 关键字修饰实现线程间的变量数据是通步的)
public static volatile int wordsIndex = 0;
//实现公平锁,依次交替打印26字母
public static ReentrantLock lock = new ReentrantLock(true);
public static void main(String[] args) throws InterruptedException {
//创建线程One
ThreadOne threadOne = new ThreadOne ();
Thread threadA = new Thread(threadOne );
//创建线程Two
ThreadTwo threadTwo = new ThreadTwo ();
Thread threadB = new Thread(threadTwo );
// 启动任务
threadA.start();
threadB.start();
}
public static class ThreadOne implements Runnable {
@Override
public void run() {
// 打印元音字母
wordsIndex = 0;
while (true){
//阻塞100ms,避免其它线程还未运行
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
e.printStackTrace();
}
//获取任务锁
lock.lock();
try {
//处理字典中数据
for (;wordsIndex<words.length;wordsIndex++){
//判断是否为元音
if(isYuanWords(words[wordsIndex])){
//打印元音字母
System.out.println(" I am Ta Thread print YuanWord is *********** : "+ words[wordsIndex]);
}else {
//结束本次,等待下次获取任务
System.out.println("我是线程TA:释放锁" + wordsIndex);
break;
}
}
}catch (Exception e){
e.printStackTrace();
}finally {
//释放任务锁,让其它线程处理数据
lock.unlock();
}
}
}
}
public static class ThreadTwo implements Runnable {
@Override
public void run() {
wordsIndex = 0;
while (true) {
//阻塞100ms,避免其它线程还未运行
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
e.printStackTrace();
}
//获取任务锁
lock.lock();
try{
//处理字典中数据
for (; wordsIndex < words.length; wordsIndex++) {
//判断是否为元音
if (!isYuanWords(words[wordsIndex])) {
//打印非元音字母
System.out.println(" I am Tb Thread print OtherWord is : " + words[wordsIndex]);
} else {
//结束本次,等待下次获取任务
System.out.println("我是线程TB:释放锁" + wordsIndex);
break;
}
}
}catch (Exception e){
e.printStackTrace();
}finally {
//释放任务锁,让其它线程处理数据
lock.unlock();
}
}
}
}
//判断是否为元音字母
public static boolean isYuanWords(String word){
//a, e, i, o, u
if ("a".equals(word)) {
return true;
}else if ("e".equals(word)) {
return true;
}else if ("i".equals(word)) {
return true;
}else if ("o".equals(word)) {
return true;
}else if ("u".equals(word)) {
return true;
}else {
return false;
}
}
}