进程本质上是正在执行的程序。执行一个程序为一个进程。在Unix操作系统中,每个应用程序的执行都在操作系统的内核中登记一个进程的标志,操作系统根据分配的进程标志对应用程序的执行进行调度和系统资源的分配。每个进程都有自己的内存单元,进程之间是相互独立的,一个进程一般不允许访问其他进程的内存空间。因此,进程间通信十分困难。
.
线程是比进程更小的执行单位。如果将“进程”的概念一分为二,则进程中的系统资源,可以看出静态的对象;而程序代码的执行位置,可以看成动态对象,这个动态的部分就是线程。进程在执行过程中拥有独立的内存单元,而多个线程共享内存,线程之间的通信比较容易解决,从而极大地提高了程序的运行效率。
在基于线程的多任务环境中,最小的可调度代码单元是线程,这意味着单个程序可以同时执行两个或更多个任务。例如文本编辑器可以在打印的同时格式化文本,只要这两个动作是通过两个独立的线程执行的即可。
.
.
.
java中为每个线程都指定了优先级,优先级决定了相对于其他线程应当如何处理某个线程。线程优先级是一些整数,他们指定了一个线程相当于另一个线程的优先程度。但是不代表优先级越高就一定先被运行。优先级低只是意味着获得调度的概率低。并不是绝对先调用优先级高,后调用优先级低的线程。他们在Thread
中有三个成员变量:
默认情况下,线程的优先级为NORM_PRIORITY
。
用Thread中的setPriority(参数)
;
线程切换的一般规则:
线程自愿放弃控制。线程显式地放弃控制权、休眠或在I/O之前阻塞,都会出现这样的情况。在这种情况下,检查所有其他线程,线程中优先级别高的容易获得CPU的调度。
线程被优先级更高的线程取代。对于这种情况,没有放弃控制权的低优先级线程不管正在做什么,都会被优先级高的线程简单地取代。基本上,只要高优先级的线程希望运行,他就会取代低优先级线程,这被称为抢占式多任务处理
。
创建线程的两种方式:
1.实现Runnable接口,重写run方法。
public class TicketThread implements Runnable{ //继承Runnable接口
private static int tickets=5;
public boolean saleTicket() {
if(tickets>0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在售票,售出后的余票为 :"+--tickets);
return true;
}else {
return false;
}
}
@Override
public void run() { //重写run方法
while(true) {
try {
Thread.sleep(1000);
if(!saleTicket()) {
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
TicketThread t = new TicketThread(); //创建线程实体对象
for(int i=0;i<4;i++) {
new Thread(t,"售票机-"+1).start(); //将线程实体丢入Thread中启动
}
}
}
2.继承Thread类,重写run方法
public class SubThreadDemo extends Thread {
private String name;
public SubThreadDemo(String name) {
super(name);
this.name = name;
}
@Override
public void run() {
try {
for (int i = 5; i > 0; i--) {
System.out.println(name+"......"+i);
Thread.sleep(1300);
}
} catch (InterruptedException e) {
System.out.println("子线程异常。");
}
System.out.println(name+"线程退出。");
}
public static void main(String[] args) {
SubThreadDemo s1 =new SubThreadDemo("阿杰");
SubThreadDemo s2 =new SubThreadDemo("阿兰");
s1.start();
s2.start();
}
}
以上就是创建线程的两种方式,方式上尽量用实现Runnable
接口这种方式,因为java是单继承模式。如果继承了Thread
类,当开发中需要继承其他类时就不方便。
.
.
在多线程中,如果多个线程同时对一个数据进行操作,会出现数据不正确的现象,也称为线程不安全。例如:两个人同时对一个账户存取钱。一方刚存5000元,另一方又同时取5000。在程序中就会出现数据错误。为了使这种错误不发生。于是有了同步这个机制。让线程一个一个形成排队一样的去访问数据,这样就提高了安全性。他是使用synchronize
这个关键字进行控制。线程访问数据会先找到这个锁,然后把资源锁上,此时别的线程无法操作只能等待,待获得锁的线程执行完,再将锁释放,别的线程找到锁后继续操作资源。
synchronize 的三种使用方式:
synchronize(线程共享的对象){
代码体
}
这种方式比较灵活。
synchronize
public synchronize void dome(){
}
表示共享的对象是this
,并且同步代码块是整个方法体。
synchronize
public static synchronize void dome(){
}
表示找类锁,类锁永远只有一把,就算创建了一万了对象,类锁也只有一把。
.
.
java中的线程通信是有从Object类继承老的wait()
、notify()
、notifyAll()
这几个方法来实现线程间通信的,由于所有类都是从Object继承的,因此在任何类中够可以直接使用这些方法。
wait()
notify()
方法为止。notify()
wait()
的第一个线程。就像在餐馆有一个空位后,通知等待就餐中的第一位顾客可以入座的情况。notifyAll()
wait()
的所有线程,拥有最高优先级的线程,首先被唤醒并执行。相当于疫情期间学校把所有防护措施做好后,通知所有同学入学。public class Product {
private int counter = 1;
private String name = "产品";
private boolean bFlag = false;
public synchronized void produce(String name,int counter) {
try {
if(bFlag) //bFlag为true 的时候 不能生产,需要等待消费完
wait();
} catch (InterruptedException e) {
}
this.name = name;
this.counter = counter;
System.out.println("生产Product "+name+counter);
bFlag = true;
notify(); //唤醒消费
}
public synchronized void consume() {
try {
if(bFlag)
wait();
} catch (InterruptedException e) {
}
System.out.println("消费Product "+name+counter);
bFlag = false;
notify(); //唤醒生产
}
public static void main(String[] args) {
Product p = new Product();
new Thread(new Producer(p)).start();
new Thread(new Customer(p)).start();
}
}
class Producer implements Runnable{
Product product;
public Producer(Product obj) {
product = obj;
}
@Override
public void run() {
int i = 1;
while(i<=5){
product.produce("机器人", i++);
}
}
}
class Customer implements Runnable{
Product product;
public Customer(Product obj) {
product = obj;
}
@Override
public void run() {
int i = 1;
while(i<=5){ //此处对应前面生产的数量
i++;
product.consume();
}
}
}
这是一个信号灯法的案例。
.
.
.
.
可以通过调用Thread中的getState()
方法来获取当前的状态。他会返回Thread.State枚举值。枚举值共6个,代表着线程的状态。
值 | 状态 |
---|---|
BLOCKED |
线程因为正在等待需要的锁而挂起执行 |
NEW |
线程还没有开始运行 |
RUNNABLE |
线程要么当前正在执行,要么在获得CPU的访问权之后执行 |
TERMINATED |
线程已经完成执行 |
TIMED_WAITING |
线程挂起执行一段指定的时间,例如当调用sleep() 方法时就会处于这种状态。当调用wait() 或join() 方法的暂停版时,也会进入这种状态 |
WAITING |
县城因为等待某些动作而挂起执行。例如因为调用非暂停版的wait() 或join() 方法而等待时,会处于这种状态 |
学海无涯,回头是岸。