标签:
** 概念
进程:简单说就是一个正在运行的程序。进程负责分配程序的内存空间等资源。
线程:一个进程的某个执行流程。一个进度可以有多个线程。进程中的多个线程共享进程的内存。
多线程就是一个进程中有多个线程同时进行。
对于电脑,多个程序同时运行,其实是CPU的分时机制在起作用,不过现在都是多核的电脑了。
多线程意味这可以在一个进程里同时执行多个任务。而且可以提高资源的利用率。
# 如何使线程
方式一、继承自Thread类
>> 继承自Thread类
>> 重写run()方法
>> 创建继承自Thread类的实例
>> 调用start()方法开启线程
publicclass Tm05 extends Thread{ /** * 一个程序就是一个进程,进程负责分配内存 * 一个进程有多个线程[多线程]可以执行不同的任务,线程负责CPU资源的抢夺 * 多线程提高了资源的利用率 * 对于单核的电脑对于微观来说是单进程的 * 任何一个java程序,jvm在运行的时候创建一个主线程,来执行main中的代码 * 任何一个java程序,至少有两个线程[1.main | 2.垃圾回收器[GC]] */ @Override public void run() {
// 自定义线程的任务代码定义在run()方法中 for(int i=0;i<100;i++){ System.out.println("run()+"+(i+1)); } }
public static void main(String[] args){ System.out.println("多线程使用"); System.out.println("#继承Thread类 | #重写run()方法 | #创建Thread实例,并调用start()方法"); System.out.println("注意不要调用run()方法"); Tm05 t=new Tm05(); t.start();; for(int i=0;i<100;i++){ System.out.println("main()+"+(i+1)); } } }
|
方式二、实现Runnable接口[tips:推荐使用第二种,因为java是单继承多实现的]
>>实现Runnable接口
>>重写run()方法
>>创建Runnable接口子类的实例
>>创建Thread类实例,并将Runnable接口子类的实例作为实参传入到构造方法
>>由Thread类的实例调用start()方法开启线程[start()方法在Thread类中]
package hs; /** * 推荐使用第二种[这种] * 因为java是单继承多实现的,如果用了继承Thread类,其他类就不能继承了 */ publicclass Tm10 implements Runnable{ static Object lockState=new Object(); // 方式二中Tm10只是共享了一份代码而已,所以不用加static int num=100;
@Override public void run() { while(true){ synchronized (lockState) { if(num>0){ num--; System.out.println(Thread.currentThread().getName()+"卖出1张,还有"+num+"张"); }else{ System.out.println("已经买完了......"); break; } } } }
public static void main(String[] args) { Tm10 t=new Tm10(); Thread t1=new Thread(t, "A"); Thread t2=new Thread(t, "B"); Thread t3=new Thread(t, "C"); t1.start(); t2.start(); t3.start(); } }
|
** 线程的状态和常见的方法
就绪状态:状态的线程位于可运行线程池中,等待获取cpu的执行权
临时阻塞状态:线程因为某种原因放弃CPU使用权,暂时停止运行
运行状态:就绪状态的线程获取了CPU执行权,执行程序代码
常见的线程方法
publicclass Tm12 implements Runnable{ @Override public void run() { int i=0; while(true){ i++; System.out.println(Thread.currentThread().getName()+" i="+i); if(i>=10){ break; } } } /** * 常用的线程方法 * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { Tm12 t=new Tm12(); Thread th=new Thread(t, "my thread"); // # setName() 设置线程的名字 th.setName("set thread name"); // # setPriority() 设置当前线程的优先级 th.setPriority(10); // # start() 开启线程 th.start(); // # sleep() 让线程睡眠XXX毫秒对于sleep是对当前运行的线程有效果 Thread.sleep(1000); // # setDaemon() 设置是否为守护线程 th.setDaemon(true); // # 还有notify()/notifyAll()、wait()等方法后面讲 // # currentThread() 获取当前线程对象 Thread thMain=Thread.currentThread(); for(int i=1;i<=10;i++){ System.out.println(Thread.currentThread().getName()+" i="+i); } } }
|
** 同步代码块和同步函数
同步代码块[同步代码块比较容易控制锁对象,所以推荐使用同步代码块]
@Override public void run() { while (true) { synchronized (lockState) { if (num > 0) { num--; System.out.println(Thread.currentThread().getName()+"成功卖出1张票,还有:" + this.num + "张"); } else { System.out.println("已经买完了票......"); break; } } } } |
同步函数
// 静态同步函数 --> 锁对象是唯一的 // **推荐使用同步代码块** public static synchronizedvoid getMoney(){ while(true){ if(money-1000>0){ money=money-1000; System.out.println(Thread.currentThread().getName()+"取了1000,还有"+money); }else{ System.out.println("对不起余额不足......"); break; } } } |
** 锁对象
Java中的每个对象都有一个内置锁,只有当对象具有同步方法代码时,内置锁才会起作用,当进入一个同步的非静态方法时,就会自动获得与类的当前实例(this)相关的锁,该类的代码就是正在执行的代码。获得一个对象的锁也成为获取锁、锁定对象也可以称之为监视器来指我们正在获取的锁对象。
因为一个对象只有一个锁,所有如果一个线程获得了这个锁,其他线程就不能获得了,直到这个线程释放(或者返回)锁。也就是说在锁释放之前,任何其他线程都不能进入同步代码(不可以进入该对象的任何同步方法)。释放锁指的是持有该锁的线程退出同步方法,此时,其他线程可以进入该对象上的同步方法。
1:只能同步方法(代码块),不能同步变量或者类
2:每个对象只有一个锁
3:不必同步类中的所有方法,类可以同时具有同步方法和非同步方法
4:如果两个线程要执行一个类中的一个同步方法,并且他们使用的是了类的同一个实例(对象)来调用方法,那么一次只有一个线程能够执行该方法,另一个线程需要等待,直到第一个线程完成方法调用,总结就是:一个线程获得了对象的锁,其他线程不可以进入该对象的同步方法。
5:如果类同时具有同步方法和非同步方法,那么多个线程仍然可以访问该类的非同步方法。
同步会影响性能(甚至死锁),优先考虑同步代码块。
6:如果线程进入sleep()睡眠状态,该线程会继续持有锁,不会释放。
** 生产者和消费者
[百度百科] 生产者消费者问题(英语:Producer-consumerproblem),也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同步问题的经典案例。该问题描述了两个共享固定大小缓冲区的线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。
wait:告诉当前线程放弃执行权,并放弃监视器(锁)并进入阻塞状态,直到其他线程持有获得执行权,并持有了相同的监视器(锁)并调用notify为止。
notify:唤醒持有同一个监视器(锁)中调用wait的第一个线程,例如,餐馆有空位置后,等候就餐最久的顾客最先入座。注意:被唤醒的线程是进入了可运行状态。等待cpu执行权。
notifyAll:唤醒持有同一监视器中调用wait的所有的线程。
如何解决生产者和消费者的问题?
可以通过设置一个标记,表示数据的(存储空间的状态)例如,当消费者读取了(消费了一次)一次数据之后可以将标记改为false,当生产者生产了一个数据,将标记改为true。
,也就是只有标记为true的时候,消费者才能取走数据,标记为false时候生产者才生产数据。
package hs;
import java.util.ArrayList; import java.util.List;
/** * 产品类 */ class Product{ String name; double price;
public Product(String name, doubleprice) { this.name=name; this.price=price; } }
/** * 生产者 */ class Producer implements Runnable{ static int i=0; List<Product> p;
public Producer(List<Product> p) { this.p=p; }
@Override public void run() { while(true){ synchronized (p) { if(p.size()<10){ Product pro; if(i%2==0){ pro=new Product("西瓜",2.99); p.add(pro); i++; }else{ pro=new Product("桃子", 4.88); p.add(pro); i++; } System.out.println(Thread.currentThread().getName()+":生产了一个" +pro.name+",价格是"+pro.price+"¥"); // 通知唤醒的线程 p.notify(); }else{ System.out.println(Thread.currentThread().getName()+":存储区域已经满了......"); try { p.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } }
/** * 消费者 */ class Consumer implements Runnable{ List<Product> p;
public Consumer(List<Product> p) { this.p=p; }
@Override public void run() { while(true){ synchronized (p) { if(p.size()>0){ System.out.println(Thread.currentThread().getName()+ ":取出了一个"+p.get(0).name+",价格"+p.get(0).price+"¥"); p.remove(0); p.notify(); }else{ System.out.println(Thread.currentThread().getName()+":物品已经被取完......"); try { p.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } }
publicclass Tm11 {
public static void main(String[] args) { List<Product> p=new ArrayList<Product>(); Producer producer=new Producer(p); Consumer consumer=new Consumer(p);
Thread th1=new Thread(producer, "生产者"); Thread th2=new Thread(consumer,"消费者");
th1.start(); th2.start();
} }
|
** 其他
后台线程:就是隐藏起来一直在默默运行的线程,直到进程结束。
setDaemon(booleanon)
当所有的非后台线程结束时,程序也就终止了同时还会杀死进程中的所有后台线程,也就是说,只要有非后台线程还在运行,程序就不会终止,执行main方法的主线程就是一个非后台线程。
必须在启动线程之前(调用start方法之前)调用setDaemon(true)方法,才可以把该线程设置为后台线程。
join方法
当A线程执行到了B线程Join方法时A就会等待,等B线程都执行完A才会执行,Join可以用来临时加入线程执行
标签:
原文地址:http://blog.csdn.net/u013468915/article/details/51941275