线程笔记

from:Thread-Note


2.1、线程的各种状态
2.1.1、创建状态
在程序中用构造方法创建了一个线程对象后,新的线程对象便处于新建状态,它已经有了相应的内在空间和其他资源,但还处于不可运行状态,新建一个线程对象可采用Thread类的构造方法来实现
2.1.2、就绪状态
新建线程对象后,调用该线程的start()方法就可以启动线程,当线程启动时,线程进入就绪状态,线程将进入线程队列排队,等待cpu服务,这表明它已经具备了运行条件
2.1.3、运行状态
当就绪状态的线程被调用并获得处理器资源时,线程就进入了运行状态,自动调用该线程对象的run()方法,run()方法定义了该线程的操作和功能
2.1.4、堵塞状态
一个正在执行的线程在某些特殊情况下,如被人为挂起或需要执行耗时的输入/输出操作时,将让出cpu并暂时中止自己的执行,进入堵塞状态,在可执行状态下,如果调用sleep()、suspend()、wait()等方法,线程都将进入堵塞状态。堵塞时,线程不能进入排队队列,只有当引起堵塞的原因被消除后,线程才可以转入就绪状态
2.1.5、死亡状态
线程调用stop()方法时或run()方法执行结束后,都处于死亡状态,处于死亡状态的线程不具有继续运行的能力
2.2、线程的创建
方式一(继承Thread类):子类覆盖父类中的run()方法,将线程运行的代码存放在run()中
建立子类对象的同时线程也被创建
通过调用start方法开启线程
class MyThread extends Thread{
private String name;
public MyThread(){}
public MyThread(String name){
this.name = name;
}
public void run(){          //重写run()方法
for (int i=0; i<10; i++){
System.out.println(name+":"+i);
}
}
}
public static main void(String[] args){
MyThread A = new MyThread("A");   //创建一个线程,进入创建状态
MyThread B = new MyThread("B");
A.start();   //调用start()方法,进入就绪状态
B.start();
}
注意:同个线程只能调用一次start()方法,多次调用会报异常;sleep方法需要指定睡眠时间,单位是毫秒;就绪是一个特殊的状态:具备了执行资格,但是还没有获取资源
方式二(实现Runnable接口):子类覆盖接口中的run方法
通过Thread类创建线程,并将实现了Runnable接口的子类对象作为参数传递给Thread类的构造函数
Thread类对象调用start方法开启线程
class MyRunnable implements Runnable{
public void run(){
for (int i=0; i<10; i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
public static void main(String[] args){
MyRunnable m = new MyRunnable();
Thread A = new Thread(m);
Thread B = new Thread(m);
A.start();
B.start();
}
注意:以后在实际开发中,我们一般都用第二种方式,它比之第一种方式区别在于:Runnable共享数据,它只有一个对象
2.3、线程安全问题:
原因:多个线程访问出现延迟;线程随机性;一般出现在多个线程有数据共享,并且run中的语句不止只有一句
解决:同步(synchronized)
class MyRunnable implements Runnable{
static int ticket = 10;
Object obj = new Object();
public void run(){
for (int i=0; i<10; i++){
synchronized (obj){     //同步需要锁,也就是对象,同步的关键在于锁是否相同,一般此处用object对象或this,this更好用,它与同步函数正好对应,因为同步函数不用锁,默认是this
if (ticket > 0){
System.out.println(Thread.currentThread().getName()+":"+ticket--);   //卖票,如果不同步,那么很容易在一个线程判断完后或在ticket自减前被另一个线程抢,从而造成出现卖0,或者卖两张10
}
}
//test();   与上面效果一样
}
}
public synchronized void test(){   //同步方法
if (ticket > 0){
System.out.println(Thread.currentThread().getName()+":"+ticket--);
}
}
}
public static void main(String[] args){
MyRunnable m = new MyRunnable();   //创建一个对象
Thread A = new Thread(m);
Thread B = new Thread(m);
A.start();
B.start();
}
注意:静态同步函数的锁不再是"this",而是class的字节码文件"this.getClass()"
2.4、生产者和消费者:前面是不同线程做相同的事情,现在用不同的线程做不相同的事,一个线程生产然后等待,另一个线程消费然后唤醒生产并等待
package com.java.Thread;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class PSThread {
// 主函数
public static void main(String[] args) {
Production p = new Production(); // 创建生产类对象
ProThread p1 = new ProThread(p); // 创建四个线程
ConThread p2 = new ConThread(p);
ProThread p3 = new ProThread(p);
ConThread p4 = new ConThread(p);
Thread t1 = new Thread(p1);
Thread t2 = new Thread(p2);
Thread t3 = new Thread(p3);
Thread t4 = new Thread(p4);
t1.start();
t2.start();
t3.start();
t4.start();
}
}

class Production {
private String name; // 产品名称
private int num; // 产品编号
private boolean b = false;
ReentrantLock lock = new ReentrantLock();  //一个锁可以有多个监视器,这里创建了两个监视器
Condition cd_p1 = lock.newCondition();  //生产的锁
Condition cd_s1 = lock.newCondition();  //销售的锁
public Production() {
} // 无参构造函数

public Production(String name, int num) { // 有参构造函数
this.name = name;
this.num = num;
}

public void setName(String name) { // set和get方法
this.name = name;
}

public String getName() {
return this.name;
}

public boolean isB() {
return b;
}

public void setB(boolean b) {
this.b = b;
}

public void setNum(int num) {
this.num = num;
}

public int getNum() {
return this.num;
}
public void Pro(){
lock.lock();   //监视器开始
try{
if (this.isB()){
try {
cd_p1.await();   //如果为真说明有产品,就用生产锁来让生产等待
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else{
this.setName("炸弹"); // 设置产品名称
this.setNum(++num); // 设置产品编号,并让其从1开始
System.out.println("生产:" + this.getName() + this.getNum()); // 生产出产品
this.setB(true);
//this.notifyAll();
cd_s1.signal();    //为假就生产产品,然后用销售锁来唤醒销售
}
}
finally{
lock.unlock();  //监视器结束
}
}
public void Con(){
lock.lock();
try{
if (this.isB()){
System.out.println("卖出:" + this.getName() + this.getNum()); // 卖出产品
this.setB(false);
cd_p1.signal();    //如果为真就卖出产品,并用生产锁来唤醒生产
}
else{
try {
cd_s1.await();  //如果为假就用销售锁来让销售等待
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
finally{
lock.unlock();
}
}
}

// 生产线程
class ProThread implements Runnable {
Production p; // 声明一个生产类对象
//private int num; // 声明一个编号,因为下面需要做运算

public ProThread() {
}

public ProThread(Production p) { // 传一个生产类对象进来
this.p = p;
}

public void run() {
while (true) {
p.Pro();
}
}
}

// 消费线程
class ConThread implements Runnable {
Production p;

public ConThread() {
}

public ConThread(Production p) {
this.p = p;
}

public void run() {
while (true) {
p.Con();
}
}
}
注意:用监视器的好处是提高效率,因为用同步会造成很多空转