------- <a href="http://www.itheima.com" target="blank">android培训</a>、<a href="http://www.itheima.com" target="blank">java培训</a>、期待与您交流! ----------
学习内容:线程的创建;线程的同步;线程间的通信;操纵线程的常见函数
一、线程的创建
1,线程的创建方式一
1)定义一个类并且继承Thread
2)覆写其中的run()方法,run()方法中存放的是线程的运行代码
3)调用线程的start()方法,该方法启动线程,并且调用run()方法
2,线程的创建方式二
1)定义一个类实现Runnable接口
2)覆写Runnable接口的run()方法,将线程要运行的代码存放在该run()方法中
3)创建实现了Runnable接口的子类的实例对象,并将它作为实际参数传递给Thread类的构造函数
4)调用Thread类的start()方法,开启线程并且调用Runnable接口子类中的run方法
3,两种创建方式的总结
1)实现方式建立线程,避免了java语言单继承的局限。所以在定义线程时,建议使用实现方式。
2)继承Thread方式中,线程的运行代码存放在Thread子类的run()方法中。实现Runnable方式中,线程代码存放在Runnable接口的子类的run()方法。
二,线程同步
1,同步问题
当多个线程操作共享数据时,如果没有加以同步限制,可能出现这样的情况,一个线程在操作共享数据一部分时,另外的线程也进来操作共享数据,这样就造成了数据的不一致,出现了错误。
2,解决办法
对于操作共享数据的语句,只能让一个线程执行完毕后,另外线程不可以参与执行。也就是对于共享数据,每次只能有一个线程操纵。java提供了这种同步方式 同步代码块,同步函数。
3,同步的前提
必须存在两个或者两个以上的线程
必须是多个线程使用同一把锁
4,同步的利弊
利:解决了多线程的安全问题
弊:多线程需要判断锁,较为消耗资源。(这个较为消耗资源是在允许的范围内)
5,关于同步的锁的问题
同步代码块需要手动指定一个对象当做锁。非静态同步函数的锁是this;静态同步函数的锁是该方法所在类的字节码文件对象。
6,死锁
同步中嵌套同步而锁不同。用比较专业的术语说就是:相互抢夺资源但是又不释放自己的资源,造成相互等待的情况
7,线程同步的举例
需求:通过实现方式定义四个线程,操作共享数据tick,每操作一次tick值减一并且打印一次。
class Ticket implements Runnable
{
private int tick = 1000;
Object obj = new Object();
public void run()
{
while(true)
{
synchronized(obj)
{
if(tick>0)
{
System.out.println(Thread.currentThread().getName()+"。。。。。"+ tick--);
}
}
}
}
}
class TicketDemo2
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
三、线程间的通信
1,线程间通信
其实就是多个线程在操作一个资源,但是操作的动作不同。
2,等待唤醒机制
wait(),notify()以及notifyAll()都使用在同步中,因为要对持有锁(监视器)的线程操作,所以要使用在同步中,因为只有同步才具有锁。
3,为什么wait(),notify()以及notifyAll()都要定义在Object类中呢?
因为这些方法是操作同步线程时,都必须要标识他们所操作线程持有的锁。只有同一个锁上的被等待线程可以被同一个锁上的notify唤醒,不可以对不同锁中的线程唤醒,也就是说,等待和唤醒必须使用同一把锁。而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中。
4,JDK1.5中提供的操作线程的新特性。
将同步Synchronized替换成了现实lock操作
将object类中的wait(),notify(),notifyAll()替换了Condition对象,该对象可以有lock锁获得。
对于最后的解锁语句unlock一般都在finally语句中执行
通过这种方式,可以显式的加锁,显式的解锁;还有一个好处,就是根据Condition对象的不同,可以随意的指定await那个线程,随意的指定signal那个线程。
5,一个关于生产者消费者的示例
需求:定义一个资源,然后定义两个生产者,两个消费者来操作共享资源,只有生产出来,才能消费,并且只能消费一次。生产者消费者是一个经典的线程间通信示例,代码如下:
package Test;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Resource//定义资源
{
private String name;
private int count = 1;
private boolean flag = false;
private Lock lock = new ReentrantLock();//定义锁
private Condition condition_set = lock.newCondition();//定义生产者监视器对象
private Condition condition_get = lock.newCondition();//定义消费者监视器对象
public void set(String name)
{
lock.lock();
try
{
while(flag)
condition_set.await();
this.name = name + count++;
System.out.println("生产者"+"....."+this.name);
this.flag = true;
condition_get.signal();
}
catch(InterruptedException e)
{
e.printStackTrace();
}
finally
{
lock.unlock();//释放锁,一般释放锁都在这里
}
}
public void get()
{
lock.lock();
try
{
while(!flag)
condition_get.await();
System.out.println("消费者"+"....."+this.name);
this.flag = false;
condition_set.signal();
}
catch (InterruptedException e) {
e.printStackTrace();
}
finally
{
lock.unlock();
}
}
}
class Producer implements Runnable//定义生产者,实现Runnable接口
{
private Resource res;
Producer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
res.set("商品");
}
}
}
class Consumer implements Runnable//定义消费者,实现Runnable接口
{
private Resource res;
Consumer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
res.get();
}
}
}
public class ProducerConsumerDemo {
public static void main(String[] args)
{
Resource res = new Resource();
Producer pro = new Producer(res);
Consumer con = new Consumer(res);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(con);
Thread t3 = new Thread(pro);
Thread t4 = new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
四、操纵线程的几个函数
1,interrupt() 停止线程
停止线程的方法是stop(),但是这个方法已经过时,不能使用。那只剩下一种,run()方法结束。开启多线程运行代码通常是循环结构,只要控制住循环,就可以让run方法结束,也就是线程结束
那interrupt()这个方法是干什么呢?乍一看,interrupt不是中断嘛,就是把正在运行的线程中断停止掉。错!错!!。interrupt的中断是这个意思,当线程处于wait啊,sleep啊,join啊等状态是,她中断他们的冻结状态,使其回到运行状态,然后操纵run方法中的循环,控制其条件结束线程
2,setDemon() 守护线程 也就是创建守护线程
守护线程的特点是:当前台线程全部结束时,后台线程也随之结束;嘿嘿,因为前台线程结束后,JVM也跟着exit了。
定义后台线程的要注意:setDemon()方法需要在start()方法之前声明,也就是线程启动前调用。
3,join()方法
join()方法是,当一个线程执行了另外的一个线程的join方法时,这个线程就wait了,另外的那个线程就加入到线程运行的队列中了 ,这个被wait的线程只有另外的那个线程运行完毕有才被唤醒向下执行。
4,setPriority()方法
setPriority()方法是用于设置线程的优先级的 。优先级共10个级别(1--10)。主线程和其他线程默认的优先级都是5;
5,yield()方法
yield()方法和join()的方法有点相似,但是实现的作用是相反的。线程执行了yield()方法后,会暂停正在执行的这个线程对象,执行其他线程。
6,toString()
toString() 这个方法是定义在Object类中,功能是打印对象的字符串。而现在他定义在了线程类中,她的功能被复写为打印线程的名称,线程的优先级,线程的线程组。