特点:
单核CPU在任意时间点都只能运行一个进程
宏观是并行,微观串行
线程:又称轻量级进程(Light Weight Process)
程序中的一个顺序控制流程,同时也是CPU的基本调度单位
进程由多个线程组成,彼此完成不同的任务,交替执行,我们叫多线程
进程和线程的区别:进程是 *** 作系统 资源分配的基本单位,而线程是CPU的基本调度单位
一个程序运行后至少有一个进程
一个进程可以包括多个线程,但是最少需要一个线程
进程间不能共享数据段地址,但同进程的线程之间可以
线程组成CPU时间片: *** 作系统(OS)会为每个线程分配执行时间
运行数据
堆空间:储存线程需使用的对象,多个线程可以共享堆中的对象
栈空间:储存线程所需要的局部变量,每个线程都有独立的栈
创建线程JAVA中创建线程主要有两种方法
继承Thread类
实现Runnable接口
继承Thread类
步骤:
编写类,继承Thread
重写run方法
创建线程对象
调用start方法启动线程,(不能调用run方法)
package com.cs.test;
public class Test006 {
public static void main(String[] args) {
MyThread1 myThread1 = new MyThread1();
//启动线程
myThread1.start();
}
}
//继承Thread类
class MyThread1 extends Thread{
@Override
public void run() {
for (int i = 0; i <20 ; i++) {
System.out.println("子线程"+i);
}
}
}
获取线程名称
getName()
Thread.currentThread().getName()
package com.cs.test;
public class Test006 {
public static void main(String[] args) {
MyThread1 myThread1 = new MyThread1();
myThread1.start();
}
}
class MyThread1 extends Thread{
@Override
public void run() {
for (int i = 0; i <20 ; i++) {
System.out.println(Thread.currentThread().getName()+"吃饭了");
}
}
}
案例:
实现四个窗口卖火车票
package com.cs.test;
public class Test006 {
public static void main(String[] args) {
MyThread1 myThread1 = new MyThread1();
MyThread1 myThread2 = new MyThread1();
MyThread1 myThread3 = new MyThread1();
MyThread1 myThread4 = new MyThread1();
myThread1.start();
myThread2.start();
myThread3.start();
myThread4.start();
}
}
class MyThread1 extends Thread{
private static int count=200;
@Override
public void run() {
while (true){
if (count<=0){
break;
}
System.out.println(Thread.currentThread().getName()+"卖了第"+count+"张票");
count--;
}
}
}
实现Runnable接口:
步骤
编写类实现Runnable接口,并实现run方法
创建Runnable类实现对象
创建线程对象,传递实现类对象
启动线程
class MyThread1 implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() +);
}
}
package com.cs.test;
public class Test006 {
public static void main(String[] args) {
//创建对象
MyThread1 myThread1 = new MyThread1();
Thread thread = new Thread(myThread1,"我的线程");
thread.start();
for(int i=0;i<50;i++) {
System.out.println("main............"+i);
}
}
}
线程状态:
新建,就绪,运行,终止
常用方法:
Thread.sleep:让线程进入休眠
Thread.yield;当前线程主动放弃时间片,回到就绪状态,竞争下一个时间片
Thread.join:允许线程到其他线程中去
Thread.setPriority(int):线程优先级为1~10,默认5.,优先级越高,获得CPU的几率越大
Thread.setDaemon(blooen):设置为守护线程有两种用户线程(前台线程),守护线程(后台线程)
线程状态(等待)
新建,就绪,运行,等待,终止
线程安全线程不安全:
当多线程并发访问临界资源时,如果破坏原子 *** 作,可能会造成数据不一致
临界资源:共享资源,一次允许一个线程使用,才可以保证其正确性
原子 *** 作:不可分割的多步 *** 作,被视作一个整体,其顺序和步骤都不可以被打乱或缺缺失
public class ThreadSafe {
private static int index=0;
public static void main(String[] args) throws Exception{
//创建数组
String[] s=new String[5];
//创建两个 *** 作
Runnable runnableA=new Runnable() {
@Override
public void run() {
//同步代码块
synchronized (s) {
s[index]="hello";
index++;
}
}
};
Runnable runnableB=new Runnable() {
@Override
public void run() {
synchronized (s) {
s[index]="world";
index++;
}
}
};
//创建两个线程对象
Thread a=new Thread(runnableA,"A");
Thread b=new Thread(runnableB,"B");
a.start();
b.start();
a.join();//加入线程
b.join();//加入线程
System.out.println(Arrays.toString(s));
}
同步代码块
语法
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--;
}
}
}
}
线程状态(阻塞)
新建,就绪,运行,阻塞,终止
同步方法:语法: synchronized 返回值类型 方法名称(形参列表){
//对当前对象(this)加锁 // 代码(原子 *** 作)
}
同步规则:
只有在调用包含同步代码块的方法,或者同步方法时,才需要对象额锁标记
如调用不包含同步代码块的方法,或普通方法时,就不需要锁标记,可以直接调用
JDK中线程安全的类:
-
StringBuffer
-
Vector
-
Hashtable 以上类中的公开方法,均为synchonized修饰的同步方法。
为什么需要线程池
如果有非常多的任务需要完成,且每个线程的执行时间不会太长,这样频繁的创造和销毁线程,
这样频繁的创建和销毁线程,消耗性能比较多,有了线程池就不用创建更多的线程了,毕竟线程可以重用了
线程池原理:
线程池用维护着一个队列,队列中保存这处于等待状态的线程,不用每次都创建新线程
线程池API:
Executor:线程池顶级接口
ExecutorService:线程池接口,可通过submit(Runnable task)提交任务代码
Executor工厂类:通过此类可获得一个线程池
newFixedThreadPool(int nThreads):获取固定数量的线程池,参数:指定线程池中线程数量
newCachedThreadPool():获得动态数量的线程池,如不够就创建新的,无上限
newSingThreadExecutor()创建单个线程的线程池,只有一个线程
newScheduledThreadPool():创建固定大小的线程池,可以延时或定时执行任务
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 class TestCallable {
public static void main(String[] args) throws Exception{
//功能需求:使用Callable实现1-100和
//1创建Callable对象
Callable callable=new Callable() {
@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 task=new FutureTask<>(callable);
//3创建线程
Thread thread=new Thread(task);
//4启动线程
thread.start();
//5获取结果(等待call执行完毕,才会返回)
Integer sum=task.get();
System.out.println("结果是:"+sum);
}
}
Runnable接口和Callable接口的区别
Callable接口中Call方法有返回值,Runnable接口中run方法没有返回值
Callable接口中Call方法有声明异常,Runnable接口中run方法没有声明异常
Future接口
Future接口表示将要执行完任务的结果
get()阻塞形式等待Future中的异步处理结束call()的返回值
public class TestFuture {
public static void main(String[] args) throws Exception{
//1创建线程池
ExecutorService es=Executors.newFixedThreadPool(1);
//2提交任务 Future:表示将要执行完任务的结果
Future future=es.submit(new Callable() {
@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();
}
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)