线程
- 程序、进程、线程
程序(program):是为完成特定任务、用某种语言编写的一组指令的集合。
即指一端静态的代码。
进程(process):指正在执行的程序,从Windows角度讲,进程是操作系统进行资源分配的最小单位。
线程(thread):进程可进一步细化为线程,是一个进程内部的最小执行单元,是操作系统进行任务调度的最小单元,隶属于进程。
- 线程和进程的关系
- 创建线程
- 继承Thread类的方式(类就不能继承其他类);
package Thread.Demo01; /* 继承Thread类 类就不能继承其他类 */ 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; /* 实现Thread接口 类还可以继承其他类 重写run() 这个类可以成为线程任务类 Thread.currentThread() 获得当前正在执行的线程对象 */ 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
- 继承方式和实现方式的联系和区别:
-
【区别】
【实现Runnable的好处】
- 避免了单继承的局限性;
- 多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源。
- Thread类中的方法
- 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(毫秒)指定 |
- 线程优先级
- 线程状态
线程的状态:
代码:
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是多核的,理论上是可以同时执行多个线程。
- 线程同步
并行:在同一个时间,同时做多个事情
例如: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) {
/*
if(num > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
*/
/*
synchronized(锁对象) {
同步代码
}
锁对象可以是任何对象,但是对于多个线程必须是同一个.
在对象中,有一个对象头的区域,在对象头中有一个标志为(锁状态)
具体实现,由编译后的指令实现控制
*/
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();
}
/*
synchronized修饰方法时,并没有显示的让我们添加锁的标志对象。
在修饰非静态方法时,锁标志的对象默认this
在修饰静态方法时,锁标志对象是class类对象
*/
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
- Lock锁
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
(后不再改变)
- 线程通信
线程通讯:多个线程通过相互牵制,相互调度,即线程间的相互作用。
三个方法:
- 新增创建线程方式
实现Callable接口与使用Runnable相比,Callable功能更加强大;
package Thread.Demo07;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class SumThread implements Callable<Integer> {
@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<Integer> futureTask = new FutureTask(sumThread);
Thread t = new Thread(futureTask);
t.start();
//获取返回值
Integer sum = futureTask.get();
System.out.println(sum);
}
}
运行结果:4950