线程
线程
程序:为完成特定任务,用某种语言编写的一组指令的集合,即一段静态代码
进程:正在执行的程序,加载到内存中执行
线程:进程可进一步细化为线程,是一个进程内部的最小执行单元
进程与线程的关系
一个进程中可以包含多个线程,一个线程只属于一个进程,线程不能脱离进程存在.
一个进程至少包含一个线程,即主线程.例如java中的main就是
所有进程共享进程资源
在主线程中可以创建并启动其他线程
多线程
多线程:在一个进程中,可以拥有多个线程,执行多个任务
何时需要多线程?
程序需要同时执行多个任务
多线程的优点:
同时执行多个任务,功能强大
提高CPU利用率,CPU执行是以线程为单位
改善数据结构,将复杂任务分为多个线程独立运行
多线程的缺点:
线程越多,堆内存,CPU占用越高(处理方法:硬件升级)
多个线程对同一个共享资源进行访问,会出现各种不正常情况
解决方法:线程同步,效率低
创建线程
1.继承Thread类
重写run();方法
public class ThreadDemo extends Thread{
@Override
public void run() {
for (int i = 0; i <1000 ; i++) {
System.out.println("Thread:"+i);
}
}
}
Test测试
public class Test {
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
threadDemo.start();//启动线程
ThreadDemo2 threadDemo2 = new ThreadDemo2();
threadDemo2.start();//启动线程
for (int i = 0; i < 1000; i++) {
System.out.println("main:"+i);
}
}
}
2.实现Runnable接口
public class ThreadDemo implements Runnable{
@Override
public void run() {
for (int i = 0; i <100 ; i++) {
System.out.println("Thread:"+i);
}
}
}
test测试
public static void main(String[] args) {
//创建一个线程要执行的任务
ThreadDemo threadDemo = new ThreadDemo();
//创建线程,并将任务添加到线程中
Thread t = new Thread(threadDemo);
t.start();//启动线程
for (int i = 0; i < 100; i++) {
System.out.println("main:" + i);
}
}
线程中的方法
run();继承线程中需要执行的任务代码
start();启动线程
Tread类中的构造方法
Thread(Runnable target);
Thread(Runnable target , String name);
Thread.currentThread(); 获得当前正在执行的线程
setName();为线程设置名称
getName();获取线程名称
线程优先级
java中线程优先级,用整型1-10
默认优先级为5
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();//创建一个线程要执行的任务
Thread t = new Thread(threadDemo, "自定义线程1");//创建线程,并将任务添加到线程中
t.setPriority(1);
t.start();//启动线程
Thread t1 = new Thread(threadDemo );//创建线程,并将任务添加到线程中
t1.setName("自定义线程2");//设置线程名称
t1.setPriority(10);//设置优先级
t1.start();//启动线程
for (int i = 0; i < 100; i++) {
System.out.println("main:" + i);
}
System.out.println(t.getPriority());
System.out.println(t1.getPriority());
System.out.println(Thread.currentThread().getName());//获取当前正在执行的线程名称
System.out.println(Thread.currentThread().getPriority());//获取当前线程的优先级
}
public class ThreadDemo implements Runnable{
@Override
public void run() {
for (int i = 0; i <100 ; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
运行结果:
main:0
main:1
自定义线程1:0
自定义线程1:1
自定义线程1:2
自定义线程1:3
自定义线程1:4
自定义线程1:5
自定义线程1:6
自定义线程1:7
自定义线程1:8
自定义线程1:9
自定义线程2:0
自定义线程2:1
自定义线程2:2
自定义线程2:3
自定义线程2:4
自定义线程2:5
自定义线程2:6
自定义线程2:7
自定义线程2:8
自定义线程2:9
main:2
main:3
main:4
main:5
main:6
main:7
main:8
main:9
1
10
main
5
CPU执行策略:
时间片:就是排队,先来先服务
抢占式:对优先级高的先执行
线程的状态
start();启动线程 就绪状态(等待CPU的加载)
当线程获得了CPU的执行权,进入到运行状态
当线程失去CPU执行权(因为CPU需要轮流执行其他线程),进入待就绪状态
当线程中所有任务执行完以后,进入死亡状态
yield();线程主动让步
sleep();让线程休眠指定时间,进入阻塞状态
t.join();线程合并,等待t进程执行完后,其他进程再执行
stop();强制让线程销毁
public class ThreadDemo extends Thread{
@Override
public void run() {
for (int i = 0; i <1000 ; i++) {
/*if (i%5==0){
Thread.yield();//线程主动让步
}*/
/* try {
Thread.sleep(1000);//让线程休眠指定时间,进入阻塞状态
} catch (InterruptedException e) {
e.printStackTrace();
}*/
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
public class Test {
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
threadDemo.setName("线程1");
threadDemo.start();//启动线程 就绪状态(等待CPU加载)
// 当线程获得了CPU的执行权 ,进入到运行状态
//当线程失去CPU执行权(因为CPU还要轮流执行其他线程),进入就绪状态
//当线程中所有任务执行完以后,进入死亡状态
/* try {
threadDemo.join();//线程合并,等待threadDemo线程执行完后,其他线程执行
} catch (InterruptedException e) {
e.printStackTrace();
}*/
//threadDemo.stop();//强制让线程销毁
ThreadDemo threadDemo2 = new ThreadDemo();
threadDemo2.setName("线程2");
threadDemo2.start();//启动线程
//threadDemo2.stop();//强制让线程销毁
}
}
守护线程
是一种特殊的线程
当把一个线程设置为守护线程时,守护线程需要等所有得非守护线程执行完结束后,才会销毁
t.setDaemon(true);将线程设置为守护线程
JVM中垃圾回收器就是一个典型的守护线程
public class ThreadDemo extends Thread{
@Override
public void run() {
while (true){
System.out.println("我是守护线程");
}
}
}
public class Test {
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
threadDemo.setDaemon(true);//将线程设置为守护线程
threadDemo.start();//启动线程
for (int i = 0; i < 1000; i++) {
System.out.println("我是用户线程");
}
}
}
线程的同步
并行:多个CPU同时执行多个任务
并发:一个CPU同时执行多个任务
前提:
1.必须是多线程
2.多线程访问同一个共享资源
同步机制:线程排队+锁
static 变量 随着类的加载而加载,生命周期长
非static 变量 生命周期与对象相同
典型案例:买票
票数是固定的,买票的窗口有多个,就有多个线程同时访问同一资源
使用synchronize修饰
通过两种创建线程的方法实现功能
一.继承Thread类
1.synchronize修饰同步代码块
public class VoteDemo extends Thread {
static int votes = 10;//多个线程访问共享资源
static Object obj = new Object();//创建唯一的锁
@Override
public void run() {
//while循环一直出票
while (true) {
//为需要同步的代码,用同步代码快修饰,多个线程只有一把锁,谁先拿到谁就用
//当持有锁的线程把同步代码中的内容执行完以后,离开同步代码快,会自动释放锁
synchronized (obj){
if (votes > 0) {
System.out.println(Thread.currentThread().getName() + ":" + votes);
votes--;
} else if (votes <= 0) {
break;
}
}
}
}
2.用synchronized修饰方法,此方法为同步方法
public class VoteDemo extends Thread {
static int votes = 10;//多个线程访问共享资源
@Override
public void run() {
while (true) {
if (votes > 0) {
vote();
} else {
break;
}
}
}
//用synchronized修饰方法,此方法为同步方法
//synchronized修饰方式时,锁对象默认为this,多个线程就有多把锁
public static synchronized void vote() {
if (votes > 0) {
System.out.println(Thread.currentThread().getName() + ":" + votes);
votes--;
}
}
}
测试类
public class Test {
public static void main(String[] args) {
VoteDemo voteDemo = new VoteDemo();
voteDemo.setName("窗口1");
voteDemo.start();
VoteDemo voteDemo2 = new VoteDemo();
voteDemo2.setName("窗口2");
voteDemo2.start();
}
}
二.实现Runnable类
1.synchronize修饰同步代码块
public class VoteDemo implements Runnable {
int votes = 10;
Object obj = new Object();
//修饰同步内容
@Override
public void run() {
while (true) {
synchronized (obj){
if (votes > 0) {
System.out.println(Thread.currentThread().getName() + ":" + votes);
votes--;
} else if (votes <= 0) {
break;
}
}
}
}
}
2.用synchronized修饰方法,此方法为同步方法
public class VoteDemo implements Runnable {
int votes = 10;
Object obj = new Object();
@Override
public void run() {
while (true) {
if (votes > 0) {
Vote();
} else {
break;
}
}
}
//用synchronize修饰方法,此方法为同步方法
//synchronize修饰方法时,锁对象默认为this,只创建一个线程任务对象,this只有一个
public synchronized void Vote() {
if (votes > 0) {
System.out.println(Thread.currentThread().getName() + ":" + votes);
votes--;
}
}
}
测试类
public class Test {
public static void main(String[] args) {
VoteDemo voteDemo = new VoteDemo();
Thread thread1 = new Thread(voteDemo);
Thread thread2 = new Thread(voteDemo);
thread1.setName("窗口1");
thread2.setName("窗口2");
thread1.start();
thread2.start();
}
}
线程死锁
当线程持有对方线程中需要的锁时,会出现死锁现象
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");//objA这把锁已经被使用
synchronized (objB){//此时只能等待下面释放objB锁
System.out.println("if objB");
}
}
}else{
synchronized (objB){
System.out.println("else objB");//objB这把锁也被使用
synchronized (objA){//此时等待上面释放objA锁
System.out.println("else objA");
}
}
}
}
}
public class Test {
public static void main(String[] args) {
DieLock t1= new DieLock(true);
DieLock t2= new DieLock(false);
t1.start();
t2.start();
}
}
运行结果:
死锁出现不会报错,不会提示,只是等待.
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DZyhn87e-1611997903680)(C:\Users\17509\AppData\Roaming\Typora\typora-user-images\1611204001419.png)]
Lock方式的加锁
JDK5之后使用Lock方式显示的加锁,释放锁
Lock是显式锁(手动开启和关闭锁,别忘记关闭锁), 实现类ReentrantLock
synchronized是隐式锁,出了作用域自动释放
Lock只有代码块锁
synchronized 有代码块锁,方法
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class VoteDemo extends Thread {
static int num = 10;
static Lock lock = new ReentrantLock();//创建锁对象
@Override
public void run() {
while(true){
try {
lock.lock();//获得锁,加锁
Thread.sleep(100);
if(num>0){
System.out.println(num);
num--;
}else{
break;
}
lock.unlock();//释放锁
}catch (Exception e){
e.printStackTrace();
lock.unlock();//释放锁
}
}
}
}
public class Test {
public static void main(String[] args) {
VoteDemo voteDemo = new VoteDemo();
voteDemo.setName("窗口1");
voteDemo.start();
VoteDemo voteDemo2 = new VoteDemo();
voteDemo2.setName("窗口2");
voteDemo2.start();
}
}
线程通信
就是多个线程之间相互牵制,相互调度执行
wait();让线程等待,进入阻塞状态,不会自动唤醒
notify();唤醒等待的线程,如果有多个,那就唤醒优先级高的
notifyAll();唤醒所有等待的线程
案例:交替打印1-100的数
1.使用lock锁,实现Runnable的方法
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class PrintNum implements Runnable {
static int num = 0;
static Lock lock = new ReentrantLock();
static Condition con = lock.newCondition();
@Override
public void run() {
while (true) {
try {
lock.lock();
con.signal();//唤醒线程
num++;
if (num <= 100) {
System.out.println(Thread.currentThread().getName() + ":" + num);
} else {
lock.unlock();
break;
}
con.await();//线程等待,并释放锁
lock.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
lock.unlock();
}
}
}
}
测试类
public class Test {
public static void main(String[] args) {
PrintNum p =new PrintNum();
Thread p1 = new Thread(p);
Thread p2 = new Thread(p);
p1.start();
p2.start();
}
}
2.实现Runnable的方法,使用synchronize修饰同步代码块
public class PrintNum implements Runnable{
static int num=0;
@Override
public void run() {
while (true){
synchronized (this){//implements Runnable 情况下,this只有一个,this可以充当锁
this.notify();//唤醒等待线程 使用锁对象来调用notify(),wait();
if (num<=100){
System.out.println(Thread.currentThread().getName()+":"+num);
num++;
}else{
break;
}
try {
this.wait();//线程等待,并释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
测试类
public class Test {
public static void main(String[] args) {
PrintNum p =new PrintNum();
Thread p1 = new Thread(p);
Thread p2 = new Thread(p);
p1.start();
p2.start();
}
}
3.继承Thread类的方法,使用synchronize修饰同步代码块
public class PrintNum extends Thread {
static int num = 0;
static Object obj =new Object();//继承Thread方式,由于创建了多个对象,需要自己创建一个唯一的对象,充当锁
@Override
public void run() {
while (true) {
synchronized (obj) {
obj.notify();
if (num <= 100) {
System.out.println(Thread.currentThread().getName() + ":" + num);
num++;
} else {
break;
}
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
测试类
public class Test {
public static void main(String[] args) {
PrintNum p1 = new PrintNum();
PrintNum p2 = new PrintNum();
p1.start();
p2.start();
}
}
消费者生产者问题
1.柜台类,存放物品的类
/*柜台中 存放共享数据*/
public class Counter {
int num = 0;//初始物品数量为0
/* 生产商品*/
public synchronized void add() {
if (num == 0) {
num++;
System.out.println("生产一个");
this.notify();//唤醒消费者线程 this表示同一个柜台
} else {
try {
this.wait();//生产者等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/*消费商品 */
public synchronized void sub() {
if (num == 1) {
num--;
System.out.println("消费一个");
this.notify();//唤醒生产者线程 this表示同一个柜台
} else {
try {
this.wait();//消费者等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2.生产者线程
public class Productor extends Thread {
Counter c;
public Productor(Counter c) {
this.c = c;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);//休眠指定的时间
c.add();//调用add方法
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3.消费者线程
/*消费者线程*/
public class Customer extends Thread {
Counter c;
public Customer(Counter c) {
this.c = c;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
c.sub();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
4.测试类
public class Test {
public static void main(String[] args) {
Counter c = new Counter();//创建柜台对象,是生产或者和消费者
Productor p = new Productor(c);
Customer ct = new Customer(c);
p.start();
ct.start();
}
}
jdk8以后的两种创建线程的方法
以1-100求和问题
1.FutureTask用来接受任务
import java.util.concurrent.Callable;
public class SumNumThread implements Callable<Integer> {
/*
可以向外界返回一个结果
可以抛出一个异常
*/
@Override
public Integer call() throws Exception {
int sum=0;
for (int i = 1; i <= 100;i++){
sum+=i;
}
return sum;
}
}
public class Test {
public static void main(String[] args) {
//创建任务
SumNumThread sum = new SumNumThread();
//接受任务
FutureTask<Integer> futureTask = new FutureTask(sum);
//创建线程
Thread t = new Thread(futureTask);
t.start();
try {
Integer val = futureTask.get();
System.out.println(val);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
2.ExecutorService创建线程池
public class SumNumThread implements Callable<Integer> {
/*
可以向外界返回一个结果
可以抛出一个异常
*/
@Override
public Integer call() throws Exception {
int sum=0;
System.out.println(Thread.currentThread().getName());
for (int i = 1; i <= 100;i++){
sum+=i;
}
return sum;
}
}
public class Test {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(5);
SumNumThread sumNumThread = new SumNumThread();
//将任务交给线程池中线程执行
//线程池中线程不够时,会释放其中的重复利用
executorService.submit(sumNumThread);
executorService.submit(sumNumThread);
executorService.submit(sumNumThread);
executorService.submit(sumNumThread);
executorService.submit(sumNumThread);
Future<Integer> f = executorService.submit(sumNumThread);
try {
System.out.println(f.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
executorService.shutdown();
}
}