一、线程的概念
进程是正在执行的程序。在操作系统中进程是进行系统资源分配、调度和管理的最小单位,进程在执行过程中拥有独立的内存单元。比如:Windows采用进程作为最小隔离单位,每个进程有属于自己的数据段、程序段 ,并且与别的进程没有任何关系。
一个或更多的线程构成了一个进程(操作系统是以进程为单位的,而进程是以线程为单位的,进程中必须有一个主线程)。
为了解决进程调度资源的浪费,为了能够共享资源,出现了线程。线程是CPU调度和分派的基本单位,它可与同属一个进程的其他的线程共享进程所拥有的全部资源,多个线程共享内存,从而极大地提高了程序的运行效率。
如果一个进程没有了,那么线程肯定会消失,如果线程消失了,但是进程未必会消失。而且所有线程都是在进程的基础之上并同时运行。
二、创建线程的两种方式及区别
1 继承Thread类
1、1 示例代码
package com.study.test;
//创建线程的两种方式:继承Thread
//必须明确的覆写Thread类中的run方法,此方法为线程的主体
class AA extends Thread {
public void run() {
while (true) {
System.out.println("world");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class MyThread {
// main函数为主线程,运行时先运行主线程,再运行其他线程
public static void main(String[] args) throws InterruptedException {
AA a1 = new AA();
a1.start();// 必须先start线程,不能直接调用线程里面的函数,否则归属为主线程
while (true) {
System.out.println("hello");
Thread.sleep(2000);// 让线程等待2s,先让其他进程运行,2s后再运行
}
}
}
View Code
2 实现Runnable接口
2、1 定义一个类,实现Runnable接口,并覆盖 run()方法,在这个方法里是我们希望这个线程运行的代码。
2、2 创建一个这个新类的对象。
2、3 创建一个Thread类的对象,用刚才的Runnable对象作为构造函数参数。
2、4 调用Thread对象的start()方法来启动线程。
2、5 示例代码
package com.study.test;
//创建线程的两种方式:实现Runnable接口
//必须明确的覆写Thread类中的run方法,此方法为线程的主体
class AA implements Runnable {
public void run() {
while (true) {
System.out.println("world");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class MyThread {
// main函数为主线程,运行时先运行主线程,再运行其他线程
public static void main(String[] args) throws InterruptedException {
AA a1 = new AA();// 创建一个这个新类的对象。
Thread t1 = new Thread(a1);// 创建一个Thread类的对象,用刚才的Runnable对象作为构造函数参数。
t1.start();// 必须先start线程,不能直接调用线程里面的函数,否则归属为主线程
while (true) {
System.out.println("hello");
Thread.sleep(2000);// 让线程等待2s,先让其他进程运行,2s后再运行
}
}
}
View Code
3、实现Runnable接口与继承Thread的区别
3、1 实现Runnable 接口比继承 Thread 类有如下的明显优点:
(1)、适合多个相同程序代码的线程去处理同一个资源。
(2)、可以避免由于单继承局限所带来的影响。
(3)、增强了程序的健壮性,代码能够被多个线程共享,代码与数据是独立的。
3、2 Thread更加简单易用
三、线程的生命周期
多线程在操作过程也有一个固定的操作状态,任何一个线程都具有五种状态:创建、就绪、运行、阻塞、终止
创建状态:准备好的一个多线程的对象
就绪状态:调用了start()方法,等待cpu调度
运行状态:执行run()方法。
阻塞状态:暂停线程,可以将资源交给其他资源使用
终止状态:线程执行完毕,不再使用。
四、线程同步加锁解决资源竟态问题
1、 竞态的概念
1、1 多个线程调用同一资源就会产生竞争状态(竞态)
1、2 访问的同一资源,我们称之为临界资源
1、3 访问临界资源的代码,我们称之为临界区
1、4 如果两个线程同时取得数据,将会造成流水号重复
1、5 示例代码
class CThread extends Thread
{
public static int i=1; //相当于全局变量 临界资源
//临界区
public void run()
{
while(true)
{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}//模拟做一些事情
out.println(CThread.i);//取得数据
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
CThread.i++;//流水号加1
if (CThread.i==100) break;
}
}
}
public class Hello1 {
public static void main(String[] args) throws InterruptedException {
//多个线程(t1,t2)同时访问全局变量i;
Thread t1=new CThread();
t1.start();
Thread t2=new CThread();
t2.start();
}
}
View Code
2、 解决方法是利用同步加锁
2、1 多个线程访问同一资源,线程之间相互协调的功能,叫线程同步。也就是说在同一个时间段内只能有一个线程进行,其他线程要等待此线程完毕之后才可以继续执行。
2、2 在临界资源上放置同步块synchronized()可以解决这个问题
2、3 线程执行到synchronized()代码块时首先看看有没有锁定,如果没有锁定,则获得锁,如果已经锁定,则需要等待,线程离开synchronized()代码块的时候释放锁。
2、4 这一切的过程由系统任意给定的一个对象来监视,这个对象放入synchronized块的()中
2、5 示例代码
package com.study.test;
import static java.lang.System.*;
//利用同步加锁的方式解决竟态(多个线程竟争同一资源)问题
class CThread extends Thread {
public static int i = 1; // 相当于全局变量
public static Object j = new Object();// 随便一个对象用于监视
public void run() {
while (true) {
synchronized (j) // 对对象加锁,这里用的是任意一个对象,当一个对象进来的时候看有没有对象正在使用,如果有就等待,没有的话就使用并加锁,使用完以后进行解锁
{ // 对临界区加锁实现同步
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} // 模拟做一些事情
out.println(CThread.i);// 取得数据
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
CThread.i++;// 流水好加1
if (CThread.i == 100)
break;
} // 解锁
}
}
}
public class MyThread {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new CThread();
t1.start();
Thread t2 = new CThread();
t2.start();
}
}
View Code
五、线程安全类
1 把一个类中的方法标志为synchronized,我们称之为该类线程安全,多个线程访问同一个对象的内部数据将会采用线程同步
package com.study.test;
import static java.lang.System.out;
//把一个类中的方法标志为synchronized,我们称之为该类线程安全,多个线程访问同一个对象的内部数据将会采用线程同步
class dd// 线程安全类
{
public int i = 1;
public void run() {
while (true) {
synchronized (this) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} // 模拟做一些事情
out.println(i);// 取得数据
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
i++;// 流水好加1
if (i == 100)
break;
}
}
}
}
class CThread extends Thread {
// public static int i=1; //相当于全局变量
public static dd j = new dd();// 随便一个对象用于监视
public void run() {
j.run();
}
}
public class MyThread {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new CThread();
t1.start();
Thread t2 = new CThread();
t2.start();
}
}
View Code
六、Notify()与notifyall()区别
1、 使用notify(),通知第一个等待线程,所有排队的线程按先后顺序执行。
2、 notifyall()通知所有的等待线程,那么优先级别高线程就先执行。
七、yeild()与sleep()的区别
1、 yield:发杨雷锋主义精神,主动暂停当前正在执行的线程对象,并执行其他线程。 将执行的权力交给其它线程,自己到队列的最后等待。
2、 Thread.yield()线程是从Running状态到Ready状态。
3、 Thread.sleep()调用会给较低优先级线程一个运行的机会。
4、 Thread.yield()方法只会给相同优先级线程一个执行的机会。
八、join()
在一个线程A里面加入另一个线程B运行,等待B线程运行完以后再运行A线程
九、wait notify 的应用生产者消费者问题
package com.study.test;
import static java.lang.System.in;
import static java.lang.System.out;
import java.io.IOException;
import java.util.Scanner;
class Product {
public static Object monitor = new Object();// 监视
public static String flowNo = null;// 流水号
}
class Customer extends Thread {
Customer(String name) {
super(name);
}
public void run() {
// while(true)
// {
synchronized (Product.monitor) {
out.println("now the thread:" + Thread.currentThread().getName() + " get lock");
try {
out.println("now the thread:" + Thread.currentThread().getName() + " is waiting");
Product.monitor.wait(); // wait的同时会解开锁给其他线程
out.println(Product.flowNo);
} catch (Exception e) {
out.println(e.getMessage());
}
}
// }
}
}
class Producter extends Thread {
Producter(String name) {
super(name);
}
public void run() {
// while(true)
// {
synchronized (Product.monitor) {
out.println("now the thread:" + Thread.currentThread().getName() + " get lock");
// 生产产品
Scanner sc = new Scanner(in);
Product.flowNo = sc.nextLine();
out.println("now notify all thread to work");
Product.monitor.notifyAll();// 通知所有的线程开始运行
}
// }
}
}
public class MyThread {
public static void main(String[] args) throws InterruptedException, IOException {
Customer t1 = new Customer("customer");
t1.start();
Producter p1 = new Producter("producter");
p1.start();
}
}
View Code
十、线程池以及使用线程池的好处
1、多线程运行时间,系统不断的启动和关闭新线程,成本非常高
2、使用线程池可以很好地提高性能,线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动一条线程来执行这个任务,执行结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个任务。
package com.study.test;
import static java.lang.System.out;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class CThread extends Thread
{
public static int i=1; //相当于全局变量
public static Object j=new Object();//随便一个对象用于监视
public void run()
{
while(true)
{
synchronized(j) //对对象加锁,这里用的是任意一个对象
{ //对临界区加锁实现同步
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}//模拟做一些事情
out.println(CThread.i);//取得数据
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
CThread.i++;//流水好加1
if (CThread.i==100) break;
}//解锁
}
}
}
public class MyThread {
public static void main(String[] args) throws InterruptedException {
// Thread t1=new CThread();
// t1.start();
// Thread t2=new CThread();
// t2.start();
//现在不要用单个线程去跑,当然这个是服务器端一定干的事,最好使用线程池
//使用线程池的好处:
// 1、多线程运行时间,系统不断的启动和关闭新线程,成本非常高
// 2、使用线程池可以很好地提高性能,线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,
// 线程池就会启动一条线程来执行这个任务,执行结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个任务。
// 调用Executors类的静态工厂方法创建一个ExecutorService对象。
ExecutorService server=Executors.newFixedThreadPool(2);//创建两个线程的池
// 创建Runnable实现类,作为线程执行任务
// 调用ExecutorService对象的submit方法来提交Runnable实例。
server.submit(new CThread());//提交线程到线程池去处理
server.submit(new CThread());
// 当不想提交任何任务时调用ExecutorService对象的shutdown方法来关闭线程池。
server.shutdown();//关闭线程池
}
}
View Code
十一、定时调度
1、 定时调度的本质是一个线程,只是系统控制了这个线程运行的时间与频率
package com.study.test;
import static java.lang.System.out;
import static java.lang.System.in;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
class aa extends TimerTask {
@Override
public void run() {
out.println("1 ");
// 定时调度的本质是一个线程,只是系统控制了这个线程运行的时间与频率
out.println(Thread.currentThread().getName());
}
}
public class MyTimer {
public static void main(String[] args) throws InterruptedException, IOException {
Timer t1 = new Timer();
t1.schedule(new aa(), 0, 1000); // 利用cpu的时钟去控制的
in.read();//输入任意一个值回车时种植执行调度
t1.cancel();//表示终止定时器
}
}
View Code
2、 schedule到达某一个时间点开始执行
package com.study.test;
import static java.lang.System.out;
import static java.lang.System.in;
import java.io.IOException;
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;
class aa extends TimerTask {
@Override
public void run() {
out.println("1 ");
// 定时调度的本质是一个线程,只是系统控制了这个线程运行的时间与频率
out.println(Thread.currentThread().getName());
}
}
public class MyTimer {
public static void main(String[] args) throws InterruptedException, IOException {
Timer t1 = new Timer();
aa aa1 = new aa();
Calendar c1 = Calendar.getInstance();
c1.add(Calendar.SECOND, 5);
c1.set(2017, 10, 8, 00, 29, 10);
t1.schedule(aa1, c1.getTime());// 到了某一个时间点开始执行
}
}
View Code
3、 到达某一个时间点,再按照频率执行
package com.study.test;
import static java.lang.System.out;
import static java.lang.System.in;
import java.io.IOException;
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;
class aa extends TimerTask {
@Override
public void run() {
out.println("1 ");
// 定时调度的本质是一个线程,只是系统控制了这个线程运行的时间与频率
out.println(Thread.currentThread().getName());
}
}
public class MyTimer {
public static void main(String[] args) throws InterruptedException, IOException {
Timer t1 = new Timer();
aa aa1 = new aa();
Calendar c1 = Calendar.getInstance();
c1.add(Calendar.SECOND, 5);
c1.set(2017, 10, 8, 00, 35, 00);
// t1.schedule(aa1, c1.getTime());// 到了某一个时间点开始执行
t1.schedule(aa1, c1.getTime(), 1000);// 到了某一个时间点开始,按照1秒的频率
}
}
View Code
4、 延误时间间隔之后执行
package com.study.test;
import static java.lang.System.out;
import static java.lang.System.in;
import java.io.IOException;
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;
class aa extends TimerTask {
@Override
public void run() {
out.println("1 ");
// 定时调度的本质是一个线程,只是系统控制了这个线程运行的时间与频率
out.println(Thread.currentThread().getName());
}
}
public class MyTimer {
public static void main(String[] args) throws InterruptedException, IOException {
Timer t1 = new Timer();
aa aa1 = new aa();
Calendar c1 = Calendar.getInstance();
c1.add(Calendar.SECOND, 5);
c1.set(2017, 10, 8, 00, 35, 00);
// t1.schedule(aa1, c1.getTime());// 到了某一个时间点开始执行
// t1.schedule(aa1, c1.getTime(), 1000);// 到了某一个时间点开始,按照1秒的频率
t1.schedule(aa1, 2000, 1000);// 延误多少时间之后,按照1秒的频率
}
}
View Code
5、 固定频率
package com.study.test;
import static java.lang.System.out;
import static java.lang.System.in;
import java.io.IOException;
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;
class aa extends TimerTask {
@Override
public void run() {
out.println("1 ");
// 定时调度的本质是一个线程,只是系统控制了这个线程运行的时间与频率
out.println(Thread.currentThread().getName());
}
}
public class MyTimer {
public static void main(String[] args) throws InterruptedException, IOException {
Timer t1 = new Timer();
aa aa1 = new aa();
Calendar c1 = Calendar.getInstance();
c1.add(Calendar.SECOND, 5);
c1.set(2017, 10, 8, 00, 35, 00);
// t1.schedule(aa1, c1.getTime());// 到了某一个时间点开始执行
// t1.schedule(aa1, c1.getTime(), 1000);// 到了某一个时间点开始,按照1秒的频率
// t1.schedule(aa1, 2000, 1000);// 延误多少时间之后,按照1秒的频率
// t1.schedule(aa1,c1.getTime(),5000 );//过去的时间以当前时间为准,然后按照频率运行
t1.scheduleAtFixedRate(aa1, c1.getTime(), 5000);// 一定要补足,以固定频率为准
}
}
View Code
6、 多定时器,多任务
package com.study.test;
import static java.lang.System.out;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
class aa extends TimerTask {
@Override
public void run() {
out.println("1 ");
}
}
class bb extends TimerTask {
@Override
public void run() {
// TODO Auto-generated method stub
out.println("2 ");
}
}
class cc extends TimerTask {
@Override
public void run() {
SimpleDateFormat sf = new SimpleDateFormat("yyy.MM.dd hh:mm:ss");
String curDate = sf.format(new Date(this.scheduledExecutionTime()));
out.println(curDate + " 3 ");
}
}
public class MyTimer {
public static void main(String[] args) throws InterruptedException, IOException {
Timer t1 = new Timer();
aa aa1 = new aa();
bb bb1 = new bb();
t1.scheduleAtFixedRate(aa1, 0, 2000);// 加入一个任务
t1.scheduleAtFixedRate(bb1, 0, 1000);// 加入一个任务
Timer t2 = new Timer();
cc cc1 = new cc();
t2.scheduleAtFixedRate(cc1, 0, 3000);// 另外一个定时器加入任务
}
}
View Code