进程的概念
? 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,即进程空间或(虚空间)。进程不依赖于线程而独立存在,一个进程中可以启动多个线程。比如在 Windows 系统中,一个运行的 exe 就是一个进程。
线程的概念
? 线程是指进程中的一个执行流程,一个进程中可以运行多个线程。比如 java.exe 进程中可以运行很多线程。线程总是属于某个进程,线程没有自己的虚拟地址空间,与进程内的其他线程一起共享分配给该进程的所有资源。
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。
? 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
? 线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程包含以下内容:
? 在Java中,每次程序运行至少启动2个线程:一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个JVM实际上就是在操作系统中启动了一个进程。
在Java中,“线程”指两件不同的事情:
一个Java应用总是从main()方法开始运行,main()方法运行在一个线程内,他被称为主线程。一旦创建一个新的线程,就产生一个新的调用栈。
线程总体分两类:用户线程 和 守候线程。
? 当所有用户线程执行完毕的时候,JVM自动关闭。但是守候线程却不独立于JVM,守候线程一般是由操作系统或者用户自己创建的。
Thread
类? 继承自Thread
类,Thread
类是所有线程类的父类,实现了对线程的抽取和封装
继承Thread类创建并启动多线程的步骤:
Thread
类,并重写该类的 run()
方法,该run方法的方法体就代表了线程需要完成的任务,因此,run()
方法的方法体被称为线程执行体。Thread
子类的对象,即创建了子线程。start()
方法来启动该线程。public class Demo01 extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("!!!你好!!!" + i);
}
}
public static void main(String[] args) {
Demo01 testThread = new Demo01();
testThread.start();
for (int i = 0; i < 100; i++) {
System.out.println("!!!欢迎!!!" + i);
}
}
}
Runnable
接口? 实现Runnable
接口创建并启动多线程的步骤:
Runnable
接口的实现类,并重写该接口中的run()
方法,该run()
方法的方法体同样是该线程的线程执行体。Runnable
实现类的实例,并以此实例作为Thread
的target
来创建Thread
对象,该Thread
对象才是真正的线程对象。start()
方法来启动该线程public class Demo02 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("陈浩楠" + i);
}
}
public static void main(String[] args) {
Demo02 t1 = new Demo02();
new Thread(t1).start();
for (int i = 0; i < 100; i++) {
System.out.println("曹超" + i);
}
}
}
一、获取当前线程的对象的方法:
Thread.CurrentThread()
二、获取当前线程的名字:
Thread.CurrentThread().GetName()
线程有六种状态
初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。
阻塞(BLOCKED):表示线程阻塞于锁。
等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
终止(TERMINATED):表示该线程已经执行完毕。
run()
方法完成时,或者主线程的main()
方法完成时,我们就认为它终止了。线程一旦终止了,就不能复生。//测试stop
//1、建议线程正常停止--->利用次数,不建议死循环
//2、建议使用标志位--->设置一个标志位
//3、不要使用stop或者destroy等过时的方法
public class Demo07 implements Runnable{
//1、设置一个标志位
private boolean flag = true;
@Override
public void run() {
int i = 0;
while (flag) {
System.out.println("run...thread" + i++);
}
}
//2、设置一个公开的方法停止线程
public void stop(){
this.flag = false;
}
public static void main(String[] args) {
Demo07 testStop = new Demo07();
new Thread(testStop).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main" + i);
if (i == 900) {
//调用stop方法,停止线程
testStop.stop();
System.out.println("线程停止");
}
}
}
}
? Thread.sleep(longmillis)
和Thread.sleep(long millis, int nanos)
静态方法强制当前正在执行的线程休眠(暂停执行),以“减慢线程”。当线程睡眠时,它入睡在某个地方,在苏醒之前不会返回到可运行状态。当睡眠时间到期,则返回到可运行状态。
sleep()
方法指定当前线程阻塞的毫秒数。sleep()
方法存在异常InterruptedException
。sleep()
时间到达后线程进入就绪阶段。sleep()
可以模拟网络延迟,倒计时等。sleep()
不会释放锁。//模拟网络延迟:放大问题的发生性
public class Demo08 implements Runnable{
//票数
private int ticketNums = 10;
@Override
public void run() {
while (true) {
if (ticketNums <= 0) {
break;
}
try {//模拟延时
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-->拿到了第" + ticketNums-- + "票");
}
}
public static void main(String[] args) {
Demo08 ticket = new Demo08();
new Thread(ticket,"曹超").start();
new Thread(ticket,"陈浩楠").start();
new Thread(ticket,"周惠康").start();
}
}
? 线程的让步是通过Thread.yield()
来实现的。yield()
方法的作用是:暂停当前正在执行的线程对象,并执行其他线程。
//测试线程礼让
//礼让不一定成功,看CPU心情
public class Demo01 {
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield,"曹超").start();
new Thread(myYield,"陈浩楠").start();
}
}
class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程开始执行");
Thread.yield();//线程礼让
System.out.println(Thread.currentThread().getName() + "线程停止执行");
}
}
? Thread
的非静态方法join()
让一个线程B“加入”到另外一个线程A的尾部。在A执行完毕之前,B不能工作。
//测试Join方法public class Demo02 implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("线程vip来了"); } } public static void main(String[] args) throws InterruptedException { //启动线程 Demo02 testJoin = new Demo02(); Thread thead = new Thread(testJoin); thead.start(); //主线程 for (int i = 0; i < 1000; i++) { if (i == 200) { thead.join();//插队 } System.out.println("main" + i); } }}
? 静态方法State()
用来查看当前线程的允许状态。
//观察测试线程状态public class Demo03 { public static void main(String[] args) { Thread thread = new Thread(()->{ for (int i = 0; i < 5; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("///////"); }); //观察状态 Thread.State state = thread.getState(); System.out.println(state); //观察启动后 thread.start(); state = thread.getState(); System.out.println(state);//Run while (state != Thread.State.TERMINATED) {//只要线程不终止,就一直输出状态 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } state = thread.getState(); System.out.println(state); } }}
? 线程总是存在优先级,优先级范围在1~10之间。JVM线程调度程序是基于优先级的抢先调度机制。在大多数情况下,当前运行的线程优先级将大于或等于线程池中任何线程的优先级。但这仅仅是大多数情况。
当设计多线程应用程序的时候,一定不要依赖于线程的优先级。因为线程调度优先级操作是没有保障的,只能把线程
优先级作用作为一种提高程序效率的方法,但是要保证程序不依赖这种操作。
Thread
类有三个常量,定义线程的优先级范围
可以使用
getPriority()
、setPriority()
两个方法来获取和修改优先级。
public class Demo04 { public static void main(String[] args) { System.out.println(Thread.currentThread().getName() + "---->" + Thread.currentThread().getPriority());//主线程的优先级 MyPriority myPriority = new MyPriority(); Thread t1 = new Thread(myPriority); Thread t2 = new Thread(myPriority); Thread t3 = new Thread(myPriority); Thread t4 = new Thread(myPriority); //设置优先级,再启动 t1.start(); t2.setPriority(1); t2.start(); t3.setPriority(4); t3.start(); t4.setPriority(Thread.MAX_PRIORITY); t4.start(); }}class MyPriority implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName() + "---->" + Thread.currentThread().getPriority()); }}
线程的优先级设定建议在
start()
调度前。
setDeamon()
方法创建一个守护线程。public class Demo05 { public static void main(String[] args) { God god = new God(); You you = new You(); Thread thread = new Thread(god); thread.setDaemon(true);//默认是false表示是用户线程,正常的线程都是用户线程 thread.start();//上帝启动 new Thread(you).start();//你 用户线程启动 }}//上帝class God implements Runnable { @Override public void run() { while (true) { System.out.println("上帝保佑着你"); } }}//你class You implements Runnable { @Override public void run() { for (int i = 0; i < 36500; i++) { System.out.println("你活着"); } System.out.println("你死了"); }}
并发:同个对象被多个线程同时操作
Java中每个对象都有一个内置锁。
? 当程序运行到非静态的synchronized
同步方法上时,自动获得与正在执行代码类的当前实例(this实例)有关的锁。获得一个对象的锁也称为获取锁、锁定对象、在对象上锁定或在对象上同步。
当程序运行到`synchronized`同步方法或代码块时才该对象锁才起作用。 一个对象只有一个锁。所以,如果一个线程获得该锁,就没有其他线程可以获得锁,直到第一个线程释放(或返回)锁。这也意味着任何其他线程都不能进入该对象上的`synchronized方法`或代码块,直到该锁被释放。
? 释放锁是指持锁线程退出了synchronized
同步方法或代码块。
synchronized
方法,并且两个线程使用相同的实例来调用方法,那么一次只能有一个线程能够执行方法,另一个需要等待,直到锁被释放。也就是说:如果一个线程在对象上获得一个锁,就没有任何其他线程可以进入(该对象的)类中的任何一个同步方法。需要注意的是,锁会降低进程的性能,需要看情况使用。
public class Demo06 { public static void main(String[] args) { BuyTicket station = new BuyTicket(); new Thread(station,"曹超").start(); new Thread(station,"陈浩楠").start(); new Thread(station,"周惠康").start(); }}class BuyTicket implements Runnable { //票 private int ticketNums = 10; //外部标记 boolean flag = true; @Override public void run() { //买票 while (flag) { try { buy(); } catch (InterruptedException e) { e.printStackTrace(); } } } //同步方法 private synchronized void buy() throws InterruptedException { //判断是否有票 if (ticketNums <= 0) { flag = false; return; } Thread.sleep(100); System.out.println(Thread.currentThread().getName() + "拿到" + ticketNums--); }}
import java.util.concurrent.CopyOnWriteArrayList;//测试JUC安全类型的集合public class Demo08 { public static void main(String[] args) { CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>(); for (int i = 0; i < 10000; i++) { new Thread(() -> list.add(Thread.currentThread().getName())).start(); } try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(list.size()); }}
? 多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或多个线程都在等待对方释放资源,都停止执行的情形。
? 某个一个同步块同时拥有”两个以上对象的锁“时,就可能发生”死锁“的问题。
//死锁:多个线程互相抱着对方需要的资源,然后形成僵持public class Demo09 { public static void main(String[] args) { MakeUp g1 = new MakeUp(0, "曹超"); MakeUp g2 = new MakeUp(1, "陈浩楠"); g1.start(); g2.start(); }}//口红class Lipstick {}//镜子class Mirror {}//化妆class MakeUp extends Thread { //需要的资源只有一份 static Lipstick lipstick = new Lipstick(); static Mirror mirror = new Mirror(); int choice;//选择 String girlName;//使用化妆品的人 public MakeUp(int choice, String girlName) { this.choice = choice; this.girlName = girlName; } @Override public void run() { //化妆 try { this.MakeUP(); } catch (InterruptedException e) { e.printStackTrace(); } } //化妆:互相持有对方的锁 private void MakeUP() throws InterruptedException { if (choice == 0) { synchronized (lipstick) {//获得口红的锁 System.out.println(this.girlName + "获得口红的锁"); Thread.sleep(1000); synchronized (mirror) {//1秒钟后想获得镜子 System.out.println(this.girlName + "获得镜子的锁"); } } } else { synchronized (mirror) {//获得镜子的锁 System.out.println(this.girlName + "获得镜子的锁"); Thread.sleep(2000); synchronized (lipstick) {//1秒钟后想获得口红 System.out.println(this.girlName + "获得口红的锁"); } } } }}
//死锁:多个线程互相抱着对方需要的资源,然后形成僵持public class Demo09 { public static void main(String[] args) { MakeUp g1 = new MakeUp(0, "曹超"); MakeUp g2 = new MakeUp(1, "陈浩楠"); g1.start(); g2.start(); }}//口红class Lipstick {}//镜子class Mirror {}//化妆class MakeUp extends Thread { //需要的资源只有一份 static Lipstick lipstick = new Lipstick(); static Mirror mirror = new Mirror(); int choice;//选择 String girlName;//使用化妆品的人 public MakeUp(int choice, String girlName) { this.choice = choice; this.girlName = girlName; } @Override public void run() { //化妆 try { this.MakeUP(); } catch (InterruptedException e) { e.printStackTrace(); } } //化妆:互相持有对方的锁 private void MakeUP() throws InterruptedException { if (choice == 0) { synchronized (lipstick) {//获得口红的锁 System.out.println(this.girlName + "获得口红的锁"); Thread.sleep(1000); } synchronized (mirror) {//1秒钟后想获得镜子 System.out.println(this.girlName + "获得镜子的锁"); } } else { synchronized (mirror) {//获得镜子的锁 System.out.println(this.girlName + "获得镜子的锁"); Thread.sleep(2000); } synchronized (lipstick) {//1秒钟后想获得口红 System.out.println(this.girlName + "获得口红的锁"); } } }}
Lock
对象充当。Lock
对象加锁,线程开始访问共享资源之前应先获得Lock
对象。ReentrantLock
类实现了Lock
,它拥有与Synchronized
相同的并发性和内存语义,在实现线程安全的控制中,比较常使用的是ReentrantLock
,可以显式加锁、释放锁。import java.util.concurrent.locks.ReentrantLock;//测试Lock锁public class Demo01 { public static void main(String[] args) { TestLock testLock = new TestLock(); new Thread(testLock).start(); new Thread(testLock).start(); new Thread(testLock).start(); }}class TestLock implements Runnable { private int ticketNums = 10; //定义Lock锁 private final ReentrantLock lock = new ReentrantLock(); @Override public void run() { while (true) { try { lock.lock();//加锁 if (ticketNums > 0) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(ticketNums--); } else { break; } } finally {//解锁 lock.unlock(); } } }}
Synchronized
和Lock
的对比Lock
是显式锁(手动开启和手动关闭);Synchronized
是隐式锁,出了作用域自动释放。Lock
只有代码块锁;Synchronized
有代码块锁和方法锁。Lock
锁,JVM将花费较少的时间来调度线程使用的优先顺序:
? Lock ---> 同步代码块 ---> 同步方法
此模型将要结合java.lang.Object的
wait()
与notify()
、notifyAll()
方法来实现以上的需求。
//测试生产者-消费者模型--->利用缓冲区解决:管程法public class Demo02 { public static void main(String[] args) { SynContainer container = new SynContainer(); new Productor(container).start(); new Consumer(container).start(); }}//生产者class Productor extends Thread { SynContainer container; public Productor(SynContainer container) { this.container = container; } //生产 @Override public void run() { for (int i = 0; i < 100; i++) { container.Push(new Chicken(i)); System.out.println("生产了" + i + "只鸡"); } }}//消费者class Consumer extends Thread { SynContainer container; public Consumer(SynContainer container) { this.container = container; } //消费 @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("消费了" + container.Pop().id + "只鸡"); } }}//产品class Chicken { int id;//产品编号 public Chicken(int id) { this.id = id; }}//缓冲区class SynContainer { //容器大小 Chicken[] chickens = new Chicken[10]; //容器计数器 int count = 0; //生产者放入产品 public synchronized void Push(Chicken chicken) { //如果容器满了,就需要等待消费 if (count == chickens.length) { //通知消费者消费,生产者等待 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //如果没有满就需要丢入 chickens[count] = chicken; count++; //可以通知消费者消费了 this.notifyAll(); } //消费者消费产品 public synchronized Chicken Pop() { //判断能否消费 if (count == 0) { //等待生产者生产,消费者等待 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //如果可以消费 count--; Chicken chicken = chickens[count]; //通知生产者生产 this.notifyAll(); return chicken; }}
//测试生产者-消费者模型--->利用缓冲区解决:标志位法public class Demo03 { public static void main(String[] args) { TV tv = new TV(); new Player(tv).start(); new Watcher(tv).start(); }}//生产者-->演员class Player extends Thread { TV tv; public Player(TV tv) { this.tv = tv; } @Override public void run() { for (int i = 0; i < 20; i++) { if (i % 2 == 0) { this.tv.Play("====快乐大本营===="); } else { this.tv.Play("====广告===="); } } }}//消费者-->观众class Watcher extends Thread { TV tv; public Watcher(TV tv) { this.tv = tv; } @Override public void run() { for (int i = 0; i < 20; i++) { tv.Watch(); } }}//产品-->节目class TV { //演员表演,观众等待 T //观众观看,演员等待 F String voice;//表演的节目 boolean flag = true; //表演 public synchronized void Play(String voice) { if (!flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("演员表演了:" + voice); //通知观众观看 this.notifyAll(); this.voice = voice; this.flag = !this.flag; } //观看 public synchronized void Watch() { if (flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("观看了:" + voice); //通知演员表演 this.notifyAll(); this.flag = !this.flag; }}
常用的属性
corePoolSize //核心池的大小maximumPoolSize //最大线程数keepAliveTime //线程没有任务时最多抱持多长时间后会终止。
JDK5.0起提供了线程池相关的API:ExecutorService
和Executors
。
ExecutorService
:真正的线程池接口。
拥有如下方法
void execute(Runable command)
:执行任务/命令,没有返回值,一般用来执行Runable
。<T> Future<T> submit(Callable<T> task)
:执行任务,有返回值,一般用来执行Callable
。void shotdown()
:关闭连接池Executors
:工具类、线程池的工厂类,用于创建并返回不同类型的线程池。
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;//测试线程池public class Demo04 { public static void main(String[] args) { //1,创建线程池 ExecutorService service = Executors.newFixedThreadPool(10); service.execute(new MyThead()); service.execute(new MyThead()); service.execute(new MyThead()); service.execute(new MyThead()); //2,关闭连接 service.shutdown(); }}class MyThead implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + i); } }}
原文:https://www.cnblogs.com/kimariyb/p/15127593.html