- 程序、进程、线程
● 程序:一段静态的代码,在电脑安装的静态代码文件。
● 进程:运行中的程序,是操作系统进行资源分配的最小单位。
● 线程:进程中最小的执行单位,cpu以线程为单位调度线程。
● 进程与线程的关系:
一个进程包含多个线程,一个线程只属于一个进程;
线程不能脱离进程独立运行;
一个进程中至少有一个线程,即主线程
2.创建线程
2.1继承Thread类
● 代码语法:
public class MyThread extends Thread{
//需独立在线程中执行的任务,写在run();中
@Override
public void run() {
}
}
public class Test {
//顺序由操作系统决定
public static void main(String[] args) {
MyThread myThread=new MyThread();//创建线程对象
myThread.start();//启动线程
}
}
2.2实现Runnable接口
● 代码语法:
public class MyThread implements Runnable{
@Override
public void run() {
}
}
public class Test {
public static void main(String[] args) {
//创建线程中需执行的任务,没有创建线程
MyThread myThread=new MyThread();
//创建线程,添加任务
Thread thread=new Thread(myThread);
thread.start();
}
}
● 好处:
1. 可多继承其他类
2.适合多线程处理同一份资源
3.Thread类中方法
![](https://img-blog.csdnimg.cn/img_convert/320cba1d7d9eeb4e099f1bf01450c78f.png)
4.线程优先级
优先级以整数表示,范围是:1~10,一般线程的默认优先级为5,可通过setPriority()和getPriority()来设置,返回优先级。
优先级较高的线程有更多获得CPU的机会,反之亦然 。
Thread类有如下3个静态常量来表示优先级 ● MAX_PRIORITY:取值为10,表示最高优先级。 ● MIN_PRIORITY:取值为1,表示最底优先级。 ● NORM_PRIORITY:取值为5,表示默认的优先级。
5.线程状态
![](https://img-blog.csdnimg.cn/img_convert/c0f61c368468464f9bd2a32815407335.png)
● 新建:当一个Thread类或其子类的对象被声明并创建时,此时不能执行;
● 就绪:处于新建状态的线程被start()后,进入线程队列等待,此时可执行;
● 运行:当就绪的线程被调度且获得CPU执行权时,进入运行状态;
● 阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,失去cpu执行权,临时中止自己的执行,进入阻塞状态
● 死亡:线程完成了它的全部工作/线程被提前强制性地中止/出现异常 导致结束。
6.守护线程
● Java中线程分为:用户线程和守护线程。
● 守护线程为其他线程的执行提供服务,只要当前JVM中存在非守护线程未结束,守护线程就全部运行。
● 二者的区别:若用户线程全部退出运行,守护线程会随之结束运行。
● 设置守护线程:
setDaemon(boolean on)
设置线程为守护线程要在启动线程前,否则会出现IllegalThreadStateException异常。
public class Test {
public static void main(String[] args) {
ThreadDemo t=new ThreadDemo();
t.start();
DaemonThread daemonThread=new DaemonThread();
daemonThread.setDaemon(true);//必须写在线程启动前,设置线程为守护线程
daemonThread.start();
}
}
public class ThreadDemo extends Thread{
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
System.out.println("ThreadDemo"+i);
}
}
}
public class DaemonThread extends Thread{
@Override
public void run() {
while(true){
System.out.println("DaemonThread");
}
}
}
7.多线程
7.1概念
指允许单个程序可以同时运行多个不同的线程来执行不同的任务。
7.2优缺点
● 优点:
提高CPU利用率;
提高程序响应;
改善程序结构,将复杂任务分为多个线程独立运行。
● 缺点:
内存随线程的增多而增大;
线程之间访问共享资源会受影响。
8.线程同步
8.1并发、并行
● 并发:一个时间段内一次执行操作。如:买票,看似同时进行,实则一个个进行。
● 并行:多个CPU同时执行多个任务。如:多个人同时做不同事。
8.2多线程同步
多线程同时共享同一份资源,会产生影响。故由线程”同步“来解决此问题。
8.3同步锁
可以是任何对象,但必须唯一,使得多个线程访问同一对象。
● 同步执行过程:
第一个线程访问,锁定同步对象,执行代码;
第二个线程访问,同步对象被锁定,无法访问;
第一个线程访问完毕,解锁同步对象;
第二个线程访问,锁定并访问。
Java中实现同步:
- 使用synchronized关键字修饰
1.1修饰代码块
synchronized(键对象【可以为任何对象】){
必须多个线程对应同一个对象,
用该对象记录是否由线程进入到同步代码块中,
用该对象的对象头中的一块空间记录锁的状态。
}
//1.当继承Thread时
public class TicketThread extends Thread{
static int num=10;//内存中只有一份,两线程对象共用同一份
static String s=new String();//确保多个线程对应同一对象
@Override
public void run() {
while(true){
synchronized(s){
if(num>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"买到了"+num--);
}
else{
break;
}
}
}
}
}
public class Test {
public static void main(String[] args) {
TicketThread t1=new TicketThread();
t1.setName("1号");
TicketThread t2=new TicketThread();
t2.setName("2号");
t1.start();
t2.start();
}
}
//2.当实现Runnable接口时
public class Ticket implements Runnable{
int num=10;
@Override
public void run() {
while(true){
synchronized (this){//只创建了一个对象,仅有一个this
if(num>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"买到了"+num--);
}
else{
break;
}
}
}
}
}
public class Test {
public static void main(String[] args) {
Ticket ticket=new Ticket();
Thread t1=new Thread(ticket,"1号");
t1.start();
Thread t2=new Thread(ticket,"2号");
t2.start();
}
}
1.2修饰方法
//1.当继承Thread时
public class TicketThread extends Thread{
static int num=10;//内存中只有一份,两线程对象共用同一份
static String s=new String();//确保多个线程对应同一对象
@Override
public void run() {
while(true){
if(num<=0){
break;
}
TicketThread.printTicket();
}
}
/*
synchronized修饰非static方法时,键对象是this,可能有多个this,
若修饰静态方法 staitc ,锁对象变为该类的class类的对象.
*/
public static synchronized void printTicket() {
if(num>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"买到了"+num--);
}
}
}
public class Test {
public static void main(String[] args) {
TicketThread t1=new TicketThread();
t1.setName("1号");
TicketThread t2=new TicketThread();
t2.setName("2号");
t1.start();
t2.start();
}
}
//2.当实现Runnable接口时
public class Ticket implements Runnable{
int num=10;
@Override
public void run() {
while(true){
if(num<=0){
break;
}
this.printTicket();
}
}
public synchronized void printTicket(){
if(num>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"买到了"+num--);
}
}
}
public class Test {
public static void main(String[] args) {
Ticket ticket=new Ticket();
Thread t1=new Thread(ticket,"1号");
t1.start();
Thread t2=new Thread(ticket,"2号");
t2.start();
}
}
9.Lock锁
● 使用ReentrantLock类实现Lock,可以显示加锁,释放锁。
Lock lock=new ReentrantLock();
/*创建ReentrantLock对象,当有线程获取执行权时,底层state状态由0变为1
若还有其他线程访问,都会进入到一个队列中等待
*/
lock.lock();//加锁
lock.unlock();//释放锁,一般写在finally代码块中,以防出现异常不能执行
10.线程死锁
● 多个线程分别占用对方所需的资源不放手,都在等待对方放弃自己所需的同步对象。程序不报错,也不提示。称为死锁。
● 代码举例:
public class DieLock extends Thread{
//两把锁
static Object objA=new Object();
static Object objB=new Object();
boolean flag;
public DieLock(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 class Test {
public static void main(String[] args) {
DieLock d1=new DieLock(true);
DieLock d2=new DieLock(false);
d1.start();
d2.start();
}
}
11.线程通信
11.1概念
● 多个线程通过相互牵制,相互调度,即线程间的相互作用。
11.2三方法
wait(); 在Object类 中定义,使线程进入等待状态,
须通过notify() 进行唤醒,可自动释放锁对象。
notify(;) 在Object类 中定义,唤醒等待中的线程
notifyAll(); 唤醒所有等待的线程
以上方法,必须在同步代码块/同步方法中使用
11.3例题:生产者/消费者
假设生产者(Product)每次在柜台(Counter)上生产固定数量的商品(如:1个),且柜台不能再放商品,此时生产者停止生产,进入等待状态,来唤醒消费者消费,而消费者消费后,唤醒生产者,再进入等待状态。
public class Counter {
int num=0;
public synchronized void add(){
if(num>0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else{
System.out.println("生产者生产");
num+=1;
this.notify();
}
}
public synchronized void sub(){
if(num==0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else{
System.out.println("消费者消费");
num-=1;
this.notify();
}
}
}
public class Product extends Thread{
Counter counter;
public Product(Counter counter) {
this.counter=counter;
}
@Override
public void run() {
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
counter.add();
}
}
}
public class Customer extends Thread{
Counter counter;
public Customer(Counter counter) {
this.counter=counter;
}
@Override
public void run() {
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
counter.sub();
}
}
}
public class Test {
public static void main(String[] args) {
Counter counter=new Counter();
Product product=new Product(counter);
Customer customer=new Customer(counter);
product.start();
customer.start();
}
}
12.新增创建线程方式
● 实现Callable接口:
• 相比run()方法,可以有返回值 • 方法可抛出异常 • 支持泛型的返回值 • 使用FutureTask类,获取返回结 果
//接收任务
FutureTask<Integer> futureTask = new FutureTask(任务);
//创建线程
Thread t = new Thread(futureTask);
t.start();
Integer val = futureTask.get();获得线程call方法的返回值
● 代码举例:
import java.util.concurrent.Callable;
public class Fun implements Callable {
@Override
public Integer call() throws Exception {
int num=0;
for (int i = 0; i < 100; i++) {
num+=i;
}
return num;
}
}
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Fun fun=new Fun();
FutureTask<Integer> futureTask=new FutureTask<>(fun);
Thread t=new Thread(futureTask);
t.start();
Integer s=futureTask.get();
System.out.println(s);//4950
}
}