1.程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。
2.进程(process)是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。——生命周期
3.线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路径。
1).若一个进程同一时间并行执行多个线程,就是支持多线程的
2).线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小
3).一个进程中的多个线程共享相同的内存单元/内存地址空间它们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就会带来安全的隐患。
并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。
并发:一个CPU(采用时间片)同时执行多个任务。比如:秒杀、多个人做同一件事。
一个Java应用程序java.exe,其实至少有三个线程:main()主线程,gc()垃圾回收线程,异常处理线程。当然如果发生异常,会影响主线程。
1.继承于Thread类
步骤:
1).创建一个继承于Thread类的子类
2).重写Thread类的run()
3).创建Thread类的子类的对象
4).通过此对象调用start()
例子:
public class ThreadTest {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
for(int i=0;i<100;i++){
if(i%2!=0){
System.out.println(i);
}
}
System.out.println("hello");
}
}
class MyThread extends Thread{
public void run(){
for(int i=0;i<100;i++){
if(i%2==0){
System.out.println(i);
}
}
}
}
注意:
1).start方法的作用:启动当前线程、调用当前线程的run方法,如果代码试图不经过start来运行线程,将导致线程不启动,只是线性的执行程序
2).获取当前线程名称:Thread.currentThread().getName()
3).不可以让已经start()的线程再去执行start(),会报IllegalThreadStateException异常。
4).创建自定义匿名线程
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
new Thread(){
public void run(){
for(int i=0;i<100;i++){
if(i%2!=0){
System.out.println(i);
}
}
}
}
2.通过实现Runnable接口的方式实现
1)创建一个实现了Runnable接口的类
2)实现类去实现Runnale中的抽象方法run()
3)创建实现类的对象
4)将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
5)通过Thread类的对象调用start()
public class ThreadTest1 {
public static void main(String[] args) {
MyThread2 t1 = new MyThread2();
for(int i=0;i<10;i++){
new Thread(t1).start();
}
}
}
class MyThread2 implements Runnable{
int temp=500;
int test=0;
@Override
public void run() {
while(temp>0)
{
System.out.println(temp--+"********************************");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3.实现Callable接口(JDK5.0 新增线程创建方式)
与使用Runnable相比, Callable功能更强大些
1)相比run()方法,可以有返回值
2)方法可以抛出异常
3)支持泛型的返回值
4)需要借助FutureTask类,比如获取返回结果
创建步骤:
1)创建一个实现Callable的实现类,实现call方法
2)创建步骤1)创建的实例
3)将Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
4)将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
5)如果对返回值感兴趣,可以调用FutureTask的对象的get方法获取返回值。
class NumThread implements Callable<Integer>{
@Override
public Integer call() throws Exception {
int sum=0;
for (int i = 1; i <=100; i++) {
if(i%2==0){
System.out.println(i);
sum+=i;
}
}
return sum;
}
}
public class ThreadNew {
public static void main(String[] args) {
NumThread numThread=new NumThread();
FutureTask<Integer> futureTask=new FutureTask<Integer>(numThread);
Thread thread=new Thread(futureTask);
thread.start();
try {
System.out.println("总和:"+futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
4.使用线程池(JDK5.0 新增线程创建方式)
提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。
好处:
1)提高响应速度(减少了创建新线程的时间)
2)降低资源消耗(重复利用线程池中线程,不需要每次都创建)
3)便于线程管理
corePoolSize:核心池的大小
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务时最多保持多长时间后会终止
JDK 5.0起提供了线程池相关API:ExecutorService 和 Executors
1)Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池
Executors.newFixedThreadPool(n); 创建一个可重用固定线程数的线程池
Executors.newSingleThreadExecutor() :创建一个只有一个线程的线程池
Executors.newScheduledThreadPool(n):创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
使用步骤:
1)提供指定线程数量的线程池
2)执行指定的线程的操作,需要提供实现Runnable接口或Callable接口实现类的对象
3)关闭线程池
public class ThreadPool {
public static void main(String[] args) {
ExecutorService service= Executors.newFixedThreadPool(10);
ThreadPoolExecutor service1=(ThreadPoolExecutor)service;
service1.setCorePoolSize(100);
service.execute(new NumberThread());//适用于Runnable
service.submit(new NumThread());//适用于Callable
service.shutdown();
}
}
class NumberThread implements Runnable{
@Override
public void run() {
for (int i = 0; i <100 ; i++) {
if(i%2==0){
System.out.println(Thread.currentThread().getName()+i);
}
}
}
}
Thread():创建新的Thread对象
Thread(String threadname):创建线程并指定线程实例名
Thread(Runnable target):指定创建线程的目标对象,它实现了Runnable接口中的run方法
Thread(Runnable target, String name):创建新的Thread对象
1.void start(): 启动线程,并执行对象的run()方法
2.run(): 线程在被调度时执行的操作,该方法不能抛异常,只能用try-catch进行处理,因为父类没有抛出异常
3.String getName(): 返回线程的名称
4.void setName(String name):设置该线程名称
5.static Thread currentThread(): 返回当前线程。在Thread子类中就是this,通常用于主线程和Runnable实现类
6.yield():释放当前CPU执行权
7.join():当某个程序执行流中调用其他线程的 join() 方法时,调用线程将被阻塞,直到 join() 方法加入的 join线程执行完为止低优先级的线程也可以获得执行
8.stop(): 强制线程生命期结束,不推荐使用
9.static void sleep(long millis):(指定时间:毫秒)
令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后重排队
10.boolean isAlive():返回boolean,判断线程是否还活着
public class ThreadTest {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
thread.setName("name2");
for(int i=0;i<100;i++){
if(i%2!=0) {
System.out.println(Thread.currentThread().getName() + i);
}
try {
thread.join();
} catch (InterruptedException e) {
e.getMessage();
}
}
}
}
class MyThread extends Thread{
public MyThread() {
}
public MyThread(String name) {
super(name);
}
public void run(){
for(int i=0;i<100;i++){
if(i%2==0){
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+i);
}
if(i%20==0){
yield();
}
}
}
}
1.时间片
2.抢占式
1.同优先级线程组成先进先出队列(先到先服务),使用时间片策略
2.对高优先级,使用优先调度的抢占式策略
Thread中定义的两个方法
public final int getPriority();
public final void setPriority(int newPriority);
getPriority():返回线程优先值
setPriority(int newPriority):改变线程的优先级别
线程的优先等级
MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5//默认优先级
原因:1. 实现的方式没有类的单继承性的局限性
2. 实现的方式更适合来处理多个线程有共享数据的情况。
联系:public class Thread implements Runnable
相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。
一种是守护线程:Java垃圾回收就是一个典型的守护线程。守护线程是用来服务用户线程的,通过在start()方法前调用
thread.setDaemon(true)可以把一个用户线程变成一个守护线程。若JVM中都是守护线程,当前JVM将退出。
一种是用户线程:它们在几乎每个方面都是相同的,唯一的区别是判断JVM何时离开。
JDK中用Thread.State类定义了线程的6种状态
在它的一个完整的生命周期中通常要经历如下的五种状态:
多个线程执行的不确定性引起执行结果的不稳定,多个线程对账本的共享,会造成操作的不完整性,会破坏数据。
可以通过线程同步机制来解决线程安全问题。
方式一、同步代码块
方式二、同步方法
方式三、Lock锁,JDK5.0之后
public class ThreadTest1 {
public static void main(String[] args) {
MyThread2 t1 = new MyThread2();
for(int i=0;i<10;i++){
new Thread(t1).start();
}
}
}
class MyThread2 implements Runnable{
int temp=500;
int test=0;
Object obj=new Object();
@Override
public void run() {
while(temp>0)
{
synchronized(MyThread2.class){System.out.println(temp--+"********************************");}
//synchronized(obj){System.out.println(temp--+"********************************");}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadTest1 {
public static void main(String[] args) {
MyThread2 t1 = new MyThread2();
for(int i=0;i<10;i++){
new Thread(t1).start();
}
}
}
class MyThread2 implements Runnable{
int temp=500;
int test=0;
Object obj=new Object();
@Override
public void run() {
while(temp>0)
{
show();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void show(){
System.out.println(temp--+"********************************");
}
}
样例2://同步监视器使用static为类
public class ThreadTest {
public static void main(String[] args) {
for(int i=0;i<10;i++){
new MyThread().start();
}
}
}
class MyThread extends Thread{
static int x=500;
public MyThread() {
}
public MyThread(String name) {
super(name);
}
public void run(){
while(x>0)
{
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
show();
}
}
private static synchronized void show(){
System.out.println(x--+"********************************");
}
}
线程安全的懒汉式:
class Single{
private static Single single=null;
private Single(){
}
public static Single getSingleInstance() {
if(single==null){
synchronized (Single.class){
if(single==null){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
single=new Single();
}
}
}
return single;
}
}
3.Lock锁,JDK5.0之后
样例代码:
public class LockTest {
public static void main(String[] args) {
Window window = new Window();
new Thread(window).start();
new Thread(window).start();
new Thread(window).start();
new Thread(window).start();
}
}
class Window implements Runnable{
private int ticket=100;
private ReentrantLock lock=new ReentrantLock();
@Override
public void run() {
while(true){
lock.lock();
if(ticket>0){
try {
System.out.println(Thread.currentThread().getName()+":售票,票号为"+ticket--);
}finally {
lock.unlock();
}
}else{
break;
}
}
}
}
1).不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
2).出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续
1.解决方法
1)专门的算法、原则
2)尽量减少同步资源的定义
3)尽量避免嵌套同步
相同:两者都可以解决线程安全问题
不同:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
Lock需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unLock())
public class AccountTest {
public static void main(String[] args) {
Account account=new Account(0);
Customer c1=new Customer(account);
c1.setMoney(1000);
c1.start();
Customer c2=new Customer(account);
c2.setMoney(1000);
c2.start();
}
}
class Customer extends Thread{
private Account account;
private double money;
public Customer(Account account) {
this.account = account;
}
public void setMoney(double money) {
this.money = money;
}
public void run(){
for(int i=0;i<3;i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (Customer.class){
account.withdrow(money);
System.out.println(this.getName()+account.getBalance());
}
}
}
}
class Account{
private double balance;
public double getBalance() {
return balance;
}
public Account(double balance) {
this.balance = balance;
}
public Account() {
}
public void withdrow(double balance){
this.balance=this.balance+balance;
}
}
使用两个线程打印 1-100。线程1, 线程2 交替打印
public class Communication {
public static void main(String[] args) {
MyThreadCommunication myThreadCommunication=new MyThreadCommunication();
Thread thread1=new Thread(myThreadCommunication);
Thread thread2=new Thread(myThreadCommunication);
thread1.setName("线程1");
thread2.setName("线程2");
thread1.start();
thread2.start();
}
}
class MyThreadCommunication implements Runnable {
int i=1;
public void run(){
while(true){
synchronized (this){
notify();
if(i<=100){
System.out.println(Thread.currentThread().getName()+":"+(i++));
}else break;
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
涉及到的三个方法说明:
1.wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器
2.notify():一旦执行此方法,就会唤醒被wait的一个线程,如果有多个线程被wait,就唤醒优先级别高的线程
3.notifyAll():唤醒所有被wait的线程。
三个方法必须使用在同步代码块或同步方法中
三个方法的调用者必须是同步代码块或同步方法中的同步监视器
三个方法都是定义在java.lang.Object类中
相同点:一旦执行方法,都可以使当前的线程进入阻塞
不同点:
1)两个方法声明的位置不同:Thread类中声明sleep(),Object类中声明wait();
2)调用的要求不同:sleep()可以在任何需要的场景下使用,wait()必须使用在同步代码块中
3)关于是否释放同步监听器,如果两个方法都是用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁。
public class ProductTest {
public static void main(String[] args) {
Clerk clerk=new Clerk();
Producer producer=new Producer(clerk);
producer.setName("生产者");
Customer2 customer=new Customer2(clerk);
customer.setName("消费者");
producer.start();
customer.start();
}
}
class Clerk{
private int productCount=0;
public synchronized void produceProduct() {
if(productCount<20){
productCount++;
System.out.println(Thread.currentThread().getName()+":开始生产"+productCount);
notify();
}else{
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void comsumeProduct() {
if(productCount>0)
{
System.out.println(Thread.currentThread().getName()+":开始消费"+productCount);
productCount--;
notify();
}else{
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Producer extends Thread{
private Clerk clerk;
public Producer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(getName()+":开始生产产品....");
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.produceProduct();
}
}
}
class Customer2 extends Thread{
private Clerk clerk;
public Customer2(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(getName()+":开始消费产品....");
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.comsumeProduct();
}
}
}