程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念,而进程是程序在处理机上的一次执行过程,是个动态的概念。
进程是一个具有一定独立功能的程序,一个实体,每一个进程都有自己的地址空间。
进程执行的间断性决定了进程可能具有多种状态,事实上,运行中的进程具有以下三种状态:
线程实际上是在进程的基础上进一步划分,一个进程启动后。里面的若干程序又可以划分若干个线程。
线程:是进程的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行,一个进程最少有一个线程
并行:两个任务同时运行(多个CPU)
并发:就是将任务轮流执行,由于CPU时间片运行时间较短,就会感觉两个任务在同时执行。
class MyThread extends Thread{
public void run{
//逻辑处理
}
}
MyThread mt = new MyThread();
mt.start()
class MyRunnable implements Runnable{
public void run(){
//逻辑处理
}
}
MyRunnable mr = new MyRunnable();
Thread t = new Thread(mr);
t.start();
pulic static void sleep(long mills)
在当前线程的执行中,暂停指定的毫秒数,释放CPU的时间片
public final void setDaemon(boolean on)
将此线程标记为daemon线程或用户线程,当运行的唯一线程都是守护进程线程时,Java虚拟机将退出
参数为true时,线程为守护线程
public static void yield()
暂停当前正在执行的线程对象,并执行其他线程
void setPriority(int newPriority)
更改线程优先级
static int MAX_PRIORITY 最大优先级,抢到CPU时间片的概率大
static int MIN_PRIORITY 最小优先级
static int NORM_PRIORITY 默认优先级
package per.learn.multhread;
public class ThreadDemo2 {
public static void main(String[] args) {
MyRunnable2 mr = new MyRunnable2();
Thread tr = new Thread(mr);
tr.setDaemon(true);//设置为守护线程
tr.start();
for(int i =0;i<10;i++){
System.out.println("main--"+i);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class MyRunnable2 implements Runnable{
private long millis;
@Override
public void run() {
for(int i =0;i<10;i++){
System.out.println("--"+i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
OUT:
main--0
--0
main--1
main--2
--1
main--3
main--4
--2
main--5
main--6
main--7
--3
main--8
main--9
--4
main运行完,线程tr并未执行完。
public final void join() throws InterruptedException
等待这个线程死亡
调用此方法的行为方式与调用相同
join(0)
public void interrupt()
中断这个线程
除非当前线程中断自身,这是允许的
public static boolean interrupted()
测试当前线程是否中断,该方法会清除中断状态
join方法:
加入线程,将调用的线程执行指定时间或执行完毕,再执行其他
package per.learn.multhread;
public class ThreadDemo3 {
public static void main(String[] args) {
MyRunnable3 mr3 = new MyRunnable3();
Thread t = new Thread(mr3);
t.start();
for(int i =0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"--"+i);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(i==5){
try{
t.join();//让线程t执行完毕
}catch (InterruptedException e) {
e.printStackTrace();
}
//t.interrupt();//中断线程,只是做了中断标记,抛出异常,线程仍然会执行
}
}
}
}
class MyRunnable3 implements Runnable{
@Override
public void run() {
for(int i =0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"--"+i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Out:
main--0
Thread-0--0
main--1
main--2
Thread-0--1
main--3
main--4
Thread-0--2
main--5
Thread-0--3
Thread-0--4
Thread-0--5
Thread-0--6
Thread-0--7
Thread-0--8
Thread-0--9
main--6
main--7
main--8
main--9
package per.learn.multhread;
public class ThreadDemo3 {
public static void main(String[] args) {
MyRunnable3 mr3 = new MyRunnable3();
Thread t = new Thread(mr3);
t.start();
for(int i =0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"--"+i);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(i==5){
// try{
// t.join();//让线程t执行完毕
// }catch (InterruptedException e) {
// e.printStackTrace();
// }
t.interrupt();//中断线程,只是做了中断标记,抛出异常,线程仍然会执行
}
}
}
}
class MyRunnable3 implements Runnable{
@Override
public void run() {
for(int i =0;i<10;i++){
if(Thread.interrupted()){
break;
}
System.out.println(Thread.currentThread().getName()+"--"+i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace(); //会将中断状态清除
Thread.currentThread().interrupt();//将中断状态重新标记
}
}
}
}
main--0
Thread-0--0
main--1
main--2
Thread-0--1
main--3
main--4
main--5
Thread-0--2
main--6
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at per.learn.multhread.MyRunnable3.run(ThreadDemo3.java:35)
at java.lang.Thread.run(Thread.java:748)
main--7
main--8
main--9
同步是一种高开销的操作,因此应该尽量减少同步的内容。
通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。
synchronized(object){
要同步的操作
}
例:
package per.learn.multhread;
import sun.awt.windows.ThemeReader;
public class ThreadDemo4 {
public static void main(String[] args) {
MyRunnable5 mr5 = new MyRunnable5();
Thread t1 = new Thread(mr5);
Thread t2 = new Thread(mr5);
//模拟两个窗口
t1.start();
t2.start();
}
}
class MyRunnable5 implements Runnable{
private int ticket=10;//车票
private Object obj = new Object();
@Override
public void run() {
for(int i=0;i<300;i++){
synchronized (obj){
if(ticket > 0){
ticket--;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("您购买的车票已剩余"+ticket+"张");
}
}
}
}
}
public synchronized void method(){
要同步的操作
}
同步的方法是当前对象(this)
注: synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类
package per.learn.multhread;
import sun.awt.windows.ThemeReader;
public class ThreadDemo4 {
public static void main(String[] args) {
MyRunnable5 mr5 = new MyRunnable5();
Thread t1 = new Thread(mr5);
Thread t2 = new Thread(mr5);
//模拟两个窗口
t1.start();
t2.start();
}
}
class MyRunnable5 implements Runnable{
private int ticket=10;//车票
private Object obj = new Object();
private synchronized void method(){
if(ticket > 0){
ticket--;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("您购买的车票已剩余"+ticket+"张");
}
}
@Override
public void run() {
for(int i=0;i<300;i++){
method();
}
}
}
结构化更加方便,以上两种只能等同步方法或同步代码块执行完毕后才能释放锁。
ReentrantLock类是可重入、互斥、实现了Lock接口的锁,
它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力
ReenreantLock类的常用方法有:
ReentrantLock() : 创建一个ReentrantLock实例
lock() : 获得锁
unlock() : 释放锁
注:ReentrantLock()还有一个可以创建公平锁的构造方法,但由于能大幅度降低程序运行效率,不推荐使用
package per.learn.multhread;
import sun.awt.windows.ThemeReader;
import java.util.concurrent.locks.ReentrantLock;
public class ThreadDemo4 {
public static void main(String[] args) {
MyRunnable5 mr5 = new MyRunnable5();
Thread t1 = new Thread(mr5);
Thread t2 = new Thread(mr5);
//模拟两个窗口
t1.start();
t2.start();
}
}
class MyRunnable5 implements Runnable{
private int ticket=10;//车票
private Object obj = new Object();
ReentrantLock lock = new ReentrantLock();
private void method2(){
lock.lock();//锁
try{
if(ticket > 0){
ticket--;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("您购买的车票已剩余"+ticket+"张");
}
}finally {
lock.unlock();//释放锁
}
}
@Override
public void run() {
for(int i=0;i<300;i++){
method2();
}
}
}
注意:
LinkedBlockingQueue 类常用方法
当队列满时:
add()方法会抛出异常
offer()方法返回false
put()方法会阻塞
需要使用线程同步的根本原因在于对普通变量的操作不是原子的。
那么什么是原子操作呢?
原子操作就是指将读取变量值、修改变量值、保存变量值看成一个整体来操作
即-这几种行为要么同时完成,要么都不完成。
在java的util.concurrent.atomic包中提供了创建了原子类型变量的工具类,
使用该类可以简化线程同步。
其中AtomicInteger 表可以用原子方式更新int的值,可用在应用程序中(如以原子方式增加的计数器),
但不能用于替换Integer;可扩展Number,允许那些处理机遇数字类的工具和实用工具进行统一访问。
AtomicInteger类常用方法:
AtomicInteger(int initialValue) : 创建具有给定初始值的新的AtomicInteger
addAddGet(int dalta) : 以原子方式将给定值与当前值相加
get() : 获取当前值
过多的同步有可能出现死锁。
如:服务员需要等待厨师准备好食物,然后通知服务员上菜,然后回去继续等待,厨师代表生产者,服务员代表消费者。
package per.learn.multhread;
/**
* 两个线程协同工作,先生产,再消费
*/
public class ProducterCustomerDemo {
public static void main(String[] args) {
Food food = new Food();//共享food对象
Producter p = new Producter(food);
Customer c = new Customer(food);
//先生产,再消费
Thread t1 = new Thread(p);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
/**
* 消费者
*/
class Customer implements Runnable{
private Food food;
public Customer(Food food){
this.food =food;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
food.get();
}
}
}
/**
* 生产者
*/
class Producter implements Runnable{
private Food food;
public Producter(Food food){
this.food=food;
}
@Override
public void run() {
//模拟生产20份
for (int i = 0; i <20 ; i++) {
if(i%3==0){
food.set("锅包肉","酸甜口味");
}
else if(i%3==1){
food.set("佛跳墙","多辣");
}else{
food.set("小炒肉","多肉");
}
}
}
}
/*
共享
*/
class Food{
private String name;
private String describe;
private boolean flag = true;//true 表示生产,false表示消费
/**
* 生产产品
*/
public synchronized void set(String name,String describe){
if(!flag){
//表示不能生产,要释放cpu和锁
try {
this.wait();//线程进入等待状态,释放监视器所有权(对象锁)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.setName(name);
try {
Thread.sleep(1000); //模拟生产过程 产生时间差
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setDescribe(describe);
flag = false;
this.notify();//唤醒等待的线程(随机的其中一个)
}
/**
* 消费
*/
public synchronized void get(){
if(flag){
//表示不能消费
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.getName()+"->"+this.getDescribe());
flag = true;
this.notify();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescribe() {
return describe;
}
public void setDescribe(String describe) {
this.describe = describe;
}
@Override
public String toString() {
return "Food{" +
"name='" + name + '\'' +
", describe='" + describe + '\'' +
'}';
}
public Food(String name, String describe) {
this.name = name;
this.describe = describe;
}
public Food() {
}
}
线程池是预先创建线程的一种技术。线程池在还没有任务来之前,创建一定数量的线程,放入空闲队列中,然后对这些资源进行复用。减少频繁的创建和销毁对象。线程池接口是ExecutorService。
Executor接口:
执行已提交的Runnable任务对象
ExecutorService:
Executor提供了管理终止方法,以及可为追踪一个或多个异步任务执行状况而生成Future的方法。
Executors类:
此包中所定义的Executor、ExecutorService等的工厂和实用方法。
newSingleThreadExecutor:
创建单线程线程池。相当于单线程串行执行所有任务,如果这个唯一线程因为异常结束,那么会有一个新的线程替代它,此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
newFixedThreadPool:创建固定大小的线程池,每次提交一个任务就创建一个线程,直到达到线程池最大大小。
如果某个线程因为异常结束,就会补充一个新线程
newCachedThredPool: 创建一个可缓存的线程池,如果线程池的大小超过了处理任务所需要的线程,那么就会收回部分空闲(60s不执行任务)的线程,线程池不会对线程池大小做限制,依赖操作系统(或者说JVM)能创建的最大线程池大小。
newSchedeledThreadPool:创建一个大小无限的线程池,支持定时及周期性执行任务的需求。(需要指定初始大小)
package per.learn.multhread;
import java.util.concurrent.*;
public class ThreadDemo5 {
public static void main(String[] args) {
//创建线程池(4种)
//ExecutorService es = Executors.newSingleThreadExecutor();
//ExecutorService es = Executors.newFixedThreadPool(2);
ScheduledExecutorService es = Executors.newScheduledThreadPool(2);
//es.execute(new MyRunnable6());
//es.execute(new MyRunnable6());
es.schedule(new MyRunnable6(),3000, TimeUnit.MILLISECONDS);
es.shutdown();//结束
}
}
class MyRunnable6 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"---"+i);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
sleep() wait de 区别:
sleep:让线程进入休眠状态,让出CPU时间时间片,不释放对象锁
wait:让线程进入等待状态,让出CPU时间片,释放对象锁,等待其他线程通过notify()方法来唤醒,也可以设置等待时间,该线程再次竞争资源