一 简介
程序:指令和数据的有序集合,本身没有任何运行的含义,是一个静态的概念。
进程:在操作系统中运行的程序就是进程(QQ),是执行程序的一次执行过程,是一个动态的概念。(是系统分配资源的单位)
线程:在一个进程中包含若干个线程,一个进程中至少有一个线程,不然没有存在的意义。(是cpu调度和执行的单位)
PS:多线程:是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器;若是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能执行一个代码,因为切换的很块,所以就有同时执行的错觉。
二 线程实现(重点)
三种创建方式:
1.继承Thread:
//创建线程方式一:
//总结:
// 注意线程开启不一定立即执行,由cpu同时执行。
// 启动线程:子类对象.start()
/*
继承Thread类;
重写run()方法;
调用start开启线程。*/
public class TestThread1 extends Thread{
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 20; i++) {
System.out.println("我在看代码"+i);
}
}
public static void main(String[] args) {
//main主线程
//创建一个多线程
TestThread1 testThread1 = new TestThread1();
//调用start()方法开启线程
testThread1.start();
for (int i = 0; i < 20; i++) {
System.out.println("我在学习多线程"+i);
}
}
}
2.实现Runnable类
//创建线程方式二:推荐使用
//总结:
// 启动线程:传入目标对象+Thread对象.start()
/*
实现Runnable类;
重写run()方法;
执行线程需要丢入runnable接口实现类,调用start方法*/
public class TestThread2 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("我在看代码--"+i);
}
}
public static void main(String[] args) {
TestThread2 testThread2 = new TestThread2();
Thread thread = new Thread(testThread2);
thread.start();
for (int i = 0; i < 20; i++) {
System.out.println("我在学习多线程---"+i);
}
}
}
3.实现Callable接口,需要返回值类型
//创建线程方式三:
//总结:
// 可以定义返回值;
// 可以抛出异常
/*
* 实现Callable接口,需要返回值类型
* 重写call方法,需要抛出异常
* 创建目标对象
* 创建执行服务
* 提交执行
* 获取结果
* 关闭服务*/
public class callable implements Callable<Boolean> {
@Override
public Boolean call(){
for (int i = 0; i < 20; i++) {
System.out.println("我在看代码--" + i);
}
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable t1 = new callable();
//创建执行服务
ExecutorService service = Executors.newFixedThreadPool(1);
//提交执行
Future<Boolean> result1=service.submit(t1);
//获取结果
Boolean r1 = result1.get();
System.out.println(r1);
//关闭服务
service.shutdownNow();
}
}
三 线程状态
1.线程休眠:sleep方法
Thread.sleep使当前线程在指定时间段内暂停执行。这是使处理器时间可用于应用程序的其他线程或计算机系统上
可能正在运行的其他应用程序的有效方法。
2.线程优先级:setPriority方法
线程优先级的范围是1~10,默认的优先级是5。10极最高。
“高优先级线程”被分配CPU的概率高于“低优先级线程”。
3.线程礼让:yield方法
Thread类中提供了一种礼让方法,使用yield()方法表示,它只是给当前正处于运行状态下的线程一个提醒,
告知它可以将资源礼让给其他线程,但这仅仅是一种暗示,没有任何一种机制保证当前线程会将资源礼让。
yield()方法使具有同样优先级的线程有进入可执行状态的机会,当当前线程放弃执行权时会再度回到就绪状态。
4.线程联合:join方法
join方法允许一个线程等待另一线程的完成。如果t是Thread正在执行其线程的对象,
t.join();导致当前线程暂停执行,直到t的线程终止。重载join允许程序员指定等待时间。但是,与一样sleep,
join依赖于OS进行计时,因此不应该假定join它将完全按照您指定的时间等待。
5.线程停止:使用退出标志
停止一个线程通常意味着在线程处理任务完成之前停掉正在做的操作,也就是放弃当前的操作。
在 Java 中有以下 3 种方法可以终止正在运行的线程:
1.使用退出标识,使得线程正常退出,即当run方法完成后进程终止。
2.使用stop强行中断线程(此方法为作废过期方法),不推荐使用。
3.使用interrupt方法中断线程,不推荐使用
6.守护线程
Java线程分为两种:用户线程和守护线程。
用户线程:可以认为是系统的工作线程,它会完成这个程序要完成的业务员操作。
守护线程:是一种特殊的线程,就和它的名字一样,它是系统的守护者,在后台默默完成一些系统性的服务,比如垃圾回收线程。
如果用户线程全部结束,则意味着这个程序无事可做。守护线程要守护的对象已经不存在了,那么整个应用程序就结束了。
public class ThreadDaemon {
public static void main(String[] args) {
Daemon daemon = new Daemon();
Thread thread1 = new Thread(daemon);
thread1.setDaemon(true);
thread1.start();
Me me = new Me();
Thread thread2 = new Thread(me);
thread2.start();
}
}
class Me implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("我的努力奔跑" + i);
}
}
}
class Daemon implements Runnable {
@Override
public void run() {
int i = 0;
while (true) {
System.out.println("我的守护你" + i++);
}
}
}
四 线程同步(重点)
1.线程冲突
当在不同线程中运行作用于相同数据的两个操作时,就会发生干扰。这意味着这两个操作由多个步骤组成,并且步骤顺序重叠。
1. 检索的当前值c。
2. 将检索到的值增加1。
3. 将增加的值存储回c。
2.同步语句:synchronized(加在增删改的地方)
Java编程语言提供了两种基本的同步习惯用法:同步语句(synchronized statements )和同步方法(synchronized methods )。
创建同步代码的一种方法是使用同步语句。与同步方法不同,同步语句必须指定提供内部锁的对象。
同步语句对于通过细粒度同步提高并发性很有用。
public class SynchronizedStatements implements Runnable {
public static int ticket = 100;
@Override
public void run() {
while (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized ("") {
if (ticket <= 0) {
return;
}
System.out.println(Thread.currentThread().getName()
+ "卖了1张票,剩余" + --ticket + "张票");
}
}
}
3.同步方法
要使同步方法,只需将synchronized关键字添加到其声明中。
同步方法提供了一种防止线程干扰和内存一致性错误的简单策略:如果一个对象对一个以上线程可见,
则对该对象变量的所有读取或写入均通过synchronized方法完成。
public synchronized void sellTicket() {
while (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "卖了1张票,剩余" + --ticket + "张票");
}
}
4.线程死锁
死锁是一种特定的程序状态,在实体之间,由于循环依赖导致彼此一直处于等待之中,没有任何个体可以继续前进。
死锁不仅仅在线程之间会发生,存在资源独占的进程之间同样也可能出现死锁。通常来说,我们大多是聚焦在多线程场景中的死锁,
指两个或多个线程之间,由于互相持有对方需要的锁,互相等待,而永久处于阻塞状态。
五 线程通信问题
1.线程协调:wait和notify方法
死锁发生时的条件:
(1)互斥条件:一个资源每次只能被一个进程使用。
(2)请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3)不剥夺条件: 进程已获得的资源,在未使用完之前,不能强行剥夺。
(4)循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
public class ThreadCoordinate {
public static void main(String[] args) {
CoordinateA coordinateA = new CoordinateA();
Thread threadA = new Thread(coordinateA, "线程1");
CoordinateB coordinateB = new CoordinateB();
Thread threadB = new Thread(coordinateB, "线程2");
threadA.start();
threadB.start();
}
}
class CoordinateA implements Runnable {
@Override
public void run() {
synchronized ("A") {
System.out.println(Thread.currentThread().getName()
+ "持有了A锁,等待B锁。。。");
try {
"A".wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized ("B") {
System.out.println(Thread.currentThread().getName()
+ "持有了A锁和B锁");
}
}
}
}
class CoordinateB implements Runnable {
@Override
public void run() {
synchronized ("B") {
System.out.println(Thread.currentThread().getName()
+ "持有了B锁,等待A锁。。。");
synchronized ("A") {
System.out.println(Thread.currentThread().getName()
+ "持有了B锁和A锁");
"A".notify();
}
}
}
}
六 高级主题
1.线程池
Java中创建和销毁一个线程是比较昂贵的操作,需要系统调用。频繁创建和销毁线程会影响系统性能。
使用线程池带来以下好处:
降低资源的消耗。线程本身是一种资源,创建和销毁线程会有CPU开销;创建的线程也会占用一定的内存。
提高任务执行的响应速度。任务执行时,可以不必等到线程创建完之后再执行。
提高线程的可管理性。线程不能无限制地创建,需要进行统一的分配、调优和监控。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPool implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(new ThreadPool());
executorService.execute(new ThreadPool());
executorService.execute(new ThreadPool());
executorService.shutdown();
}
}
2.并发集合: BlockingQueue
BlockingQueue实现被设计为主要用于生产者-消费者队列,如果BlockingQueue是空的,
从BlockingQueue取东西的操作将会被阻断进入等待状态,直到BlockingQueue进了东西才
会被唤醒。同样,如果BlockingQueue是满的,任何试图往里存东西的操作也会被阻断进入
等待状态,直到BlockingQueue里有空间时才会被唤醒。
BlockingQueue实现是线程安全的。所有排队方法都可以使用内部锁或其他形式的并发控制来原子地实现其效果。
3.静态代理
静态代理就是在编译时就确定了代理类与被代理类的关系。
public class StaticProcy {
public static void main(String[] args) {
Wedding wedding = new Wedding(new You());
wedding.HappyMarry();
}
}
interface Marry{
void HappyMarry();
}
class You implements Marry{
@Override
public void HappyMarry() {
System.out.println("秦老师结婚了");
}
}
class Wedding implements Marry{
private Marry target;
public Wedding(Marry target){
this.target=target;
}
@Override
public void HappyMarry() {
befor();
this.target.HappyMarry();
after();
}
private void after() {
System.out.println("结婚后:付尾款");
}
private void befor() {
System.out.println("结婚前:布置现场");
}
}
4.Lamda表达式:
Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
使用 Lambda 表达式可以使代码变的更加简洁紧凑。
ILove love5 = () -> {
System.out.println("I Love Lambda 5");
};
love5.lambda();