1、多线程定义
进程:
正在运行的程序,是系统进行资源分配和调用的独立单位。
每一个进程都有它自己的内存空间和系统资源。
线程:
是进程中的单个顺序控制流,是一条执行路径
一个进程如果只有一条执行路径,则称为单线程程序。
一个进程如果有多条执行路径,则称为多线程程序。
是程序使用CPU的最基本单位
多线程意义:
多线程的存在,不是提高程序的执行速度。其实是为了提高应用程序的使用率。
程序的执行其实都是在抢CPU的资源,CPU的执行权。
多个进程是在抢这个资源,而其中的某一个进程如果执行路径比较多,就会有更高的几率抢到CPU的执行权。
我们是不敢保证哪一个线程能够在哪个时刻抢到,所以线程的执行有随机性。
并行和并发:
并行是逻辑上同时发生,指在某一个时间内同时运行多个程序。
并发是物理上同时发生,指在某一个时间点同时运行多个程序。
Java程序的运行原理:
由java命令启动JVM,JVM启动就相当于启动了一个进程。
接着有该进程创建了一个主线程去调用main方法。
2、继承Thread类
2.1 方式1:继承Thread类。
步骤 A:自定义类MyThread继承Thread类。
B:MyThread类里面重写run()
为了区分哪些代码能够被线程执行,java提供了Thread类中的run()用来包含那些被线程执行的代码。
C:创建对象
D:启动线程
2.2 线程常用方法
public final String getName():获取线程的名称。
public final void setName(String name):设置线程的名称
public static Thread currentThread():返回当前正在执行的线程对象
例:Thread.currentThread().getName() //获取当前线程对象的线程名称
public final int getPriority():返回线程对象的优先级
public final void setPriority(int newPriority):更改线程的优先级。
注意:
线程默认优先级是5。
线程优先级的范围是:1-10。
线程优先级高仅仅表示线程获取的 CPU时间片的几率高,但是要在次数比较多,或者多次运行的时候才能看到比较好的效果。
线程休眠(暂停)
public static void sleep(long millis):单位是毫秒
线程加入
public final void join():等待该线程终止后面的才执行,必须放在strart()之后
线程礼让
public static void yield():暂停当前正在执行的线程对象,并执行其他线程。
线程守护 (终止退出jvm)
public final void setDaemon(boolean on):将该线程标记为守护线程或用户线程。
当正在运行的线程都是守护线程时,Java 虚拟机退出。
该方法必须在启动线程前调用。 在start()之前
线程中断
public final void stop():让线程停止,过时了,但是还可以使用。
public void interrupt():中断线程。 把线程的状态终止,并抛出一个InterruptedException。
import java.util.Date;
public class MyThread extends Thread {
public MyThread() {
// TODO Auto-generated constructor stub
}
public MyThread(String name) {
// TODO Auto-generated constructor stub
super(name);
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
for(int x=0;x<10;x++){
System.out.println(getName()+":"+x+",日期 "+new Date());
//加入线程休眠
try {
sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Thread.yield();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class ThreadDemo {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName());
// * 方式1:继承Thread类。
// * 步骤 A:自定义类MyThread继承Thread类。
// * B:MyThread类里面重写run()
// * C:创建对象
// * D:启动线程
MyThread mt1=new MyThread();
MyThread mt2=new MyThread();
MyThread mt3=new MyThread();
mt1.setName("mt1:");
mt2.setName("mt2:");
mt3.setName("mt3:");
System.out.println(mt1.getPriority());
System.out.println(mt2.getPriority());
//mt1.setPriority(10)
mt1.start();
mt2.start();
mt3.start();
}
}
3、实现Runnable接口
3.1 方式2:实现Runnable接口
步骤:
A:自定义类MyRunnable实现Runnable接口
B:重写run()方法
C:创建MyRunnable类的对象
D:创建Thread类的对象,并把C步骤的对象作为构造参数传递
实现接口方式的好处
可以避免由于Java单继承带来的局限性。
适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码,数据有效分离,较好的体现了面向对象的设计思想。
3.2 同步机制
方式一:同步代码块
把多条语句操作共享数据的代码给包成一个整体,让某个线程在执行的时候,别人不能来执行。
同步代码块:
synchronized(对象){
需要同步的代码;
}
这里的锁对象可以是任意对象。
如:private Object obj = new Object();
注意:
同步可以解决安全问题的根本原因就在那个对象上。该对象如同锁的功能。
多个线程必须是同一把锁。
方式二:同步方法
把同步加在方法上。
这里的锁对象是this
如:private static synchronized void Mysynchronized() {}
public class Mysynchronized implements Runnable {
private static int tickets=100;
private Object obj=new Object();
private int x=0;
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
if(x%2==0){
synchronized(obj){
if(tickets>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第:"+tickets+"张票");
tickets--;
}
}
}else {
sellTicket();
}
x++;
}
}
private static synchronized void sellTicket(){
if(tickets>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ "正在出售第" + (tickets--) + "张票 ");
}
}
public class synchronizedDemo {
public static void main(String[] args) {
Mysynchronized ms=new Mysynchronized();
Thread t1=new Thread(ms,"窗口1");
Thread t2=new Thread(ms,"窗口2");
Thread t3=new Thread(ms,"窗口3");
t1.start();
t2.start();
t3.start();
}
}
4、等待唤醒机制
Object类中提供了三个方法:
wait():等待
notify():唤醒单个线程
notifyAll():唤醒所有线程
这些方法的调用必须通过锁对象调用,而我们刚才使用的锁对象是任意锁对象。
所以,这些方法必须定义在Object类中,而不定义在Thread类中
sleep()和wait()方法的区别
sleep():必须指时间;不释放锁。
wait():可以不指定时间,也可以指定时间;释放锁。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyLock implements Runnable {
private static int tickets=100;
private Object obj=new Object();
private Lock lock=new ReentrantLock();
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
synchronized(obj){
//加锁
lock.lock();
try{
if(tickets>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第:"+tickets+"张票");
tickets--;
}
}finally{
//释放锁
lock.unlock();
}
}
}
}
}
public class LockDemo {
public static void main(String[] args) {
MyLock ms=new MyLock();
Thread t1=new Thread(ms,"窗口1");
Thread t2=new Thread(ms,"窗口2");
Thread t3=new Thread(ms,"窗口3");
t1.start();
t2.start();
t3.start();
}
}
5、线程组
线程组: 把多个线程组合到一起。
它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。
线程组里面的方法:public final ThreadGroup getThreadGroup()
线程组里面的方法:public final String getName()
//通过组名称设置后台线程,表示该组的线程都是后台线程
tg.setDaemon(true);
public class MyRunnable implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
for(int x=0;x<10;x++){
System.out.println(Thread.currentThread().getName()+":"+x);
}
}
}
public class ThreadGroupDemo {
public static void main(String[] args) {
//创建线程组
ThreadGroup tg1=new ThreadGroup("这是第一个线程组");
MyRunnable mr=new MyRunnable();
Thread t1=new Thread(tg1,mr, "林青霞");
Thread t2=new Thread(tg1,mr, "张曼玉");
System.out.println(t1.getThreadGroup().getName());//这是第一个线程组
t1.start();
t2.start();
//ThreadGroup tg2=new ThreadGroup("这是第二个线程组");
}
}
6、线程池
线程池的好处:
线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。
而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。
public static ExecutorService newCachedThreadPool():创建一个线程池对象
public static ExecutorService newFixedThreadPool(int nThreads):制定数量线程池
public static ExecutorService newSingleThreadExecutor():单一线程池
如何实现线程的代码
A:创建一个线程池对象,控制要创建几个线程对象。
public static ExecutorService newFixedThreadPool(int nThreads)
B:这种线程池的线程可以执行:
可以执行Runnable对象或者Callable对象代表的线程
做一个类实现Runnable接口。
C:调用如下方法即可
Future<?> submit(Runnable task):提交线程到池
<T> Future<T> submit(Callable<T> task)
D:我就要结束,可以吗?
可以。
shuntdown();结束线程池
public class MyRunnable implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
for(int x=0;x<10;x++){
System.out.println(Thread.currentThread().getName()+":"+x);
}
}
}
public class ExecutorsDemo {
public static void main(String[] args) {
ExecutorService pool=Executors.newFixedThreadPool(2);
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
pool.shutdown();
}
}
7、匿名类调用线程
public class NoNameThreadDemo {
public static void main(String[] args) {
//继承Thread
new Thread(){
@Override
public void run() {
for (int x = 0; x < 10; x++) {
System.out.println(Thread.currentThread().getName() + ":"
+ x);
}
}
}.start();
//实现Runnable
new Thread(new Runnable(){
@Override
public void run() {
for (int x = 0; x < 10; x++) {
System.out.println(Thread.currentThread().getName() + ":"
+ x);
}
}
}){
}.start();
}
}
8、TimerTask定时任务
class DeleteFolder extends TimerTask {
@Override
public void run() {
File srcFolder = new File("demo");
deleteFolder(srcFolder);
}
// 递归删除目录
public void deleteFolder(File srcFolder) {
File[] fileArray = srcFolder.listFiles();
if (fileArray != null) {
for (File file : fileArray) {
if (file.isDirectory()) {
deleteFolder(file);
} else {
System.out.println(file.getName() + ":" + file.delete());
}
}
System.out.println(srcFolder.getName() + ":" + srcFolder.delete());
}
}
}
public class TimerDemo {
public static void main(String[] args) throws ParseException {
Timer t = new Timer();
String s = "2014-11-27 15:45:00";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = sdf.parse(s);
t.schedule(new DeleteFolder(), d);
}
}