- 互斥条件:顾名思义,线程对资源的访问是排他性,当该线程释放资源后下一线程才可进行占用 。
- 请求和保持:简单来说就是自己拿的不放手又等待新的资源到手 。线程T1至少已经保持了一个资源R1占用,但又提出对另一个资源R2请求,而此时,资源R2被其他线程T2占用,于是该线程T1也必须等待,但又对自己保持的资源R1不释放 。
- 不可剥夺:在没有使用完资源时 , 其他线性不能进行剥夺 。
- 循环等待:一直等待对方线程释放资源 。
1.6 补充:并发和并行的区别
并发:是指在某个时间段内,多任务交替的执行任务 。当有多个线程在操作时 , 把CPU运行时间划分成若干个时间段,再将时间段分配给各个线程执行 。在一个时间段的线程代码运行时,其它线程处于挂起状 。
并行:是指同一时刻同时处理多任务的能力 。当有多个线程在操作时,CPU同时处理这些线程请求的能力 。
区别就在于CPU是否能同时处理所有任务,并发不能 , 并行能 。
1.7 补充:线程安全三要素
- 原子性:Atomic包、CAS算法、Synchronized、Lock 。
- 可见性:Synchronized、Volatile(不能保证原子性) 。
- 有序性:Happens-before规则 。
- 互斥同步:Synchronized、Lock 。
- 非阻塞同步:CAS 。
- 无需同步的方案:如果一个方法本来就不涉及共享数据,那它自然就无需任何同步操作去保证正确性 。
- Synchronized关键字
- Lock
- CAS、原子变量
- ThreadLocl:简单来说就是让每个线程,对同一个变量,都有自己的独有副本,每个线程实际访问的对象都是自己的,自然也就不存在线程安全问题了 。
- Volatile
- CopyOnWrite写时复制
文章插图
创建线程的方法
继承Thread类:
public class ThreadCreateTest {
public static void main(String[] args) {
new MyThread.start;
}
}
class MyThread extends Thread {
@Override
public void run {
System.out.println(Thread.currentThread.getName + "\t" + Thread.currentThread.getId);
}
}
实现Runable接口:
public class RunableCreateTest {
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable;
new Thread(runnable).start;
}
}
class MyRunnable implements Runnable {
@Override
public void run {
System.out.println(Thread.currentThread.getName + "\t" + Thread.currentThread.getId);
}
}
通过Callable和Future创建线程:
public class CallableCreateTest {
public static void main(String[] args) throws Exception {
// 将Callable包装成FutureTask,FutureTask也是一种Runnable
MyCallable callable = new MyCallable;
FutureTask<Integer> futureTask = new FutureTask<>(callable);
new Thread(futureTask).start;
// get方法会阻塞调用的线程
Integer sum = futureTask.get;
System.out.println(Thread.currentThread.getName + Thread.currentThread.getId + "=" + sum);
}
}
class MyCallable implements Callable<Integer> {
@Override
public Integer call throws Exception {
System.out.println(Thread.currentThread.getName + "\t" + Thread.currentThread.getId + "\t" + new Date + " \tstarting...");
int sum = 0;
for (int i = 0; i <= 100000; i++) {
sum += i;
}
Thread.sleep(5000);
System.out.println(Thread.currentThread.getName + "\t" + Thread.currentThread.getId + "\t" + new Date + " \tover...");
return sum;
}
}
线程池方式创建:
实现Runnable接口这种方式更受欢迎,因为这不需要继承Thread类 。在应用设计中已经继承了别的对象的情况下 , 这需要多继承(而Java不支持多继承,但可以多实现?。? ,只能实现接口 。同时,线程池也是非常高效的,很容易实现和使用 。
实际开发中 , 阿里巴巴开发插件一直提倡使用线程池创建线程,原因在下方会解释,所以上面的代码我就只简写了一些Demo 。
2.1 线程池创建线程
线程池,顾名思义,线程存放的地方 。和数据库连接池一样,存在的目的就是为了较少系统开销 , 主要由以下几个特点:
推荐阅读
- iphone14技术规格
- 进程和线程:你了解它们的区别吗英语
- 阳光玫瑰葡萄大棚怎么建,阳光玫瑰葡萄种植技术与管理
- 荷兰豆种植 荷兰豆种植技术与管理
- 手机格式化照片用技术手段还会被恢复吗
- 蜜蜂怎么养殖 蜜蜂怎么养殖技术视频
- 物联网应用技术属于什么专业类别
- 现代式短跑技术强调的是什么跑法
- 蛋鸡养殖技术 蛋鸡养殖技术培训
- 物联网应用技术是什么