线程
程序(program):是为完成特定任务、用某种语言编写的一组指令的集合。
即指一端静态的代码。
进程(process):指正在执行的程序,从Windows角度讲,进程是操作系统进行资源分配的最小单位。
线程(thread):进程可进一步细化为线程,是一个进程内部的最小执行单元,是操作系统进行任务调度的最小单元,隶属于进程。
- 一个进程可以包含多个线程,一个线程只能属于一个进程,线程不能脱离进程而独立运行。
- 每一个进程至少包含一个线程(主线程);在主线程中开始执行程序,Java程序的入口main()方法就是在主线程中被执行的;
- 在主线程中可以创建并启动其他的线程;
- 一个进程内的所有线程共享该线程的内存资源。
- 继承Thread类的方式(类就不能继承其他类);
package Thread.Demo01; public class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("MyThread:"+i); } } public static void main(String[] args) { //创建线程 MyThread myThread = new MyThread(); myThread.start(); System.out.println("结束"); } }运行结果:
结束 MyThread:0 MyThread:1 MyThread:2 MyThread:3 MyThread:4 MyThread:5 MyThread:6 MyThread:7 MyThread:8 MyThread:9 - 实现Runnable接口的方式(类还可以继承其他类);
package Thread.Demo01; public class ThreadDemo implements Runnable{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("ThreadDemo:"+i); //System.out.println(Thread.currentThread().getName(); } } public static void main(String[] args) { //创建线程执行任务 ThreadDemo threadDemo = new ThreadDemo(); //创建线程 Thread t = new Thread(threadDemo); //定义线程名称 //Thread t = new Thread(threadDemo,"自定义线程"); //启动线程 t.start(); for (int i = 0; i < 10; i++) { System.out.println("main:"+i); } } }运行结果:
main:0 main:1 main:2 ThreadDemo:0 ThreadDemo:1 ThreadDemo:2 ThreadDemo:3 ThreadDemo:4 ThreadDemo:5 ThreadDemo:6 main:3 ThreadDemo:7 ThreadDemo:8 ThreadDemo:9 main:4 main:5 main:6 main:7 main:8 main:9 - 继承方式和实现方式的联系和区别:
-
【区别】
继承Thread: 线程代码存在放在子类Thread子类run方法中;
实现Runnable: 线程代码存在接口的子类的run方法中;
【实现Runnable的好处】
- 避免了单继承的局限性;
- 多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源。
- Thread类构造方法:
-
构造方法
说明
Thread()
创建一个新的线程
Thread(String name)
创建一个指定名称的线程
Thread(Runnable target)
利用Runnable对象创建一个线程,启动时将执行该对象的run方法。
Thread(Runnable target,String name)
利用Runnable对象创建一个线程,并指定该线程的名称。
2.Thread类常用方法:
| Void start() | 启动线程 |
| Final void setName(String name) | 设置线程的名称 |
| Final String getName() | 返回线程的名称 |
| Final void setPriority(int newPriority) | 设置线程的优先级 |
| Final int getPriority() | 返回线程的优先级 |
| Final void join() throws InterruptedException | 等待线程终止 |
| static Thread currentThread() | 返回对当前正在执行的线程对象的引用 |
| static void sleep(long millis) throws InterruptedException | 让当前正在执行的线程休眠(暂停执行) 休眠时间由millis(毫秒)指定 |
- 计算机只有一个CPU,各个线程轮流获得CPU的使用权,才能执行任务;
- 优先级较高的线程有更多获得CPU的机会,反之亦然;
- 优先级用整数表示,取值1~10,线程的默认优先级是5,但是也可以通过setPriority和getPriority方法来设置或返回优先级。
线程的状态:
- 新建:当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态;
- 就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间,此时它具备运行的条件,但没分配到CPU资源;
- 运行:当就绪的线程被调度并获得了CPU资源时,进入运行状态,run()方法定义了线程的操作和功能;
- 阻塞:在某种特殊状态下,被人为挂起或执行输入输出操作时,让出CPU并临时终止自己的执行,进入阻塞状态;
- 死亡:线程完成了它的全部工作或线程被提前强制性终止或出现异常导致结束。
代码:
package Thread.Demo02;
public class ThreadDemo extends Thread {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
// try {
// //让线程休眠指定的时间
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
if (i%10==0){
//线程让步
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
public static void main(String[] args) throws InterruptedException {
ThreadDemo t = new ThreadDemo();
t.start();
//等待该线程结束(把其他线程加入到此线程,让其他线程阻塞)
t.join();
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
守护线程:一直执行,必须在启动前设置,在所有的用户线程结束后自动结束,如垃圾回收线程;
用户线程
- 多线程:
指程序中包含多个执行单元,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个 线程创建多个并行线程来完成各自的任务;
- 何时需要多线程:
程序需要同时执行两个或多个任务;
程序需要实现一些需要等待的任务时;
需要一些后台运行的程序时;
- 多线程的优点:
提高程序的响应;
提高CPU的利用率;
改善程序结构,将复杂任务分为多个线程,独立运行;
- 多线程的缺点:
线程需要占用内存,线程越多,占用的内存也越多;
多线程需要协调和管理,需要CPU时间跟踪线程;
线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题。
- 多线程何时产生问题:多个线程,访问的是同一个共享资源(数据);
- 多个线程访问共享资源出现问题的本质在于CPU是多核的,理论上是可以同时执行多个线程。
并行:在同一个时间,同时做多个事情
例如:360同时做多件事情;
并发:在一段时间内,依次做多件事情 并发执行,交替执行;
多线程同步:线程排队 + 锁
确保一个时间点只有一个线程访问共享资源,可以给共享资源加一把锁,哪个线程获取了这把锁,才有权访问该资源。
每个类被加载到内存中时,都会为该类创建一个class类对象,用于封装类的信息,一个类即使创建多个对象,class类的对象只有一个;
案例:买票机制
package Thread.Demo03;
public class TicketThread extends Thread{
//10张票 加static表示共享资源,只有一份
static int num = 10;
static Object lockFlog = new Object();
@Override
public void run() {
while (true) {
synchronized (lockFlog) {
if (num > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + num);
num--;
} else{
break;
}
}
}
}
public static void main(String[] args) {
TicketThread t1 = new TicketThread();
t1.setName("窗口1");
TicketThread t2 = new TicketThread();
t2.setName("窗口2");
t1.start();
t2.start();
}
}
运行结果:
运行结果: 窗口1:10 窗口1:9 窗口1:8 窗口1:7 窗口1:6 窗口1:5 窗口1:4 窗口1:3 窗口1:2 窗口2:1
加锁后:
加锁后:
package Thread.Demo03;
public class TicketThread_back {
//10张票 加static表示共享资源,只有一份
static int num = 10;
static Object lockFlog = new Object();
public void run() {
while (true) {
if (num > 0) {
break;
}
print();
}
}
public static void main(String[] args) {
TicketThread t1 = new TicketThread();
t1.setName("窗口1");
TicketThread t2 = new TicketThread();
t2.setName("窗口2");
t1.start();
t2.start();
}
public synchronized void print() {
if (num > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + num);
num--;
}
}
}
运行结果:
窗口1:10 窗口2:9 窗口2:8 窗口2:7 窗口2:6 窗口2:5 窗口2:4 窗口2:3 窗口2:2 窗口2:1
1.java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。
2.ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁,释放锁;
3.死锁:不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,形成了线程的死锁;
出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续;
package Thread.Demo04;
public class ThreadDemo extends Thread {
static Object obja = new Object();
static Object objb = new Object();
boolean flag = true;
public ThreadDemo(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if (flag) {
synchronized (obja) {
System.out.println("if obja");
synchronized (objb) {
System.out.println("if objb");
}
}
} else {
synchronized (objb) {
System.out.println("else objb");
synchronized (obja) {
System.out.println("else obja");
}
}
}
}
public static void main(String[] args) {
ThreadDemo d1 = new ThreadDemo(true);
d1.start();
ThreadDemo d2 = new ThreadDemo(false);
d2.start();
}
}
多次运行结果:
首次运行: if obja if objb else objb else obja 二次运行: else objb else obja if obja if objb 三次运行: if obja else objb (后不再改变)
线程通讯:多个线程通过相互牵制,相互调度,即线程间的相互作用。
三个方法:
.wait一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
.notify一旦执行此方法,就会唤醒被wait的一个线程,如果有多个线程被wait,就优先唤醒优先级高的那个;
.notifyAll一旦执行此方法,就会唤醒所有被wait的线程;
.wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中。
实现Callable接口与使用Runnable相比,Callable功能更加强大;
- 相比run()方法,可以有返回值;
- 方法可以跑出异常;
- 支持泛型的返回值;
- 需要借助FutureTask类,获取返回结果
接收任务:
FutureTask
futureTask = new FutureTask(任务); 创建线程:
Thread t = new Thread(futureTask);
t.start();
获取线程Call方法的返回值;
Ingeter val = futureTask.get();
package Thread.Demo07; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class SumThread implements Callable{ @Override public Integer call() throws Exception { int num = 0; for (int i = 0; i < 100; i++) { num += i; } return num; } public static void main(String[] args) throws ExecutionException, InterruptedException { SumThread sumThread = new SumThread(); FutureTask futureTask = new FutureTask(sumThread); Thread t = new Thread(futureTask); t.start(); //获取返回值 Integer sum = futureTask.get(); System.out.println(sum); } }
运行结果:4950



