学习地址https://blog.csdn.net/m0_37897828/article/details/77110693
先说多线程可以用来干什么,这是我们要知道。什么场景用
执行效率特别低,耗时特别长都可以考虑多线程。如果耗时操作本身就包含多个task,可以直接提交到线程池去执行 ;如果是很多次的循环,每个任务(task)的界限不是很明确,可以像上面“场景一”中“验证1万条url路径是否存在”的解决思路,人为划分成多个任务(比如把处理200条url作为一个任务),分别提交到线程池去执行。划分任务 --> 提交任务到线程队列
总之特别耗时的,数据量比较大的。各个线程的锁,这个场景就需要多线程之间修改共享数据这类场景就可以用到了。
创建线程的方式:4种
1.继承Thread类
Thread thread=new Thread();
thread.start();
2.实现Runnable接口
MyRunnable myRunnable=new MyRunnable();
Thread thread=new Thread(myRunnable);
thread.start();
3.通过CallableAndFutrue,这是线程带返回值的,首先实现Callable接口,call()其实是作为线程执行体
MyCallable<T> callable=new MyCallable<T>();
FutureTask<Integer> ft = new FutureTask<Integer>(myCallable);
Thread thread=new Thread(ft);
thread.start();
以上可以看https://www.cnblogs.com/lwbqqyumidi/p/3804883.html线程的创建
4.通过线程框架,这是在jdk1.5之后才有的,通过Executors类来创建线程池的多种多样的
//创建线程池,并且线程的固定个数3个
//ExecutorService threadPool = Executors.newFixedThreadPool(3);
//这种类型的线程池特点是:工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger. MAX_VALUE), 这样可灵活的往线程池中添加线程。
//如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。
//在使用CachedThreadPool时,一定要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统瘫痪。
//ExecutorService threadPool = Executors.newCachedThreadPool();
//创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务
ExecutorService threadPool = Executors.newSingleThreadExecutor ();
//创建一个定长线程池,支持定时及周期性任务执行。
//ExecutorService threadPool = Executors.newScheduledThreadPool(corePoolSize, threadFactory);
//利用循环创建了10任务
for(int i=0;i<10;i++){
int task=i;
threadPool.execute(new Runnable() {
@Override
public void run() {
//每个线程打印10遍
for(int j=0;j<10;j++){
try {
//Thread.sleep(200);
System.out.println(Thread.currentThread().getName()+"线程循环次数"+j+"执行任务"+task);
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
}
System.out.println("执行结束");
//线程池将变成shutdown(关闭)状态,此时不接收新任务,但会处理完正在运行的 和 在阻塞队列中等待处理的任务。
threadPool.shutdown();
//线程池将变成stop状态,此时不接收新任务,不再处理在阻塞队列中等待的任务,还会尝试中断正在处理中的工作线程。
//threadPool.shutdownNow();
}
以上可以直接放到main方法中进行测试
对象锁,线程的同步,线程的互斥,在各自的线程间共享变量,死锁,读写锁,无锁会在程序中介绍。
线程常用方法总结:
start();该线程变成就绪阶段,等在cpu来调用
yeid();线程的礼让,会给比本线程高级的线程让路。但是不一定就会让啊,还要看本线程的心情
sleep();这是线程的方法,让该线程等待,让其他线程执行。这个会抱着对象锁
join();让本线程执行完,其他才可以执行。一定是在start()之后。
线程中常用的方法,但是是每个对象都有的方法:
wait();让持有该对象的线程执行等待,会释放掉该对象锁给其他线程用
notify();让持有该对象正在等待的线程,唤醒。
notifyAll();让所有持有该对象正在等待的线程,唤醒。
大家可以看看https://blog.csdn.net/jiangbr/article/details/79337573这里对于线程的方法总结的不错
线程的同步:同步的方式分类4种,一种是同步方法,同步代码块,同步类,同步静态方法
1.同步方法指的是当线程方法进入该方法时,首先要拿到该对象的锁。如果是同一对象,那其他线程只能等待该线程执行完。才可以执行。这就造成了同步
2.同步代码是同步方法的变形,只不过这个思想把其他没用的代码,不影响其他线程的代码抽出来。优化系统性能
3.同步类是指的是当线程方法进入该方法时,首先要拿到该类的锁。那其他线程只能等待该线程执行完。才可以执行。这就造成了同步
4.同步静态方法指的不需要靠对象和类的锁,其他线程只能等待该线程执行完。才可以执行。这就造成了同步。可以和类的同步画等号
同步方式的地址:https://blog.csdn.net/u013142781/article/details/51697672程序在这里
线程的互斥:互斥大家可以理解为例如A线程执行以下,B线程执行一下。但是资源就一个。
生活中的例子好比2个人吃一盘菜,一个叫做张三,一个叫做李四。同步就是张三先吃,等他吃完。李四才可以吃。而互斥就是张三吃一口的同时,李四只能看着。,然后李四在吃一口,张三只能看着。然后这样反复进行。
同步和互斥学习地址:https://blog.csdn.net/alpha_love/article/details/62422789
在各自的线程间共享变量:ThreadLocal<T> threadLocal,T可以存万物类一般是存放各个线程的Session
死锁:资源A、B,进程C、D描述如下:
资源A和资源B,都是不可剥夺资源,
现在进程C已经申请了资源A,进程D也申请了资源B,
进程C接下来的操作需要用到资源B,而进程D恰好也在申请资源A,
进程C、D都得不到接下来的资源,那么就引发了死锁。
这是死锁学习地址:https://www.cnblogs.com/-zyj/p/5683140.html
现在我来说说读写锁:读-读能共存,读-写不能共存,写-写不能共存。应用的经典案例做缓存
学习地址:https://www.cnblogs.com/liuling/archive/2013/08/21/2013-8-21-03.html
无锁学习地址:https://www.cnblogs.com/756623607-zhang/p/6876060.html