概述
进程
概述:运行起来的程序就是进程
特点:进程与进程之间的资源是独立的,操作系统分配的基本单位
多进程:同时可以运行多个程序,“宏观并行,微观串行”
线程
概述:进程里面的一条执行路径;如果有多条执行路径,则是多线程
现实中的进程与线程:
进程: QQ音乐,再执行迅雷下载
线程:迅雷中分多条执行路径下载大片;特点:在该进行中多线程会互抢资源
进程与线程区别
进程是系统分配的基本单位; 线程是cpu调度的基本单位
一个进程一般会有多个线程,至少有一个线程
进程间的资源是独立的; 进程中的多个线程资源是共享的
线程创建
继承Thread
//线程的创建:继承Thread
//案例:主线程(main的执行路径)和子线(创建的)程都打印1~200;查看执行的结果
class MyThread extends Thread{
@Override
public void run() { //run中的执行就是子线程的执行
for(int i=1;i<=200;i++) {
System.out.println("子线程..."+i);
}
}
}
public class Test1 {
public static void main(String[] args) {
//创建子线程的注意事项:1.子线程的创建要放到主线程执行前面 2.调start方法
MyThread thread = new MyThread();
//将当前线程对象放入线程组,供cpu调度;当cpu调度到你,则你执行;调度到其他线程,则就绪状态
thread.start();
//thread.start(); //new一次线程对象,不能多次调用,否则报错
new MyThread().start(); //new次new,多次调用,则不会有问题
for(int i=1;i<=200;i++) {
System.out.println("主线程..."+i);
}
}
}
实现Runnable
class Task implements Runnable{
@Override
public void run() {
for(int i=1;i<=200;i++) {
System.out.println("子线程..."+i);
}
}
}
public class Test2 {
public static void main(String[] args) {
Thread th = new Thread(new Task()); //传入任务对象
th.start();
for(int i=1;i<=200;i++) {
System.out.println("主线程..."+i);
}
}
}
线程的方法
优先级与sleep
线程的优先级,可以改变线程的执行的效率;但不是绝对性可以改变哪个线程先执行完
//线程的优先级: 优先级高,则大概率先执行完,但不绝对
//案例:两个子线程都打印1~200;查看执行的结果
class MyThread extends Thread{
public MyThread(String name) {
super(name); //值传给父类
}
@Override
public void run() {
for(int i=1;i<=200;i++) {
try {
Thread.sleep(1); //睡眠-单位毫秒; 好处复现线程抢占效果
} catch (InterruptedException e) {}
System.out.println(getName()+"..."+i); //调用父类name值
}
}
}
public class Test1 {
public static void main(String[] args) {
MyThread thread = new MyThread("线程1");
thread.setPriority(Thread.MAX_PRIORITY); //设置高优先级
thread.start();
MyThread thread2 = new MyThread("线程2");
thread2.setPriority(Thread.MIN_PRIORITY); //设置低优先级
thread2.start();
}
}
线程礼让
线程礼让yield:中断当次线程,继续和其他资源争抢; 礼让的线程相对会执行的慢一些,但不是绝对性的。
//线程的礼让:
//案例:两个子线程都打印1~200;其中一个线程每次执行都进行礼让,查看执行的结果
class A extends Thread{
@Override
public void run() {
for(int i=1;i<=200;i++) {
Thread.yield(); //线程礼让;理论上会执行得慢一点
System.out.println("线程A..."+i);
}
}
}
class B extends Thread{
@Override
public void run() {
for(int i=1;i<=200;i++) {
System.out.println("线程B..."+i); //调用父类name值
}
}
}
public class Test2 {
public static void main(String[] args) {
new A().start();
new B().start();
}
}
线程合并
线程合并join,也叫插队; 插队的线程绝对性的先执行完
应用场景: 子线程插主线程的队
//线程插队:
//案例:子线程和主线程各打印1~200;主线程执行到5,让子线程插队先执行完,查看执行的结果
class MyTh extends Thread{
@Override
public void run() {
for(int i=1;i<=200;i++) {
System.out.println("子线程..."+i);
}
}
}
public class Test3 {
public static void main(String[] args) throws InterruptedException {
MyTh th = new MyTh();
th.start();
for(int i=1;i<=200;i++) {
System.out.println("主线程线程..."+i);
if(i==5) {
th.join(); //子线程插队
}
}
}
}
线程安全
在多线程中,出现了共享数据,需要加锁处理;锁的注意事项: 1.同一个锁对象 2.锁的范围
案例:买票系统,5个窗口共卖1000张票;观察卖票过程:
继承Thread
class MyThread extends Thread{
private static int ticket = 1000; //1000张票-共享数据
public MyThread(String name) {
super(name);
}
@Override
public void run() {
while(true) {
//同步代码块方式
synchronized ("lock") {
if(ticket>0) {
System.out.println(getName()+"正在卖第"+ticket+"张票");
ticket--;
}else {
System.out.println(getName()+"已经卖完了");
break;
}
}
}
}
}
public class Test2 {
public static void main(String[] args) {
for(int i=1;i<=5;i++) {
new MyThread("窗口00"+i).start();
}
}
}
实现Runnable
public class Test1 {
public static void main(String[] args) {
Task task = new Task();
for(int i=1;i<=5;i++) {
new Thread(task,"窗口"+i).start();
}
}
}
class Task implements Runnable{
private int ticket = 1000; //1000张票-共享数据
@Override
public void run() {
while(true) {
//同步代码块方式
synchronized (this) { //"lock" 静态对象
if(ticket>0) {
//Thread.currentThread().getName():实现任务获取的线程名
System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票");
ticket--;
}else {
System.out.println(Thread.currentThread().getName()+"已经卖完了");
break;
}
}
}
}
死锁
有一个线程进入到锁里后,没有退出;其他的线程进不去,则构成死锁
同步代码块和同步方法都是自动释放锁资源,所以不容易出现死锁;如果需要演示死锁案例;在同步锁中需要进行锁嵌套;
注意:此处只是为了测试死锁,才进行的锁嵌套; 以后使用时尽量避免锁嵌套(避免死锁)
//死锁案例:两个线程,各自执行自身的代码
class MyThread extends Thread{
private boolean a; //标记判断
public MyThread(boolean a) {
this.a = a;
}
@Override
public void run() {
if(a) {
synchronized ("A") { //线程1
System.out.println("进入线程1的A锁");
synchronized ("B") {
System.out.println("进入线程1的B锁");
}
}
}else {
synchronized ("B") { //线程2
System.out.println("进入线程2的B锁");
synchronized ("A") {
System.out.println("进入线程2的A锁");
}
}
}
}
}
public class Test1 {
public static void main(String[] args) {
new MyThread(true).start(); //线程1
new MyThread(false).start(); //线程2
}
}
线程池
线程池:就是装线程的容器,预先在容器中创建指定个数的线程对象;当用户需要时,直接从容器中获取;用完了,再回收到线程池中。
之前创建线程的方式:创建线程对象后,执行完毕则销毁线程对象;如果程序中需要频繁创建线程时,会非常影响性能及消耗内存的资源
好处:减少了创建和销毁线程的数目,提升了性能及减少了资源的消耗
线程池的复用机制:线程对象使用完后,重新再回到线程池;可以交给其他用户继续使用
内部实现: 创建集合;集合中都是存线程对象;有用户使用,则移除当前集合元素;用完后,则添加回集合中。
class Task implements Runnable{
@Override
public void run() {
for(int i=1;i<=10;i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
public class Test1 {
public static void main(String[] args) {
//在线程池中创建单个线程对象,通过单个线程对象去处理多个任务
//ExecutorService es = Executors.newSingleThreadExecutor();
//带缓冲区的线程池:有多少个任务,则会产生多少个线程对象的处理
//ExecutorService es = Executors.newCachedThreadPool();
//带复用机制的线程池:指定线程池个数,进行复用
ExecutorService es = Executors.newFixedThreadPool(2);//(常用)
es.submit(new Task()); //任务接口Runnable
es.submit(new Task());
es.submit(new Task()); //有三个任务,则谁先执行完,再执行第3个--复用
es.shutdown(); //线程池的终止
}
}