JavaSE基础复习六:多线程

时间:2022-08-26 18:35:54
------- android培训java培训、期待与您交流! ----------


创建线程两种方法:继承Thread类或者实现Runnable接口

一:继承Thread类。 

步骤: 
1、定义一个类继承Thread类。 
2、覆盖Thread类中的run方法。 
3、直接创建Thread的子类对象创建线程。 
4、调用start方法开启线程并调用线程的任务run方法执行。 
通过getName()方法获取线程名,默认名称:Thread-编号(从0开始) 

二:实现Runnable接口。 

1、定义类实现Runnable接口。 
2、覆盖接口的run()方法,将线程的任务代码封装到run()方法中。 
3、创建Thread对象,将Runnable接口的子类对象作为Thread类构造函数的参数传递。  
4、调用Thread对象的start()方法开启线程。

Thread :如果没有父类可以继承Thread
Runnable :避免了单继承的局限性,建议采用实现接口方式创建线程

线程安全问题产生原因:

1、多个线程操作共享数据时。
2、操作共享数据的代码有多条代码。
在一个线程操作的过程中,其他线程可以参与进来。


线程状态转换

JavaSE基础复习六:多线程

synchronized  同步锁:

同步代码块:锁是一个对象;  
同步函数:锁其实是this;
静态同步函数:锁是 类名.class  它是一个Class类的字节码文件对象

/*
卖票程序
同步代码块形式
*/

