------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------
进程和线程
进程:就是应用程序在运行时期,所占用的内存空间区域,这个区域就称为这个应用程序的进程。
线程:是程序执行的一条路径,一个进程中可以包含多条线程。
多线程并发执行可以提高程序的效率,可以同时完成多项工作。
线程创建方式
方式一:继承Thread类
1, 定义类继承Thread类。
2, 重写run方法,把新线程要做的事写在run方法中。
3, 创建线程对象,调用Thread类的start()方法开启新线程, 内部会自动执行run方法。
例:public class ThreadDemo {
public static voidmain(String[] args) {
// 创建线程对象
ThreadSub ts = new ThreadSub();
// start()方法开启新线程
ts.start();
for (int i = 0; i < 50; i++)
System.out.println("main..."+ i);
}
}
// 定义类ThreadSub继承Thread类
class ThreadSub extends Thread {
// 重写run方法
public void run() {
for (int i = 0; i < 50; i++)
System.out.println("ThreadSub..."+ i);
}
}
方式二:实现Runnable接口
1, 定义类实现Runnable接口。
2, 实现run方法,把新线程要做的事写在run方法中。
3, 创建Thread对象,构造方法传入Runnable接口实现类对象。
4, 调用Thread类的start()开启新线程,内部会自动调用Runnable的run()方法。
例:public class RunnableDemo {
public static voidmain(String[] args) {
// 创建Runnable实现类对象
RunnableSub rs = new RunnableSub();
// Thread对象构造方法传入Runnable实现类对象,start()方法开启新线程
new Thread(rs).start();
for (int i = 0; i < 50; i++)
System.out.println("main..."+ i);
}
}
// 定义类RunnableSub实现Runnable接口
class RunnableSub implements Runnable {
// 重写run方法
public void run() {
for (int i = 0; i < 50; i++)
System.out.println("RunnableSub..."+ i);
}
}
用匿名内部类实现两种线程的方式
继承Thread类
new Thread() {//new 类(){}继承这个类
public void run() {//重写run方法
for(int i = 0; i < 3000; i++) {//将要执行的代码,写在run方法中
System.out.println("aaaaaaaaaaaaaaaaaaaaaaaa");
}
}
}.start();
实现Runnable接口
new Thread(new Runnable(){//new 接口(){}实现这个接口
public void run() {//重写run方法
for(int i = 0; i < 3000; i++) {//将要执行的代码,写在run方法中
System.out.println("bb");
}
}
}).start();
两种方式对比:
继承Thread类:单继承,代码简单。
实现Runnable接口:避免了单继承局限性,代码复杂,推荐使用。
线程之间的同步
由于多个线程访问出现延迟和线程随机性,所以多线程不安全,需要同步保证安全。
同步代码块
使用synchronized关键字加上一个锁对象来定义一段代码,这就叫同步代码块。多个同步代码块如果使用相同的锁对象,那么他们就是同步的。
格式:synchronized(对象)
{
需要同步的代码;
}
同步可以解决安全问题的根本原因就在那个对象上。该对象有如同锁的功能。
同步代码块中,只能有一个线程运行,保证了安全,但牺牲了运行速度。
同步方法
使用synchronized关键字修饰一个方法,该方法中所有的代码都是同步的。
死锁问题
多线程同步的时候, 如果同步代码嵌套, 使用相同锁, 就有可能出现死锁,尽量不要嵌套使用。
例:public class DeadlockDemo {
public static void main(String[]args) {
// 创建Runnable接口实现类对象,传递标签值
Dead d = new Dead(true);
Dead d1 = new Dead(false);
// 创建两个线程
new Thread(d).start();
new Thread(d1).start();
}
}
// 定义锁locka
class LockA {
public static final LockAlocka = new LockA();
}
// 定义锁lockb
class LockB {
public static final LockBlockb = new LockB();
}
// 定义类Dead实现Runnable
class Dead implements Runnable {
// 定义一个标签
private boolean flag;
Dead(boolean flag) {
this.flag = flag;
}
public void run() {
while (true) {
if (flag == true){
// 进入A房间,同步代码块,获取锁locka
synchronized (LockA.locka) {
System.out.println("if...locka");
//进入B房间,同步代码块,获取锁lockb
synchronized(LockB.lockb) {
System.out.println("if...lockb");
}
}
} else {
// 进入B房间,同步代码块,获取锁lockb
synchronized (LockB.lockb) {
System.out.println("else...lockb");
//进入A房间,同步代码块,获取锁locka
synchronized(LockA.locka) {
System.out.println("else...locka");
}
}
}
}
}
}
线程之间的通信
1,什么时候需要通信
多个线程并发执行时,在默认情况下CPU是随机切换线程的,如果我们希望他们有规律的执行, 就可以使用通信,例如每个线程执行一次打印。
2,怎么通信
如果希望线程等待,就调用wait( )
如果希望唤醒等待的线程,就调用notify( )
这两个方法必须在同步代码中执行,并且使用同步锁对象来调用。
3,多个线程通信的问题
notify()方法是随机唤醒一个线程。
notifyAll()方法是唤醒所有线程。
JDK5之前无法唤醒指定的一个线程。
如果多个线程之间通信,需要使用notifyAll()通知所有线程,用while来反复判断条件。
例:// 定义产品资源对象
class Product{
// 定义名字
private String name;
// 定义计数器
private int count ;
// 定义标记
private boolean flag = true;
// 生产方法,是让生产线程调用
public synchronized void set(String name){
while(!flag)
try{this.wait();}catch(Exception e){}
this.name = name +count++;
System.out.println(Thread.currentThread().getName()+"生产第 "+this.name);
flag = false;
this.notifyAll();
}
// 消费方法,是让消费线程调用
public synchronized void get(){
while(flag)
try{this.wait();}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"消费第..."+this.name);
flag = true;
this.notifyAll();
}
}
// 生产者线程
class Produce implementsRunnable{
private Product p;
Produce(Product p) {this.p = p;}
public void run(){
while(true)
p.set("Iphone");
}
}
// 消费者线程
class Consumerimplements Runnable{
private Product p ;
Consumer(Product p){this.p = p;}
public void run(){
while(true)
p.get();
}
}
public class ThreadDemo2{
public static void main(String[] args) {
Product p = newProduct();
new Thread(new Produce(p)).start();
new Thread(newProduce(p)).start();
new Thread(newProduce(p)).start();
new Thread(newProduce(p)).start();
new Thread(newConsumer(p)).start();
new Thread(newConsumer(p)).start();
new Thread(newConsumer(p)).start();
new Thread(newConsumer(p)).start();
}
}
Thread类常用方法
1,获取名字,通过getName()方法获取线程对象的名字,返回String
2,设置名字:通过构造函数可以传入String类型的名字。
通过setName(String)方法可以设置线程对象的名字。
3,获取当前线程对象,Thread.currentThread(),主线程也可以获取。
4,休眠,Thread.sleep(毫秒), 控制当前线程休眠若干毫秒。
5,守护,setDaemon(),设置一个线程为守护线程, 该线程不会单独执行,当其他非守护线程都执行结束后,自动退出,必须写在start之前。
6,join(),等待该线程终止,使用了join方法的线程,会一直运行结束,其他线程再抢CPU的资源。
7,toString(),打印线程对象,调用线程的toString方法
例:Thread[Thread-0,5,main]
[]里面的信息:Thread-0是线程的名字,5是线程的优先级,main是所属组,主线程组。
8,setPriority(int newPriority)设置线程的优先级。
优先级,Java中的线程,有三种优先级,最高,默认,最低,Thread类中用三个静态常量表示线程的优先级,优先级越高,线程在某一时刻运行的更多些。
MAX_PRIORITY =10
NORM_PRIORITY =5
MIN_PRIORITY =1
9,static yield(),线程的让步,该方法要写在run()中,出现线程之间相互谦让,不肯运行的效果。
10,方法stop(),停止线程运行,已被interrupt()替代。
11,interrupt(),停止线程运行,抛InterruptedException线程中断异常。
wait,notify,线程无限等待和唤醒等待线程,写在同步中,必须有锁的支持,锁为Object类保证任意对象锁都可以调用线程,等待唤醒方法。
wait和 sleep的区别
wait是Object类的非静态方法,必须有锁。
sleep是Thread类的静态方法,任意程序都可调用,无需锁。
sleep不会释放锁,wait线程释放同步锁。
JDK1.5之后的线程控制
同步
接口Lock,替代了原有的synchronized的使用,使用灵活广泛。
Lock实现类ReentrantLock的lock()和unlock()方法进行同步。
通信
使用Lock接口实现类ReentrantLock的newCondition()方法可以获取Condition对象。
需要等待的时候使用Condition的await()方法,唤醒的时候用signal()方法。
不同的线程使用不同的Condition,这样就能区分唤醒的时候找哪个线程了。
新旧方法的对比
接口中Condition Object类
await() wait()
signal() notify()
signalAll() notifyAll()
Timer类
构造方法中传递参数false
Timer类的方法schedule(TimerTask task, Date firstTime, long period)定时器要运行的程序,开始时间,间隔时间。
第一个参数需要运行的程序,第二个参数是开始时间,第三个参数间隔的毫秒值。
第一个参数是TimerTask类型,是一个抽象类,子类继承抽象类,重写run方法,传递子类的对象。