基本概念
1、程序:一些列有组织的文件,封装操作系统的各种API,实现不同的效果
2、进程:程序在系统中的一次执行过程,是拥有资源的最小单位,不同进程之间相互独立
3、IP地址:唯一在网络上标识一台主机的位置
3、端口号:操作系统上定位一个进程,且一般不变
4、线程:同一个进程下共享资源,任务执行(系统调度)的基本单位
5、JDK中提供的线程库实际上利用OS提供的线程库进行二次封装。故JDK中唯一的进程不一定是OS中唯一的进程,可能好几个进程
创建线程
java.long.Thread
【Java一切皆对象–线程对象–Thread类】
【Thread.activeCount()目前只在执行的线程数量,要-1出去后台进程】
1、多线程的创建
①继承Thread类覆写run方法
产生线程对象
public class Test1 {
private static class MyThread extends Thread{
@Override
public void run() {//JVM调用run方法
System.out.println(Thread.currentThread().getName());
}
}
public static void main(String[] args) {
MyThread m1 = new MyThread();
MyThread m2 = new MyThread();
MyThread m3 = new MyThread();
m1.start();//启动线程
m2.start();
m3.start();
System.out.println(Thread.currentThread().getName());
}
}
②实现Runnable接口,覆写run方法
产生任务对象
public class Test2 {
public static void main(String[] args) {
Thread t1 = new Thread(new MyThread());
Thread t2 = new Thread(new MyThread());
Thread t3 = new Thread(new MyThread());
t1.start();
t2.start();
t3.start();
System.out.println(Thread.currentThread().getName());
}
}
class MyThread implements Runnable {//不是线程实体,是线程任务
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
推荐使用②,实现接口更加灵活,子类还能实现别的接口,继承别的类
方式①只能继承Thread类,造成单继承局限
【使用匿名内部类和lambda表达式创建线程】
public class Test3 {
public static void main(String[] args) {
Thread t1 = new Thread("继承Thread类") {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
};
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}, "实现Runnable接口");
Thread t3 = new Thread(() -> {
System.out.println(Thread.currentThread().getName());
}, "使用lambda表达式实现Runnable接口");
t1.start();
t2.start();
t3.start();
System.out.println(Thread.currentThread().getName());
}
}
③实现callable接口,覆写call方法
④使用线程池创建线程
【多线程应用场景】
把一个大任务拆分为多个子任务,多个子线程并发执行,提高系统的处理效率
Thread类
1、构造方法
public class ThreadName {
public static void main(String[] args) {
Thread t1 =new Thread();
Thread t2 =new Thread(new Runnable() {
@Override
public void run() {
}
});
Thread t3 = new Thread("小丑线程");
Thread t4 = new Thread(new Runnable() {//常用
@Override
public void run() {
}
},"蝙蝠侠线程");
}
}
2、核心属性
3、线程中断–通信方式
【中断一个正在执行的run方法还没结束的线程】
①线程中断的两种方式
1)通过共享变量进行中断
/**
* 通过共享变量中断线程
*/
private static class MyThread extends Thread {
volatile boolean isQuit = false;//此关键字保证可见性
@Override
public void run() {
while (!isQuit) {
System.out.println("进行中");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "被中断了");
}
}
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread();
System.out.println("子线程开始");
myThread.start();
Thread.sleep(1000);
myThread.isQuit = true;
System.out.println("停止子线程");
}
2)使用 Thread.interrupted()静态方法 或者 Thread对象的成员方法isInterrupted()
public class Test5 {
private static class MyThread implements Runnable {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {//成员方法
//while (!Thread.isInterrupted()) { ---静态方法
System.out.println(Thread.currentThread().getName() + "正在进行");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// throw new RuntimeException(e);//被中断后抛出中断异常
//抛出异常之后,中断状态会被清除
System.out.println("被中断了");
break;
}
}
System.out.println(Thread.currentThread().getName() + "被中断了");
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new MyThread(),"小丑");
System.out.println("子线程开始");
t1.start();
Thread.sleep(5000);
System.out.println("该停止了");
t1.interrupt();
}
}
【Thread.isInterrupted()与Thread.currentThread().isInterrupted()之间的区别】
public class Test6 {
private static class MyThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.interrupted());//清除中断标志
/**
* 运行结果
* true
* false
* false
* false
* false
* false
* false
* false
* false
* false
*/
}
}
}
private static class MyThread1 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().isInterrupted());//不会清除中断标志,判断而已
/**
* true
* true
* true
* true
* true
*/
}
}
}
public static void main(String[] args) {
// Thread t1 = new Thread(new MyThread());
// t1.start();
// t1.interrupt();
Thread t2 =new Thread(new MyThread1());
t2.start();
t2.interrupt();//修改指定线程的状态为中断状态
}
}
【修改指定线程的状态为中断状态—调用线程interrupt方法】
【Thread类的静态方法和成员方法isInterrupt作判断】
4、等待
join()方法,在哪调用在哪等待(阻塞状态)
【也是进程之间通信的一种方式】
5、休眠
sleep()
【sleep和yield方法不会百分百刷新工作内存读取主存】
6、线程状态
创建态:new
就绪态:ready
运行态:running(就绪和运行都是RUNNABLE)
终止态:terminated
阻塞态:(TIMED_WAITING BLOCKED WAITING)
7、线程状态转换
notify() 唤醒
yiele()方法 让出CPU,从运行态进入就绪态等待CPU调度–OS调度程序员无法选择
【使用不多无法控制】
【一个线程创建后只能启动一次】
callable接口
【submit()可以接收Rannable或者callable接口】
public class Callable_Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<Integer> callable = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i <= 100; i++) {
sum += i;
}
return sum;
}
};
//接收call方法返回值使用FutureTask类
FutureTask<Integer> futureTask = new FutureTask<>(callable);
//启动callable接口还是得使用thread类的start方法,且必须通过FutureTask类
Thread t1 = new Thread(futureTask);
t1.start();
int result = futureTask.get();//使用get方法会阻塞,直到call方法执行完毕
System.out.println(result);
}
}
线程安全问题
** 1、什么是线程不安全?**
A:多个线程串行和并发执行的结果不同,称为线程不安全
2、保证线程安全三个特性
A:原子性 -------------------一个操作一次执行完不会被中断
可见性 -----------------------一个线程对共享变量的修改,能够及时被其他线程看到
防止指令重排 ---------------代码的书写顺序不一定是最终JVM或者CPU的执行顺序
Java的内存模型
【描述多线程场景下,Java线程内存(高速缓存和寄存器)和主内存之间的关系】
synchronized关键字—线程上锁–锁的是对象
【满足可见性和原子性】
【在变量或者方法上使用synchronized】
【监视器锁–monitor lock(对象锁)】
锁
【锁的是对象】
特性:
1、互斥----mutex lock 某个线程获取到该对象的锁时,其他线程也要获取同一个对象的锁时,处于阻塞等待状态
2、synchronized代码块刷新内存
3、可重入
【Java中的线程安全锁都是可重入的,包括java.concurrent.lock】
【获取到对象锁的线程可以再次加锁】
【若不支持可能会造成死锁】
【当synchronized锁的是静态方法,相当于将此方法的类的所有对象都锁了(锁的是类名.class对象 全局唯一)】
【锁的优化都是在优化锁的力度】
juc下的常见子类
对象锁Lock
public class Lock_Test {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(() -> {
lock.lock();//加锁
System.out.println("加锁成功-----");
try {
System.out.println(Thread.currentThread().getName() + "获取到锁,其他线程等待");
Thread.sleep(8000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();//解锁
}
System.out.println("解锁成功-----");
}, "小丑");
t1.start();
Thread t2 = new Thread(() -> {
boolean isLocked = false;
try {
isLocked = lock.tryLock(3000, TimeUnit.MICROSECONDS);//代表千分之一毫秒的时间单位
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (isLocked) {
lock.unlock();
}
}
System.out.println(Thread.currentThread().getName() + "不找你了,回去吃饭!");
}, "蝙蝠侠");
t2.start();
}
}
lock与synchronized区别
死锁
【锁对象的循环等待问题】
产生死锁的原因
如何避免死锁
【可以使用jconsole检测死锁】
【破坏循环等待条件】
concurrent类
信号量Semaphore
【p-- 申请资源操作】【acquire()】
【v–释放资源操作】【release()】
【pv操作都是原子性的】
【每次申请和释放都是相同资源数】
public static void main(String[] args) {
//构造参数传入可用资源的个数
Semaphore semaphore = new Semaphore(5);
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + "准备申请资源");
//P操作申请一个资源,无参默认
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "获取成功");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "释放资源");
//v操作,释放一个占有的资源
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}
计数器CountDownLatch–大号join
【countDown() —计数器-1】
【await() 等待计数器为0】
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(10);//10表示等待数量
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
Thread.sleep(new Random().nextInt(1000));
System.out.println(Thread.currentThread().getName() + "到达");
latch.countDown();//当一个进程到达,计数器减一
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
for (int i = 0; i < 10; i++) {
Thread t = new Thread(runnable, "运动员" + i + 1);
t.start();
}
//main宪政就是裁判线程,需要等待所有运动员到达重点再恢复执行
//当countDown方法将计数器减为0则继续执行
latch.await();
System.out.println("所有运动员均已到位");
}
循环栅栏 CyclicBarrier–自己了解补充
两个线程交换器 Exchanger–自己了解补充
线程安全类
volatile关键字
【只能保证可见性,不能保证原子性】
【内存屏障,防止指令重排】
等待与唤醒
【wait和notify是Object类的方法,用于线程的等待与唤醒,必须搭配synchronized锁使用】
等待方法
唤醒方法