Java—线程与进程的深入理解
1、线程的各个方法与属性的使用
package com.hwadee;
/**
1:要想了解多线程,必须先了解线程,而要想了解线程,必须先了解进程,因为线程是依赖于进程而存在。
2:什么是进程?
通过任务管理器我们就看到了进程的存在。
而通过观察,我们发现只有运行的程序才会出现进程。
进程:就是正在运行的程序。
进程是系统进行资源分配和调用的独立单位。每一个进程都有它自己的内存空间和系统资源。
3:多进程有什么意义呢?
单进程的计算机只能做一件事情,而我们现在的计算机都可以做多件事情。
举例:一边玩游戏(游戏进程),一边听音乐(音乐进程)。
也就是说现在的计算机都是支持多进程的,可以在一个时间段内执行多个任务。
并且呢,可以提高CPU的使用率。
问题:
一边玩游戏,一边听音乐是同时进行的吗?
不是。因为单CPU在某一个时间点上只能做一件事情。
而我们在玩游戏,或者听音乐的时候,是CPU在做着程序间的高效切换让我们觉得是同时进行的。
4:什么是线程呢?
在同一个进程内又可以执行多个任务,而这每一个任务我就可以看出是一个线程。
线程:是程序的执行单元,执行路径。是程序使用CPU的最基本单位。
单线程:如果程序只有一条执行路径。
多线程:如果程序有多条执行路径。
5:多线程有什么意义呢?
多线程的存在,不是提高程序的执行速度。其实是为了提高应用程序的使用率。
程序的执行其实都是在抢CPU的资源,CPU的执行权。
多个进程是在抢这个资源,而其中的某一个进程如果执行路径比较多,就会有更高的几率抢到CPU的执行权。
我们是不敢保证哪一个线程能够在哪个时刻抢到,所以线程的执行有随机性。
进程:
正在运行的程序,是系统进行资源分配和调用的独立单位。
每一个进程都有它自己的内存空间和系统资源。
线程:
是进程中的单个顺序控制流,是一条执行路径
一个进程如果只有一条执行路径,则称为单线程程序。
一个进程如果有多条执行路径,则称为多线程程序。
假如我们的计算机只有一个 CPU,那么 CPU 在某一个时刻只能执行一条指令,线程只有得到 CPU时间片,也就是使用权,才可以执行指令。那么Java是如何对线程进行调用的呢?
线程有两种调度模型:
分时调度模型 所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片
抢占式调度模型 优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。
Java使用的是抢占式调度模型。
*
*/
public class Thread1 {
public static void main(String[] args) {
System.out.println("主线程开始下载");
for(int i =0;i<50;i++) {
System.out.println("这里是主线程输出的i="+i);
}
System.out.println("主线程下载完成");
MyThread1 myThread=new MyThread1();
myThread.start();
System.out.println("我要去忙自己的其他事");
System.out.println("让子线程自己去下载");
MyThread2 myThread2=new MyThread2();
myThread2.start();
}
}
class MyThread1 extends Thread{
@Override
public void run() {
super.run();
for(int i =0;i<50;i++) {
System.out.println("子线程1111输出的i="+i);
}
}
}
class MyThread2 extends Thread{
@Override
public void run() {
super.run();
for(int i =0;i<50;i++) {
System.out.println("子线程2222输出的i="+i);
}
}
}
package com.hwadee;
public class Thread2 {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+"开始运行");
System.out.println("主线程下载完,子线程开始进行");
RunnableImpl1 runnableImpl1=new RunnableImpl1();
Thread thread1=new Thread(runnableImpl1);
thread1.setName("我是线程1");
thread1.start();
RunnableImpl2 runnableImpl2=new RunnableImpl2();
Thread thread2=new Thread(runnableImpl2);
thread2.setName("我是线程2");
thread2.start();
System.out.println("我要先去喝杯咖啡");
}
}
class RunnableImpl1 implements Runnable{
@Override
public void run() {
for(int i=0;i<50;i++) {
System.out.println(Thread.currentThread().getName()+"输出的i="+i);
}
}
}
class RunnableImpl2 implements Runnable{
@Override
public void run() {
for(int i=0;i<50;i++) {
System.out.println(Thread.currentThread().getName()+"输出的i="+i);
}
}
}
package com.hwadee;
public class Thread3 {
public static void main(String[] args) {
RunnableImpl3 runnableImpl3=new RunnableImpl3();
Thread thread=new Thread(runnableImpl3);
thread.setName("我是子线程");
thread.start();
}
}
class RunnableImpl3 implements Runnable{
@Override
public void run() {
for(int i=0;i<10;i++) {
if(i==5) {
try {
Thread.sleep(1000*5);
System.out.println(Thread.currentThread().getName()+"输出的i="+i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
System.out.println(Thread.currentThread().getName()+"输出的i="+i);
}
}
}
}
package com.hwadee;
/**
* 在Java中有两类线程:用户线程 (User Thread)、守护线程 (Daemon Thread)。
* 所谓守护 线程,是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。
* 因 此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。
* 反过来说,只要任何非守护线程还在运行,程序就不会终止。
*
* 用户线程和守护线程两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。
* 因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了。
*
* thread.setDaemon(true)必须在thread.start()之前设置,否则会抛出一个IllegalThreadStateException异常
*
*/
public class Thread4 {
public static void main(String[] args) {
Thread11 t11=new Thread11();
t11.setName("张飞");
t11.setDaemon(true); //为true时,设置为守护线程
Thread22 t22=new Thread22();
t22.setName("关羽");
t22.setDaemon(true);
Thread33 t33=new Thread33();
t33.setName("刘备");
t11.start();
t22.start();
t33.start();
}
}
class Thread11 extends Thread{
@Override
public void run() {
super.run();
for(int i=0;i<50;i++) {
System.out.println(getName()+"i="+i);
}
}
}
class Thread22 extends Thread{
@Override
public void run() {
super.run();
for(int i=0;i<50;i++) {
System.out.println(getName()+"i="+i);
}
}
}
class Thread33 extends Thread{
@Override
public void run() {
super.run();
for(int i=0;i<20;i++) {
System.out.println(getName()+"i="+i);
}
}
}
package com.hwadee;
public class Thread5 {
public static void main(String[] args) {
TestThread t1=new TestThread();
t1.setName("大哥 ");
t1.setPriority(1);
TestThread t2=new TestThread();
t2.setName("2哥 ");
t2.setPriority(10); //setPriority设置线程的优先级,优先级为1-10,基础优先级为5
TestThread t3=new TestThread();
t3.setName("3弟 ");
t1.setPriority(1);
t1.start();
// try {
// t1.join(); //join() 等待该线程终止,再执行其他线程
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
t2.start();
t3.start();
}
}
class TestThread extends Thread{
@Override
public void run() {
super.run();
for(int i=0;i<100;i++) {
System.out.println(getName()+"i="+i);
}
}
}
package com.hwadee;
import java.util.Date;
public class Thread6 {
public static void main(String[] args) {
System.out.println("主线程开始运行"+new Date());
StopThread stt=new StopThread();
stt.start();
try {
Thread.sleep(1000*3);
} catch (InterruptedException e) {
e.printStackTrace();
}
//stt.stop();
stt.interrupt();
System.out.println("主线程运行完毕"+new Date());
}
}
class StopThread extends Thread{
@Override
public void run() {
super.run();
System.out.println("子线程开始运行"+new Date());
try {
Thread.sleep(1000*10);
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i=0;i<50;i++) {
System.out.println("i="+i);
}
System.out.println("子线程运行完毕"+new Date());
}
}
package com.hwadee;
public class Thread7 {
public static void main(String[] args) {
ThreadYeild t1=new ThreadYeild();
t1.setName("张三");
ThreadYeild t2=new ThreadYeild();
t2.setName("李四");
t1.start();
t2.start();
}
}
class ThreadYeild extends Thread{
@Override
public void run() {
super.run();
for(int i=0;i<100;i++) {
System.out.println(getName()+":"+i);
Thread.yield();
}
}
}
2、通过Student类对生产者、消费者的展示
package com.lock;
/*
* 解决方案:
* 加锁。
* 注意:
* A:不同种类的线程都要加锁。
* B:不同种类的线程加的锁必须是同一把。
*
* 想依次的一次一个输出。
*
* 如何实现呢?
* 通过Java提供的等待唤醒机制解决。
*
* 思路:
*
* 生产者:先看是否有数据,如果有就等待;没有就生产,生产完之后通知消费者来消费
*
* 消费者:先看是否有数据,如果有就消费;如果没有就等待,通知生产者生产数据
*
* 等待唤醒:
* Object类中提供了三个方法:
* wait():等待
* notify():唤醒单个线程
* notifyAll():唤醒所有线程
* 为什么这些方法不定义在Thread类中呢?
* 这些方法的调用必须通过锁对象调用,而我们刚才使用的锁对象是任意锁对象。
* 所以,这些方法必须定义在Object类中。
*
* 小结:
* wait()被调用后,会自动释放所持有的锁。
* notify()方法被调用后,不会自动释放锁。
*/
public class StudentDemo {
public static void main(String[] args) {
//创建资源
Student s=new Student();
//设置和获取的类
SetThread st=new SetThread(s);
GetThread gt=new GetThread(s);
//线程类
Thread t1=new Thread(st);
Thread t2=new Thread(gt);
//启动线程
t1.start();
t2.start();
}
}
package com.lock;
public class Student{
String name;
int age;
boolean flag; //默认情况是没有数据的,如果是true,说明有数据。
}
package com.lock;
public class SetThread implements Runnable{
private Student s;
private int x=0;
public SetThread(Student s) {
this.s=s;
}public SetThread() {
// TODO Auto-generated constructor stub
}
@Override
public void run() {
while(true) {
synchronized (s) {
if(s.flag) {
try {
s.wait(); //t1等着,释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(x%2==0) {
s.name="张三";
s.age=20;
}else {
s.name="马丽";
s.age=19;
}
x++;
//修改标记
s.flag=true;
//唤醒线程
s.notify();//唤醒t2,唤醒并不表示你立马可以执行,必须还得抢CPU的执行权。
}
}
}
}
package com.lock;
public class GetThread implements Runnable{
private Student s;
public GetThread(Student s) {
this.s=s;
}
@Override
public void run() {
while(true) {
synchronized (s) {
if(!s.flag) {
try {
s.wait(); //t2就等待了。立即释放锁。将来醒过来的时候,是从这里醒过来的时候
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//既然有,那我就消费掉
System.out.println(s.name+"---"+s.age);
//消费掉之后就没有了,所以我把标记改为false
s.flag=false;
//既然没有了,那么就唤醒线程(生产者)进行生产
s.notify(); //唤醒t1
}
}
}
}
3、模拟3个窗口售票(对今天线程学习的检验)
package com.ticket;
/*
* 如何解决线程安全问题呢?
*
* 要想解决问题,就要知道哪些原因会导致出问题:(而且这些原因也是以后我们判断一个程序是否会有线程安全问题的标准)
* A:是否是多线程环境
* B:是否有共享数据
* C:是否有多条语句操作共享数据
*
* 我们来回想一下我们的程序有没有上面的问题呢?
* A:是否是多线程环境 是
* B:是否有共享数据 是
* C:是否有多条语句操作共享数据 是
*
* 由此可见我们的程序出现问题是正常的,因为它满足出问题的条件。
* 接下来才是我们要想想如何解决问题呢?
* A和B的问题我们改变不了,我们只能想办法去把C改变一下。
* 思想:
* 把多条语句操作共享数据的代码给包成一个整体,让某个线程在执行的时候,别人不能来执行。
* 问题是我们不知道怎么包啊?其实我也不知道,但是Java给我们提供了:同步机制。
*
* 同步代码块:
* synchronized(对象){
* 需要同步的代码;
* }
*
* A:对象是什么呢?
* 我们可以随便创建一个对象试试。
* B:需要同步的代码是哪些呢?
* 把多条语句操作共享数据的代码的部分给包起来
*
* 注意:
* 同步可以解决安全问题的根本原因就在那个对象上。该对象如同锁的功能。
* 多个线程必须是同一把锁。
*/
public class Test {
public static void main(String[] args) {
TestTickets tt=new TestTickets();
Thread t1=new Thread(tt,"窗口1");
Thread t2=new Thread(tt,"窗口2");
Thread t3=new Thread(tt,"窗口3");
t1.start();
t2.start();
t3.start();
}
}
package com.ticket;
public class TestTickets implements Runnable{
//定义100张票
private int tickets=100;
//创建锁对象
private Object lock=new Object();
@Override
public void run() {
while(true) {
synchronized (lock) { //也可以用this,指的是当前对象
if(tickets>0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
int t=tickets--;
System.out.println(Thread.currentThread().getName()+"正在售卖第"+t+"张票");
}
}
}
}
}