线程
一、概述
进程:指正在运行的程序,负责这个程序的内存空间分配,代表该程序在内存中的执行区域。
线程:就是在一个进程中负责一个执行路径。
多线程:就是在一个进程中多个执行路径同时执行。
多线程的好处:
1. 一个进程里面可以同时运行多个任务。
2. 提供资源的利用率。
多线程的弊端:
1. 降低了一个进程里面的线程的执行频率。
2. 对线程进行管理要求额外的 CPU开销,会给系统带来上下文切换的额外负担。
3. 当多个线程需要对公有变量进行写操作时,可能发生线程安全问题。
4. 线程的死锁。
线程的状态:
创建:新创建了一个线程对象。
可运行:线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取cpu的执行权。
运行:就绪状态的线程获取了CPU执行权,执行程序代码。
阻塞: 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。
死亡:线程执行完它的任务时。
二、创建线程的方式
1、继承Thread类
步骤:
1. 定义一个类继承Thread类。
2. 覆盖Thread类中的run方法。
3. 直接创建Thread的子类对象创建线程。
4. 调用start方法开启线程并调用线程的任务run方法执行。
练习:
class Demo extends Thread{
private String name ;
Demo(String name){
this.name = name;
}
public void run(){
for(int x = 0; x < 10; x++){
System.out.println(name + "...x=" + x + "...ThreadName=" + Thread.currentThread ().getName());
}
}
}
class ThreadDemo{
public static void main(String[] args){
Demo d1 = new Demo("线程1");
Demo d2 = new Demo("线程2");
//开启线程,调用run方法。
d1.start();
d2.start();
for(int x = 0; x < 20; x++){
System.out.println("x = " + x + "...over..." + Thread.currentThread().getName());
}
}
}
2、实现Runnable接口
避免了Java单继承的局限性,创建线程的第二种方式较为常用。
步骤:
1:定义了实现Runnable接口
2:重写Runnable接口中的run方法,就是将线程运行的代码放入在run方法中
3:通过Thread类建立线程对象
4:将Runnable接口的子类对象作为实际参数,传递给Thread类构造方法
5:调用Thread类的start方法开启线程,并调用Runable接口子类run方法
//需求:简单的卖票程序。多个窗口卖票。
class Ticket implements Runnable
{
private int tick = 100;
public void run()
{
while(true)
{
if(tick>0)
{
//显示线程名及余票数
System.out.println(Thread.currentThread().getName()+":sale -- "+ tick--);
}
}
}
}
class TicketDemo
{
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();
}
}
三、线程安全
2. 操作共享数据的线程代码有多条。
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。一种是同步代码块,还有就是同步函数。都是利用关键字synchronized来实现。
1、同步代码块
class Ticket implements Runnable{
private int num = 100;
Object obj = new Object();
public void run(){
while(true ){
synchronized(obj ){//要同步的代码块
if(num > 0){
System.out.println(Thread.currentThread().getName() + "...sale..." + num--);
}
}
}
}
}
class TicketDemo{
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();
}
}
2、同步函数
class Bank{
private int sum ;
public synchronized void add(int num){ //同步函数
sum = sum + num;
System.out.println("sum = " + sum);
}
}
同步函数和同步代码块的区别:
2. 同步代码块的锁是任意的对象。
建议使用同步代码块。
//加同步的单例设计模式————懒汉式
class Single
{
private static Single s = null;
private Single(){}
public static void getInstance()
{
if(s==null)
{
synchronized(Single.class)
{
if(s==null)
s = new Single();
}
}
return s;
}
}
死锁出现情况:
1:两个任务以相反的顺序申请两个锁,死锁就可能出现
2:线程T1获得锁L1,线程T2获得锁L2,然后T1申请获得锁L2,同时T2申请获得锁L1,此时两个线程将要永久阻塞,死锁出现
四、线程通讯
多个线程操作同一资源,但操作动作不一样需要线程通讯
1、使用同步操作同一资源
//多消费者和生产者
//资源
class Resource{
private String name ;
private int count = 1;
private boolean flag = false;
public synchronized void set(String name){
while(flag )
try{
//让线程处于冻结状态,被wait的线程会被存储到线程池中。
this.wait();
} catch(InterruptedException e){
e.printStackTrace();
}
this.name = name + count;
count++;
System.out.println(Thread.currentThread().getName() + "...生产者..." + this. name);
flag = true ;
//唤醒线程池中所有线程
notifyAll();
}
public synchronized void out(){
while(!flag )
try{
this.wait();
} catch(InterruptedException e){
e.printStackTrace();
}
flag = false ;
notifyAll();
System.out.println(Thread.currentThread().getName() + "...消费者..." + this. name);
}
}
//生产者线程
class Producer implements Runnable{
private Resource r ;
Producer(Resource r){
this.r = r;
}
public void run(){
while(true ){
r.set( "烤鸭");
}
}
}
//消费者线程
class Consumer implements Runnable{
private Resource r ;
Consumer(Resource r){
this.r = r;
}
public void run(){
while(true ){
r.out();
}
}
}
class ProducerConsumerDemo {
public static void main(String[] args){
Resource r = new Resource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
Thread t0 = new Thread(pro);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(con);
Thread t3 = new Thread(con);
t0.start();
t1.start();
t2.start();
t3.start();
}
}
2、JDK1.5新特性
lock():获取锁。
unlock():释放锁,为了防止异常出现,导致锁无法被关闭,所以锁的关闭动作要放在finally中。
Condition接口:出现替代了Object中的wait、notify、notifyAll方法。将这些监视器方法单独进行了封装,变成Condition监视器对象,可以任意锁进行组合。
Condition接口中的await方法对应于Object中的wait方法。
Condition接口中的signal方法对应于Object中的notify方法。
Condition接口中的signalAll方法对应于Object中的notifyAll方法。
import java.util.concurrent.locks.*;
class Resource{
private String name ;
private int count = 1;
private boolean flag = false;
//创建一个锁对象
Lock lock = new ReentrantLock();
//通过已有的锁获取该锁上的监视器对象
Condition con = lock .newCondition();
//通过已有的锁获取两组监视器,一组监视生产者,一组监视消费者
Condition producer_con = lock .newCondition();
Condition consumer_con = lock .newCondition();
public void set(String name){
lock.lock();
try{
while(flag )
try{
producer_con.await();
} catch(InterruptedException e){
e.printStackTrace();
}
this.name = name + count;
count++;
System.out.println(Thread.currentThread().getName() + "...生产者..." + this. name);
flag = true ;
consumer_con.signal();
} finally{
lock.unlock();
}
}
public void out(){
lock.lock();
try{
while(!flag )
try{
consumer_con.await();
} catch(InterruptedException e){
e.printStackTrace();
}
flag = false ;
producer_con.signal();
System.out.println(Thread.currentThread().getName() + "...消费者..." + this. name);
} finally{
lock.unlock();
}
}
}
class Producer implements Runnable{
private Resource r ;
Producer(Resource r){
this.r = r;
}
public void run(){
while(true ){
r.set( "烤鸭");
}
}
}
class Consumer implements Runnable{
private Resource r ;
Consumer(Resource r){
this.r = r;
}
public void run(){
while(true ){
r.out();
}
}
}
class ProducerConsumerDemo {
public static void main(String[] args){
Resource r = new Resource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
Thread t0 = new Thread(pro);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(con);
Thread t3 = new Thread(con);
t0.start();
t1.start();
t2.start();
t3.start();
}
}
五、停止线程
public void run()
{
while(flag)
{
System.out.println(Thread.currentThread().getName()+"....run");
}
}
2、当线程处于冻结状态。就不会读取到标记。那么线程就不会结束。需要对冻结进行清除。强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。Thread类提供该方法interrupt();
六、其他小方法1、join方法
当A线程执行到了b线程的.join()方法时,A线程就会等待,等B线程都执行完,A线程才会执行。(此时B和其他线程交替运行。)join可以用来临时加入线程执行。
2、setPriority()方法用来设置优先级
MAX_PRIORITY 最高优先级10
MIN_PRIORITY 最低优先级1
NORM_PRIORITY 分配给线程的默认优先级
3、yield()方法可以暂停当前线程,让其他线程执行。