进程:程序是静止的,只有真正运行时的程序,才被称为进程。
特点:
单核CPU在任何时间点上。
只能运行一个进程。
宏观并行、微观串行。
线程:又称轻量级进程。程序中的一个顺序控制流程,同时也是CPU的基本调度单位。进程由多个线程组成,彼此间完成不同的工作,交替执行,称为多线程。
线程和进程的区别
进程是操作系统资源分配的基本单位,而线程是CPU的基本调度单位。
一个程序运行后至少有一个进程。
一个进程可以包含多个线程,但是至少需要有一个线程。
进程间不能共享数据段地址,但同进程的线程之间可以。
线程的组成:
- CPU时间片:操作系统会为每个线程分配执行时间。
- 运行数据:
(1)堆空间:存储线程要使用的对象。多个线程可以共享堆中的对象。
(2)栈空间:存储线程需使用的局部变量,每个线程都拥有独立的栈。 - 线程的逻辑代码。
创建线程的方式:
1、继承Thread类
2、实现Runnable接口
3、通过线程池
4、实现Callable接口
一、继承Thread类
public class MyThread extends Thread {
public MyThread() {
// TODO Auto-generated constructor stub
}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for(int i=0;i<100;i++) {
System.out.println("子线程:"+i);
}
}
}
//this.getId获取线程Id || Thread.currentThread().getName()
//this.getName获取线程名称 || Thread.currentThread().getID()
public class TestThread {
public static void main(String[] args) {
//1创建线程对象
MyThread myThread=new MyThread();
myThread.start();//myThread.run()
//创建第二个线程对象
MyThread myThread2=new MyThread();
myThread2.start();
//主线程执行
for(int i=0;i<50;i++) {
System.out.println("主线程======"+i);
}
}
}
二、实现Runnable接口
java
public class MyRunnable implements Runnable{
@Override
public void run() {
for(int i=0;i<100;i++) {
System.out.println(
Thread.currentThread().getName()+" .."+i);
}
}
}
1、线程优先级为1-10,默认是5。线程级别越高,获取CPU机会越多。
2、线程对象被创建,即为初始状态,只在堆中开辟内存,与常规对象无异。
3、调用start()方法后,线程进入就绪状态。只有获取到时间片之后,才会进
入运行状态。线程结束,释放时间片。
4、线程状态:新建、就绪、运行、等待、终止。
//当前线程主动休眠 millis 毫秒。
public static void sleep(long millis)
//当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片。
public static void yield()
//允许其他线程加入到当前线程中
public final void join()
//线程优先级为1-10,默认为5,优先级越高,表示获取CPU机会越多。
public void setPriority(int)
线程安全问题:
当多线程并发访问临界资源时,如果破坏原子操作,可能会造成数据不一致。
临界资源:共享资源(同一对象),一次仅允许一个线程使用,才可保证其正确性。
原子操作:不可分割的多步操作,被视作一个整体,其顺序和步骤不可打乱或缺省。
同步代码块:
每个对象都有一个互斥锁标记,用来分配给线程的。
只有拥有对象互斥锁标记的线程,才能进入对该对象加锁的同步代码块。
线程退出同步代码块时,会释放相应的互斥锁标记。
synchronized(临界资源对象){
//代码原子操作
}
public class Ticket implements Runnable{
private int ticket=100;
//创建锁
//private Object obj=new Object();
@Override
public void run() {
while(true) {
synchronized (this) {//this ---当前对象
if(ticket<=0) {
break;
}
System.out.println(
Thread.currentThread().getName()+"卖了第"+
ticket+"票");
ticket--;
}
}
}
}
死锁:
当第一个线程拥有A对象锁标记,并等待B对象锁标记,同时第二个线程拥有B对象锁标记,并等待A对象锁标记时,产生死锁。
//死锁案例
//Main
public class TestDeadLock {
public static void main(String[] args) {
Boy boy=new Boy();
Girl girl=new Girl();
girl.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
boy.start();
}
}
//MyLock
public class MyLock {
//两个锁(两个筷子)
public static Object a=new Object();
public static Object b=new Object();
}
//BoyThread
public class Boy extends Thread{
@Override
public void run() {
synchronized (MyLock.a) {
System.out.println("男孩拿到了a");
synchronized (MyLock.b) {
System.out.println("男孩拿到了b");
System.out.println("男孩可以吃东西了...");
}
}
}
}
//GirlThred
public class Girl extends Thread {
@Override
public void run() {
synchronized (MyLock.b) {
System.out.println("女孩拿到了b");
synchronized (MyLock.a) {
System.out.println("女孩拿到了a");
System.out.println("女孩可以吃东西了...");
}
}
}
}
线程通信:
等待:
wait():wait会释放锁。sleep()是不释放锁的。
通知:
notify():随机通知一个在等待队列中的线程。
notifyAll():通知所有进入了等待队列的线程。
生产者消费者问题:
//Bread
public class Bread {
private int id;
private String productName;
public Bread() {
// TODO Auto-generated constructor stub
}
public Bread(int id, String productName) {
super();
this.id = id;
this.productName = productName;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
@Override
public String toString() {
return "Bread [id=" + id + ", productName=" + productName + "]";
}
}
//BreadCon
public class BreadCon {
//存放面包的数组
private Bread[] cons=new Bread[6];
//存放面包的位置
private int index=0;
//存放面包
public synchronized void input(Bread b) { //锁this
//判断容器有没有满
while(index>=6) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
cons[index]=b;
System.out.println(Thread.currentThread().getName()+"生产了"+b.getId()+"");
index++;
//唤醒
this.notifyAll();
}
//取出面包
public synchronized void output() {//锁this
while(index<=0) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
index--;
Bread b=cons[index];
System.out.println(Thread.currentThread().getName()+"消费了"+b.getId()+" 生产者:"+b.getProductName());
cons[index]=null;
//唤醒生产者
this.notifyAll();
}
}
//Consume
public class Consume implements Runnable{
private BreadCon con;
public Consume(BreadCon con) {
super();
this.con = con;
}
@Override
public void run() {
for(int i=0;i<30;i++) {
con.output();
}
}
}
//Product
public class Prodcut implements Runnable {
private BreadCon con;
public Prodcut(BreadCon con) {
super();
this.con = con;
}
@Override
public void run() {
for(int i=0;i<30;i++) {
con.input(new Bread(i, Thread.currentThread().getName()));
}
}
}
//Test
public class Test {
public static void main(String[] args) {
//容器
BreadCon con=new BreadCon();
//生产和消费
Prodcut prodcut=new Prodcut(con);
Consume consume=new Consume(con);
//创建线程对象
Thread chenchen=new Thread(prodcut, "晨晨");
Thread bingbing=new Thread(consume, "消费");
Thread mingming=new Thread(prodcut, "明明");
Thread lili=new Thread(consume, "莉莉");
//启动线程
chenchen.start();
bingbing.start();
mingming.start();
lili.start();
}
}
三、线程池创建线程:
1、频繁创建和销毁线程会比较耗性能。有了线程池就不要创建更多的线程来完成任务,因为线程可以重用。
2、线程池用维护者一个队列,队列中保存着处于等待(空闲)状态的线程。不用每次都创建新的线程。
public class TestThreadPool {
public static void main(String[] args) {
//1.1创建固定线程个数的线程池
//ExecutorService es=Executors.newFixedThreadPool(4);
//1.2创建缓存线程池,线程个数由任务个数决定
ExecutorService es=Executors.newCachedThreadPool();
//1.3创建单线程线程池
//Executors.newSingleThreadExecutor();
//1.4创建调度线程池 调度:周期、定时执行
//Executors.newScheduledThreadPool(corePoolSize)
Executors.newScheduledThreadPool(3);
//2创建任务
Runnable runnable=new Runnable() {
private int ticket=100;
@Override
public void run() {
while(true) {
if(ticket<=0) {
break;
}
System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"张票");
ticket--;
}
}
};
//3提交任务
for(int i=0;i<5;i++) {
es.submit(runnable);
}
//4关闭线程池
es.shutdown();//等待所有任务执行完毕 然后关闭线程池,不接受新任务。
}
}
四。Callable接口创建线程
public interface Callable< V >{
public V call() throws Exception;
}
public class TestCallable {
public static void main(String[] args) throws Exception{
//功能需求:使用Callable实现1-100和
//1创建Callable对象
Callable<Integer> callable=new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println(
Thread.currentThread().getName()+"开始计算");
int sum=0;
for(int i=1;i<=100;i++) {
sum+=i;
Thread.sleep(100);
}
return sum;
}
};
//2把Callable对象 转成可执行任务
FutureTask<Integer> task=new FutureTask<>(callable);
//3创建线程
Thread thread=new Thread(task);
//4启动线程
thread.start();
//5获取结果(等待call执行完毕,才会返回)
Integer sum=task.get();
System.out.println("结果是:"+sum);
}
}
Future接口:
Future接口表示将要执行完任务的结果。
get()以阻塞形式等待Future中的异步处理结果(call()的返回值)。
//案例 计算1-100的值
public class TestFuture {
public static void main(String[] args) throws Exception{
//1创建线程池
ExecutorService es=Executors.newFixedThreadPool(1);
//2提交任务 Future:表示将要执行完任务的结果
Future<Integer> future=es.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName()+"开始计算");
int sum=0;
for(int i=1;i<=100;i++) {
sum+=i;
Thread.sleep(10);
}
return sum;
}
});
//3获取任务结果,等待任务执行完毕才会返回.
System.out.println(future.get());
//4关闭线程池
es.shutdown();
}
}
Lock接口: