目录
一、进程与线程
进程是操作系统分配资源的基本单位,通过系统的分配获取系统资源,具有独立性、互斥性。
线程是CPU的基本调度单位。
线程特性:抢占式运行,CPU在执行的时候是按照时间片执行的,单位的时间片是相互抢占的;资源共享性:一个应用程序中的线程可以共享当前的资源。
区别:
进程相当于一个独立的应用程序。
线程是进程中最小的基本单位。
进程中至少有一个线程,所以进程的运行依托于线程,只有线程运行,进程才能执行。
二、并发与并行
并发(单核):在一个处理器上同时发生多个任务,轮流交替执行。
并行(多核):在多个处理器或者多核的处理器上同时处理多个不同的任务,是真正意义上的同时进行。
三、创建线程的三种方式
1、继承Thread类(无资源共享)
(1)定义Thread类的子类、重写该类的run方法,该run方法的方法体就代表线程要执行的任务。因此,run方法称为执行体。
(2)创建Thread子类的实例,即创建线程的对象。
(3)调用线程对象的start()方法来启动该线程。
public class Demo1 extends Thread{
int i = 0;
public static void main(String[] args) {
for(int i = 0;i< 100;i++){
System.out.println(Thread.currentThread().getName()+" : "+i);
if(i==20)
{
new Demo1().start();
}
}
}
//重写run方法,run方法的方法体就是现场执行体
public void run()
{
for(;i<100;i++){
System.out.println(getName()+" "+i);
}
}
}
2、通过Runnable接口创建线程(有资源共享)
(1)定义Runnable接口的实现类,重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
(2)创建 Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
(3)调用线程对象的start()方法来启动该线程。
public class RunnableThreadTest implements Runnable{
private int i;
public static void main(String[] args) {
for(int i = 0;i < 100;i++)
{
System.out.println(Thread.currentThread().getName()+" "+i);
if(i==20)
{
RunnableThreadTest rtt = new RunnableThreadTest();
new Thread(rtt,"新线程1").start();
new Thread(rtt,"新线程2").start();
}
}
}
public void run()
{
for(i = 0;i <100;i++)
{
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}
3、通过Callable和Future创建线程
(1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。(FutureTask是一个包装器,它通过接受Callable来创建,它同时实现了Future和Runnable接口。)
(3)使用FutureTask对象作为Thread对象的target创建并启动新线程。
(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
public class CallableThreadTest implements Callable<Integer>{
public static void main(String[] args) {
CallableThreadTest ctt = new CallableThreadTest();
FutureTask<Integer> ft = new FutureTask<>(ctt);
for(int i = 0;i < 100;i++)
{
System.out.println(Thread.currentThread().getName()+" 的循环变量i的值"+i);
if(i==20)
{
new Thread(ft,"有返回值的线程").start();
}
}
try
{
System.out.println("子线程的返回值:"+ft.get());
} catch (InterruptedException e)
{
e.printStackTrace();
} catch (ExecutionException e)
{
e.printStackTrace();
}
}
@Override
public Integer call() throws Exception {
// TODO Auto-generated method stub
int i = 0;
for(;i<100;i++)
{
System.out.println(Thread.currentThread().getName()+" "+i);
}
return i;
}
}
四、线程的方法
1、构造方法
Thread() //分配一个新的Thread对象
Thread(Runnable target, String name) //分配一个新的Thread对象,并对当前线程起个名字
2、成员方法
static Thread currentThread(); //获取当前线程对象
String getName(); //获取当前线程名字的
void setName(String name); //设置当前线程的名字
void setPriority(int newPriority); //设置当前线程的优先级。这个优先级只会增加线程执行的概率
int getPriority(); //获取当前线程优先级的
static void sleep(); //让线程休眠多少毫秒,通过睡眠控制线程抢占的顺序
五、线程同步
有两个或多个线程同时访问一个资源,为确保在任何时间点一个共享的资源只能被一个线程使用,使用了synchronized关键字实现“同步”;
当一个线程运行到需要同步的语句时,CPU不再去执行其他线程中的代码块,因为必须执行完当前代码块后才能去执行其他线程中的相关代码块,这就是线程同步。
1、线程同步的两种方法
(1)synchronized方法
synchronized void methodA() { }
(2)synchronized代码块
synchronized (Object) {
//要同步的代码块
}
(2)注意事项
受到synchronized保护的程序代码块和方法中,要访问的对象的属性必须为private类型,因为如果不设定为private,那么就可以用不同的方式来访问它,这样就达不到保护的效果了
(3)实例
public class Test {
public static void main(String[] args) {
SaleTicket saleTicket = new SaleTicket();
Thread thread1 = new Thread(saleTicket, "淘票票");
thread1.start();
Thread thread2 = new Thread(saleTicket, "美团");
thread2.start();
Thread thread3 = new Thread(saleTicket, "猫眼");
thread3.start();
}
}
class SaleTicket implements Runnable {
private static int ticket = 100;
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
synchronized (this) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName()+ "售出了第" + ticket + "张");
ticket--;
}else {
//tiket =0
System.out.println(Thread.currentThread().getName()+"卖完了");
break;//循环终止
}
}
}
}
}