进程:是一个正在执行中的程序。
每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。
线程:就是进程中的一个独立的控制单元。
线程在控制着进程的执行。
一个进程中至少有一个线程。
cpu在做着快速的切换,以达到看上去是同时运行的效果。
我们可以形象把多线程的运行行为在互相抢夺cpu的执行权。
这就是多线程的一个特性:随机性。谁抢到谁执行,至于执行多长,cpu说的算。
创建线程的第一种方式:继承Thread类。
步骤:
1,定义类继承Thread。
2,复写Thread类中的run方法。
目的:将自定义代码存储在run方法。让线程运行。
3,调用线程的start方法,
该方法两个作用:启动线程,调用run方法。
public class ThreadDemo {
public static void main(String[] args) {
ThreadC c1 = new ThreadC();
ThreadC c2 = new ThreadC();
ThreadC c3 = new ThreadC();
ThreadC c4 = new ThreadC();
c1.start();
c2.start();
c3.start();
c4.start();
}
}
class ThreadC extends Thread{
@Override
public void run() {
System.out.println("线程:"+Thread.currentThread().getName()+"执行");
}
}
创建线程的第二种方式:实现Runable接口
步骤:
1,定义类实现Runnable接口
2,覆盖Runnable接口中的run方法。
将线程要运行的代码存放在该run方法中。
3,通过Thread类建立线程对象。
4,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
为什么要将Runnable接口的子类对象传递给Thread的构造函数。
因为,自定义的run方法所属的对象是Runnable接口的子类对象。
所以要让线程去指定指定对象的run方法。就必须明确该run方法所属对象。
5,调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
public class ThreadDemo {
public static void main(String[] args) {
ThreadC c = new ThreadC();
Thread t1 = new Thread(c);
Thread t2 = new Thread(c);
Thread t3 = new Thread(c);
Thread t4 = new Thread(c);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class ThreadC implements Runnable{
@Override
public void run() {
System.out.println("线程:"+Thread.currentThread().getName()+"执行");
}
}
多线程复制文件
public class MyThread implements Runnable{
//线程编号
private int threadNo;
//输出地址
private String outPos;
//拷贝文件地址
private String inPos;
//拷贝开始位置
private long start;
//拷贝结束位置
private long end;
private InputStream inputStream;
private RandomAccessFile outputStream;
public MyThread(int threadNo, String inPos, String outPos, long start, long end) {
this.threadNo = threadNo;
this.inPos = inPos;
this.outPos = outPos;
this.start = start;
this.end = end;
}
@Override
public void run() {
try {
Date startTime = new Date();
System.out.println("线程"+threadNo+":"+Thread.currentThread().getName()+" 开始下载==="+"时间:"+startTime+"====开始位置:"+start+"=====结束位置:"+end);
File inFile = new File(inPos);
File outFile = new File(outPos);
inputStream = new FileInputStream(inFile);
outputStream = new RandomAccessFile(outFile,"rw");
//指定从哪里开始读
inputStream.skip(start);
//指定从哪里开始写
outputStream.seek(start);
int a =0;
//读的文件大小
long sumbyte = 0;
byte[] b = new byte[1024];
while ((a=inputStream.read(b))!=-1&& sumbyte<=(end-start)) {
outputStream.write(b, 0, a);
sumbyte+=a;
}
Date stopTime = new Date();
System.out.println("线程"+threadNo+":"+Thread.currentThread().getName()+" 下载结束===="+"结束时间:"+stopTime+"====用时:"+(stopTime.getTime()-startTime.getTime())+"");
} catch (Exception e) {
e.printStackTrace();
}
finally{
try {
inputStream.close();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public class TestMyThread {
public static void main(String[] args) {
try {
String in = "D://1.exe";
String out = "E://1.exe";
int threadNum = 4;
File file = new File(in);
InputStream inputStream = new FileInputStream(file);
long size = file.length()/threadNum;
long start;
long end;
for (int i = 0; i < threadNum; i++) {
start = i*size;
end = start+size;
MyThread myThread = new MyThread(i,in,out,start,end);
Thread thread = new Thread(myThread);
thread.start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
关于并发问题的使用synchronized 解决,可以同步代码块,也可以放在函数上同步函数
public class TestThread {
public static void main(String[] args) {
Trick trick = new Trick();
Thread t1 = new Thread(trick);
Thread t2 = new Thread(trick);
Thread t3 = new Thread(trick);
t1.start();
t2.start();
t3.start();
}
}
class Trick implements Runnable{
private int trick = 1000;
@Override
public void run() {
Object obj = new Object();
while (true) {
//同步代码块防止并发操作,哪里操作需要同步的数据就放在哪里,原理是枷锁,这里的锁就是obj,随便放什么都可以
synchronized (obj) {
if(trick>0){
System.out.println(Thread.currentThread().getName()+"=====trick====="+trick--);
}
}
}
}
}
Executors提供四种线程池:
class ThreadD implements Runnable {
@Override
public void run() {
System.out.println("线程:"+Thread.currentThread().getName()+"开始执行");
}
}
(1) newCachedThreadPool
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。
public class ExecutorsTest {
public static void main(String[] args) {
ExecutorService executors = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
ThreadD threadD = new ThreadD();
executors.execute(threadD);
}
}
}
(2) newFixedThreadPool
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
public class ExecutorsTest {
public static void main(String[] args) {
ExecutorService executors = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
ThreadD threadD = new ThreadD();
executors.execute(threadD);
}
}
}
(3) newScheduledThreadPool
创建一个定长线程池,支持定时及周期性任务执行。
public class ExecutorsTest {
public static void main(String[] args) {
ScheduledExecutorService executors = Executors.newScheduledThreadPool(5);
for (int i = 0; i < 10; i++) {
ThreadD threadD = new ThreadD();
executors.schedule(threadD, 3, TimeUnit.SECONDS);
}
}
}
(4) newSingleThreadExecutor
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
public class ExecutorsTest {
public static void main(String[] args) {
ExecutorService executors = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
ThreadD threadD = new ThreadD();
executors.execute(threadD);
}
}
}
常用方法:
Object类的方法:wait(), notify(), notifyAll()
用于协调多线程对共享数据的存取,所以必须在Synchronized语句块内使用。
wait()
使当前线程暂停执行并释放对象锁,让其它线程可以进入Synchronized数据块。
当前线程被放入对象等待池中。
notify()
调用notify方法后,将从“对象等待池”中移走任意的线程并放到“锁标志等待池”中。
如果“对象等待池”中没有线程,则notify不起作用。
notifyAll()
从对象等待池中移走所有等待那个对象的线程并放到锁标志等待池中
Thread类的方法:sleep(), yield(), join()
sleep()
使当前线程暂停执行一段时间,让其它线程有机会继续执行。但是不释放对象锁。
在没有Synchronized保护下,高优先级线程调用sleep后,低优先级线程可以执行。
sleep可以使低优先级的线程得到执行的机会。
yield()
与sleep类似,只是不能由用户指定暂停多长时间,并且yield只能让同优先级的线程有执行的机会。
yield()方法称为“退让”。
yield做了如下操作:
先检测当前是否有相同优先级的线程处于可运行状态。如果有,则把CPU的占有权交给此线程,否则继续运行原来的线程。
yield只是使当前线程重新回到可执行状态,所以执行yield的线程有可能在进入到可执行状态后马上又被执行。
当一个线程对象调用yield()方法时会马上交出执行权,回到可运行状态,等待OS的再次调用。
join()
当前运行的线程可以调用另一个线程的join()方法,当前运行的线程将转入阻塞状态,直到另一个线程运行完毕,它才进入可运行状态。
子线程执行完通知主线程继续执行:
//计数器
CountDownLatch countDownLatch = new CountDownLatch(number);
countDownLatch.countDown();//子线程减去计数
countDownLatch.await();//主线程等待
//join方法