class Ticket implements Runnable { //接口没有抛出异常,所以run()方法内只能捕捉异常

private int num = 100;
Object obj = new Object();

public void run() {

while(true) {
try{
Thread.sleep(100);//---测试多线程调用时,数据num出现负数
}catch(InterruptedException e) {
System.out.println("线程出错了");
}
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();//---只有一个Ticket对象
Thread t1 = new Thread(t);
//Thread t2 = new Thread(new Ticket());
//Thread t3 = new Thread(new Ticket());
//Thread t4 = new Thread(new Ticket()); //---4个Ticket对象需要使用static修饰num
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();

}



举例:火车上的卫生间

同步的好处:解决了线程的安全问题。 
同步的弊端:相对降低了效率,因为同步外的线程的都会判断同步锁。 
同步的前提:有多个线程并使用同一个锁。


单例设计模式:

使类只有一个对象(构造函数私有化,在类中创建本类对象并私有化为静态,定义公共静态方法,通过调用返回对象)
饿汉式:对象在类进入内存中时加载。不存在线程安全的问题
懒汉式:对象延迟加载。使用同步代码块,锁是类名.class,并在锁外在进行一次判断(双层判断)。可以防止加锁浪费资源。
/*
多线程下的单例设计模式

*/
//---饿汉式
class SingleHg {
private static final SingleHg s = new SingleHg();
private SingleHg(){}

public static SingleHg getInstance(){
return s;
}
}

//---懒汉式
class SingleLz {
private static SingleLz s = null;
private SingleLz(){}

public static SingleLz getInstance(){
if(s==null)
s = new SingleLz();
return s;
}
}

//---加同步锁的懒汉式,面试常考,一般实际用饿汉式

class Single {
private static Single s = null;
private Single(){}

public static /*synchronized*/ Single getInstance(){ //---使用同步函数会降低效率,每次都要获取锁
if(s==null)
synchronized(Single.class){
if(s==null)
s = new Single();
}
return s;
}
}



死锁程序:

Interrupt();强制清除线程的冻结状态,使线程获取执行资格,但是会使线程抛出InterruptedException


线程间通信:其实是多个线程在操作同一个资源,只是操作的动作不同;

考虑同步问题,加入等待唤醒机制;wait()  notify()
wait();notify();notifyAll(); 这些方法都应该在同步代码中使用,对持有锁的线程进行操作。
这些方法都定义在Object类中,所有对象都可以是锁,这些方法只能唤醒或者等待持有相同锁的线程。


生产者消费者问题:

多个生产者消费者,需要用while()循环判断标记;

class Duck {
private int id;
Duck(int id) {
this.id = id;
}

public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "Duck [id=" + id + "]";
}
}

class DuckBasket {
int index = 0;
Duck[] ducks = new Duck[5];

public synchronized void push(Duck duck) {
while(index==ducks.length)
try {
System.out.println(Thread.currentThread().getName()+":篮子满了,歇会~~~~~~~~~!");
this.wait();
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
ducks[index] = duck;
System.out.println(Thread.currentThread().getName()+"生产了"+duck);
index++;
notify();
}

public synchronized Duck pop() {
while(index==0)
try {
System.out.println(Thread.currentThread().getName()+":空了,快生产烤鸭!");
wait();
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
index--;
//---打印输出用到了共享数据,要放到同步代码中
System.out.println(Thread.currentThread().getName()+"消费了"+ducks[index]);
notify();
return ducks[index];
}
}

class Producer implements Runnable {
DuckBasket db;
public Producer(DuckBasket db) {
super();
this.db = db;
}

@Override
public void run() {
for(int i=0; i<20; i++) {
Duck duck = new Duck(i);
db.push(duck);
}
}
}

class Consumer implements Runnable {
DuckBasket db;
public Consumer(DuckBasket db) {
super();
this.db = db;
}

@Override
public void run() {
for(int i=0; i<20; i++) {
db.pop();
}
}
}

public class ProducerConsumerDemo {

/**
* @param args
*/
public static void main(String[] args) {
DuckBasket db = new DuckBasket();
Producer pro1 = new Producer(db);
Consumer con1 = new Consumer(db);
Thread t1 = new Thread(pro1,"生产者----1");
Thread t2 = new Thread(con1,"消费者1");
Thread t3 = new Thread(pro1,"生产者----2");
Thread t4 = new Thread(con1,"消费者2");
t1.start();
t2.start();
t3.start();
t4.start();

}

}


新特性,多线程的升级解决方案,将同步的synchronized 升级为显示的lock(接口),
将wait(),notify(),notifyAll()升级为condition对象,该对象可以通过lock锁获取。
可以实现只唤醒对方的操作。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


class Duck {
private int id;
Duck(int id) {
this.id = id;
}

public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "Duck [id=" + id + "]";
}
}

class DuckBasket {
int index = 0;
Duck[] ducks = new Duck[5];
Lock lock = new ReentrantLock();
Condition proL = lock.newCondition();
Condition conL = lock.newCondition();

public void push(Duck duck) {
lock.lock();
try{
while(index==ducks.length)
try {
System.out.println(Thread.currentThread().getName()+":篮子满了,歇会~~~~~~~~~!");
proL.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
ducks[index] = duck;
System.out.println(Thread.currentThread().getName()+"做了"+duck);
index++;
conL.signal();
}finally{
lock.unlock();
}
}

public Duck pop() {
lock.lock();
try {
while(index==0)
try {
System.out.println(Thread.currentThread().getName()+":空了,快做烤鸭!");
conL.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
index--;
//---打印输出用到了共享数据,要放到同步代码中
System.out.println(Thread.currentThread().getName()+"消费了"+ducks[index]);
proL.signal();
return ducks[index];
} finally{
lock.unlock();
}
}
}

class Producer implements Runnable {
DuckBasket db;
public Producer(DuckBasket db) {
super();
this.db = db;
}

@Override
public void run() {
for(int i=0; i<20; i++) {
Duck duck = new Duck(i);
db.push(duck);
}
}
}

class Consumer implements Runnable {
DuckBasket db;
public Consumer(DuckBasket db) {
super();
this.db = db;
}

@Override
public void run() {
for(int i=0; i<20; i++) {
db.pop();
}
}
}

public class ProConLock {

/**
* @param args
*/
public static void main(String[] args) {
DuckBasket db = new DuckBasket();
Producer pro1 = new Producer(db);
Consumer con1 = new Consumer(db);
Thread t1 = new Thread(pro1,"生产者----1");
Thread t2 = new Thread(con1,"消费者1");
Thread t3 = new Thread(pro1,"生产者----2");
Thread t4 = new Thread(con1,"消费者2");
t1.start();
t2.start();
t3.start();
t4.start();

}



如何停止线程?

1、控制循环,做一个标记,使标记改变时满足条件就停止循环,结束run()方法;
2、如果线程处于等待(冻结)状态,无法结束线程,可以使用interrupt()方法强制解除冻结状态,进入运行状态,这样就可以操作标记,结束线程。


wait()和sleep()相同点,都可以使线程进入冻结状态,都抛出异常。

区别:
1、wait()为Object的方法,sleep()为Thread的方法
2、wait()必须有锁
3、wait()需要notify()唤醒,sleep()时间到了自动获取CPU执行资格

其他方法

join()方法,当a线程执行到了b线程的join方法时,a等待b执行完在继续执行。


setDaemon();守护进程,前台结束就结束


Thread类的 toString()方法,返回  [程名称,优先级,线程组]


setPriority()设置优先级  MAX_PRIORITY  MIN_PRIORITY NORMAL_PRIORITY   setName()  


yield();静态方法:暂停当前线程执行


匿名内部类创建进程---常用。


------- android培训java培训、期待与您交流! ----------